From d13f34469065230dd86e63733e4dc8d1f775acfb Mon Sep 17 00:00:00 2001 From: Oliver Sander <oliver.sander@tu-dresden.de> Date: Fri, 5 Jan 2024 16:20:07 +0100 Subject: [PATCH] Run uncrustify on all files, to make the new CI job pass --- dune/gfe/assemblers/chiralskyrmionenergy.hh | 187 +- .../gfe/assemblers/cosseratenergystiffness.hh | 791 ++++--- dune/gfe/assemblers/cosseratrodenergy.hh | 337 ++- dune/gfe/assemblers/geodesicfeassembler.hh | 313 ++- .../assemblers/geodesicfeassemblerwrapper.hh | 230 +-- dune/gfe/assemblers/harmonicenergy.hh | 73 +- .../gfe/assemblers/l2distancesquaredenergy.hh | 1 - dune/gfe/assemblers/localenergy.hh | 55 +- dune/gfe/assemblers/localfirstordermodel.hh | 25 +- .../localgeodesicfeadolcstiffness.hh | 521 +++-- .../assemblers/localgeodesicfefdstiffness.hh | 333 ++- .../assemblers/localgeodesicfestiffness.hh | 55 +- dune/gfe/assemblers/localintegralenergy.hh | 128 +- dune/gfe/assemblers/mixedgfeassembler.hh | 507 +++-- .../mixedlocalgeodesicfestiffness.hh | 93 +- .../assemblers/mixedlocalgfeadolcstiffness.hh | 803 ++++---- .../nonplanarcosseratshellenergy.hh | 75 +- dune/gfe/assemblers/simofoxenergy.hh | 78 +- dune/gfe/assemblers/sumenergy.hh | 68 +- dune/gfe/assemblers/surfacecosseratenergy.hh | 672 +++--- .../surfacecosseratstressassembler.hh | 496 ++--- dune/gfe/assemblers/weightedsumenergy.hh | 3 +- dune/gfe/averagedistanceassembler.hh | 162 +- dune/gfe/averageinterface.hh | 1028 +++++----- dune/gfe/cosseratstrain.hh | 46 +- dune/gfe/cosseratvtkwriter.hh | 938 ++++----- dune/gfe/coupling/rodcontinuumcomplex.hh | 212 +- dune/gfe/coupling/rodcontinuumddstep.hh | 258 +-- .../coupling/rodcontinuumfixedpointstep.hh | 1082 +++++----- .../rodcontinuumsteklovpoincarestep.hh | 1776 ++++++++-------- dune/gfe/densities/bulkcosseratdensity.hh | 228 +- dune/gfe/densities/localdensity.hh | 48 +- dune/gfe/embeddedglobalgfefunction.hh | 576 +++--- dune/gfe/filereader.hh | 40 +- dune/gfe/geodesicdifference.hh | 16 +- dune/gfe/geodesicfefunctionadaptor.hh | 152 +- dune/gfe/geometries/moebiusstrip.hh | 168 +- dune/gfe/geometries/twistedstrip.hh | 148 +- dune/gfe/gfedifferencenormsquared.hh | 508 ++--- dune/gfe/globalgfefunction.hh | 818 ++++---- dune/gfe/gramschmidtsolver.hh | 2 +- dune/gfe/linearalgebra.hh | 278 +-- dune/gfe/localgeodesicfefunction.hh | 1104 +++++----- dune/gfe/localgfetestfunctionbasis.hh | 266 +-- dune/gfe/localprojectedfefunction.hh | 30 +- dune/gfe/localquickanddirtyfefunction.hh | 6 +- dune/gfe/localtangentfefunction.hh | 6 +- dune/gfe/maxnormtrustregion.hh | 100 +- dune/gfe/mixedriemanniantrsolver.cc | 898 ++++---- dune/gfe/mixedriemanniantrsolver.hh | 252 +-- dune/gfe/neumannenergy.hh | 128 +- dune/gfe/parallel/globalmapper.hh | 4 +- dune/gfe/parallel/globalp1mapper.hh | 4 +- dune/gfe/parallel/globalp2mapper.hh | 52 +- dune/gfe/parallel/matrixcommunicator.hh | 4 +- dune/gfe/parallel/p2mapper.hh | 3 +- dune/gfe/parallel/vectorcommunicator.hh | 6 +- dune/gfe/polardecomposition.hh | 600 +++--- dune/gfe/riemannianpnsolver.cc | 832 ++++---- dune/gfe/riemannianpnsolver.hh | 206 +- dune/gfe/riemanniantrsolver.cc | 1176 +++++------ dune/gfe/riemanniantrsolver.hh | 258 +-- dune/gfe/rodfactory.hh | 232 +-- dune/gfe/skewmatrix.hh | 162 +- dune/gfe/spaces/hyperbolichalfspacepoint.hh | 974 ++++----- dune/gfe/spaces/orthogonalmatrix.hh | 166 +- dune/gfe/spaces/productmanifold.hh | 310 +-- dune/gfe/spaces/quaternion.hh | 286 +-- dune/gfe/spaces/realtuple.hh | 450 ++-- dune/gfe/spaces/rigidbodymotion.hh | 638 +++--- dune/gfe/spaces/rotation.hh | 1826 ++++++++--------- dune/gfe/spaces/unitvector.hh | 834 ++++---- dune/gfe/svd.hh | 416 ++-- dune/gfe/symmetricmatrix.hh | 36 +- dune/gfe/targetspacertrsolver.cc | 148 +- dune/gfe/targetspacertrsolver.hh | 68 +- dune/gfe/tensor3.hh | 272 +-- dune/gfe/tensorssd.hh | 184 +- dune/gfe/trustregionmmgbasesolver.hh | 106 +- dune/gfe/vtkfile.hh | 4 +- src/compute-disc-error.cc | 394 ++-- src/cosserat-continuum.cc | 979 ++++----- src/film-on-substrate-stress-plot.cc | 97 +- src/film-on-substrate.cc | 153 +- src/gradient-flow.cc | 8 +- src/harmonicmaps.cc | 394 ++-- src/rod3d.cc | 441 ++-- src/rodobstacle.cc | 477 ++--- src/simofoxshell.cc | 82 +- test/adolctest-scalar-and-vector-mode.cc | 62 +- test/adolctest.cc | 711 +++---- test/averagedistanceassemblertest.cc | 344 ++-- test/cosseratcontinuumtest.cc | 136 +- test/cosseratenergytest.cc | 214 +- test/cosseratrodenergytest.cc | 137 +- test/cosseratrodtest.cc | 19 +- test/geodesicfeassemblerwrappertest.cc | 86 +- test/harmonicenergytest.cc | 132 +- test/harmonicmaptest.cc | 32 +- test/localgeodesicfefunctiontest.cc | 488 ++--- test/localgfetestfunctiontest.cc | 110 +- test/localprojectedfefunctiontest.cc | 322 ++- test/multiindex.hh | 66 +- test/nonplanarcosseratenergytest.cc | 272 +-- test/orthogonalmatrixtest.cc | 134 +- test/polardecompositiontest.cc | 358 ++-- test/rigidbodymotiontest.cc | 128 +- test/rotationtest.cc | 367 ++-- test/surfacecosseratstressassemblertest.cc | 74 +- test/svdtest.cc | 92 +- test/targetspacetest.cc | 598 +++--- test/test-cylinder.cc | 156 +- test/valuefactory.hh | 271 +-- 113 files changed, 17870 insertions(+), 17839 deletions(-) diff --git a/dune/gfe/assemblers/chiralskyrmionenergy.hh b/dune/gfe/assemblers/chiralskyrmionenergy.hh index 059fa068..d3e02a67 100644 --- a/dune/gfe/assemblers/chiralskyrmionenergy.hh +++ b/dune/gfe/assemblers/chiralskyrmionenergy.hh @@ -8,122 +8,121 @@ namespace Dune { -namespace GFE { - -/** \brief Energy of certain chiral Skyrmion - * - * The energy is discussed in: - * - Christof Melcher, "Chiral skyrmions in the plane", Proc. of the Royal Society, online DOI DOI: 10.1098/rspa.2014.0394 - */ -template<class Basis, class LocalInterpolationRule, class field_type> -class ChiralSkyrmionEnergy -: public GFE::LocalEnergy<Basis,UnitVector<field_type,3> > -{ - // various useful types - typedef UnitVector<field_type,3> TargetSpace; - typedef typename Basis::GridView GridView; - typedef typename GridView::ctype DT; - typedef typename TargetSpace::ctype RT; - typedef typename GridView::template Codim<0>::Entity Entity; - - // some other sizes - constexpr static int gridDim = GridView::dimension; - -public: - - ChiralSkyrmionEnergy(const Dune::ParameterTree& parameters) - { - h_ = parameters.template get<double>("h"); - kappa_ = parameters.template get<double>("kappa"); - } - - //! Dimension of a tangent space - constexpr static int blocksize = TargetSpace::TangentVector::dimension; - - /** \brief Assemble the energy for a single element */ - RT energy (const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localConfiguration) const override; - - field_type h_; - field_type kappa_; -}; - -template <class Basis, class LocalInterpolationRule, class field_type> -typename ChiralSkyrmionEnergy<Basis, LocalInterpolationRule, field_type>::RT -ChiralSkyrmionEnergy<Basis, LocalInterpolationRule, field_type>:: -energy(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localConfiguration) const -{ - typedef typename GridView::template Codim<0>::Entity::Geometry Geometry; - - RT energy = 0; - - const auto element = localView.element(); - const auto& localFiniteElement = localView.tree().finiteElement(); - LocalInterpolationRule localInterpolationRule(localFiniteElement,localConfiguration); - - int quadOrder = (element.type().isSimplex()) ? (localFiniteElement.localBasis().order()-1) * 2 + namespace GFE { + + /** \brief Energy of certain chiral Skyrmion + * + * The energy is discussed in: + * - Christof Melcher, "Chiral skyrmions in the plane", Proc. of the Royal Society, online DOI DOI: 10.1098/rspa.2014.0394 + */ + template<class Basis, class LocalInterpolationRule, class field_type> + class ChiralSkyrmionEnergy + : public GFE::LocalEnergy<Basis,UnitVector<field_type,3> > + { + // various useful types + typedef UnitVector<field_type,3> TargetSpace; + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef typename TargetSpace::ctype RT; + typedef typename GridView::template Codim<0>::Entity Entity; + + // some other sizes + constexpr static int gridDim = GridView::dimension; + + public: + + ChiralSkyrmionEnergy(const Dune::ParameterTree& parameters) + { + h_ = parameters.template get<double>("h"); + kappa_ = parameters.template get<double>("kappa"); + } + + //! Dimension of a tangent space + constexpr static int blocksize = TargetSpace::TangentVector::dimension; + + /** \brief Assemble the energy for a single element */ + RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localConfiguration) const override; + + field_type h_; + field_type kappa_; + }; + + template <class Basis, class LocalInterpolationRule, class field_type> + typename ChiralSkyrmionEnergy<Basis, LocalInterpolationRule, field_type>::RT + ChiralSkyrmionEnergy<Basis, LocalInterpolationRule, field_type>:: + energy(const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localConfiguration) const + { + typedef typename GridView::template Codim<0>::Entity::Geometry Geometry; + + RT energy = 0; + + const auto element = localView.element(); + const auto& localFiniteElement = localView.tree().finiteElement(); + LocalInterpolationRule localInterpolationRule(localFiniteElement,localConfiguration); + + int quadOrder = (element.type().isSimplex()) ? (localFiniteElement.localBasis().order()-1) * 2 : localFiniteElement.localBasis().order() * 2 * gridDim; - const Dune::QuadratureRule<double, gridDim>& quad - = Dune::QuadratureRules<double, gridDim>::rule(element.type(), quadOrder); + const Dune::QuadratureRule<double, gridDim>& quad + = Dune::QuadratureRules<double, gridDim>::rule(element.type(), quadOrder); - for (size_t pt=0; pt<quad.size(); pt++) { + for (size_t pt=0; pt<quad.size(); pt++) { - // Local position of the quadrature point - const Dune::FieldVector<double,gridDim>& quadPos = quad[pt].position(); + // Local position of the quadrature point + const Dune::FieldVector<double,gridDim>& quadPos = quad[pt].position(); - const double integrationElement = element.geometry().integrationElement(quadPos); + const double integrationElement = element.geometry().integrationElement(quadPos); - const typename Geometry::JacobianInverseTransposed& jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos); + const typename Geometry::JacobianInverseTransposed& jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos); - double weight = quad[pt].weight() * integrationElement; + double weight = quad[pt].weight() * integrationElement; - // The value of the function - auto value = localInterpolationRule.evaluate(quadPos); + // The value of the function + auto value = localInterpolationRule.evaluate(quadPos); - // The derivative of the local function defined on the reference element - typename LocalInterpolationRule::DerivativeType referenceDerivative = localInterpolationRule.evaluateDerivative(quadPos,value); + // The derivative of the local function defined on the reference element + typename LocalInterpolationRule::DerivativeType referenceDerivative = localInterpolationRule.evaluateDerivative(quadPos,value); - // The derivative of the function defined on the actual element - typename LocalInterpolationRule::DerivativeType derivative(0); + // The derivative of the function defined on the actual element + typename LocalInterpolationRule::DerivativeType derivative(0); - for (size_t comp=0; comp<referenceDerivative.N(); comp++) - jacobianInverseTransposed.umv(referenceDerivative[comp], derivative[comp]); + for (size_t comp=0; comp<referenceDerivative.N(); comp++) + jacobianInverseTransposed.umv(referenceDerivative[comp], derivative[comp]); - ////////////////////////////////////////////////////////////// - // Exchange energy (aka harmonic energy) - ////////////////////////////////////////////////////////////// - // The Frobenius norm is the correct norm here if the metric of TargetSpace is the identity. - // (And if the metric of the domain space is the identity, which it always is here.) - energy += weight * 0.5 * derivative.frobenius_norm2(); + ////////////////////////////////////////////////////////////// + // Exchange energy (aka harmonic energy) + ////////////////////////////////////////////////////////////// + // The Frobenius norm is the correct norm here if the metric of TargetSpace is the identity. + // (And if the metric of the domain space is the identity, which it always is here.) + energy += weight * 0.5 * derivative.frobenius_norm2(); - ////////////////////////////////////////////////////////////// - // Dzyaloshinskii-Moriya interaction term - ////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////// + // Dzyaloshinskii-Moriya interaction term + ////////////////////////////////////////////////////////////// - // derivative[a][b] contains the partial derivative of m_a in the direction x_b - Dune::FieldVector<field_type, 3> curl = {derivative[2][1], -derivative[2][0], derivative[1][0]-derivative[0][1]}; + // derivative[a][b] contains the partial derivative of m_a in the direction x_b + Dune::FieldVector<field_type, 3> curl = {derivative[2][1], -derivative[2][0], derivative[1][0]-derivative[0][1]}; - FieldVector<field_type, 3> v = value.globalCoordinates(); + FieldVector<field_type, 3> v = value.globalCoordinates(); - energy += weight * kappa_ * (v * curl); + energy += weight * kappa_ * (v * curl); - ////////////////////////////////////////////////////////////// - // Zeeman interaction term - ////////////////////////////////////////////////////////////// - v[2] -= 1; // subtract e_3 - energy += weight * 0.5 * h_ * v.two_norm2(); + ////////////////////////////////////////////////////////////// + // Zeeman interaction term + ////////////////////////////////////////////////////////////// + v[2] -= 1; // subtract e_3 + energy += weight * 0.5 * h_ * v.two_norm2(); - } + } - return energy; -} + return energy; + } -} // namespace GFE + } // namespace GFE } // namespace Dune #endif - diff --git a/dune/gfe/assemblers/cosseratenergystiffness.hh b/dune/gfe/assemblers/cosseratenergystiffness.hh index 4e5eceee..3caffc35 100644 --- a/dune/gfe/assemblers/cosseratenergystiffness.hh +++ b/dune/gfe/assemblers/cosseratenergystiffness.hh @@ -47,8 +47,8 @@ class LocalFiniteElementFactory { public: static auto get(const typename Basis::LocalView& localView, - std::integral_constant<std::size_t, i> iType) - -> decltype(localView.tree().child(iType,0).finiteElement()) + std::integral_constant<std::size_t, i> iType) + -> decltype(localView.tree().child(iType,0).finiteElement()) { return localView.tree().child(iType,0).finiteElement(); } @@ -60,8 +60,8 @@ class LocalFiniteElementFactory<Dune::Functions::LagrangeBasis<GridView,order>,i { public: static auto get(const typename Dune::Functions::LagrangeBasis<GridView,order>::LocalView& localView, - std::integral_constant<std::size_t, i> iType) - -> decltype(localView.tree().finiteElement()) + std::integral_constant<std::size_t, i> iType) + -> decltype(localView.tree().finiteElement()) { return localView.tree().finiteElement(); } @@ -69,276 +69,276 @@ public: template<class Basis, int dim, class field_type=double> class CosseratEnergyLocalStiffness - : public Dune::GFE::LocalEnergy<Basis,RigidBodyMotion<field_type,dim> >, - public MixedLocalGeodesicFEStiffness<Basis, - RealTuple<field_type,dim>, - Rotation<field_type,dim> > + : public Dune::GFE::LocalEnergy<Basis,RigidBodyMotion<field_type,dim> >, + public MixedLocalGeodesicFEStiffness<Basis, + RealTuple<field_type,dim>, + Rotation<field_type,dim> > { - // grid types - typedef typename Basis::GridView GridView; - typedef typename GridView::ctype DT; - typedef RigidBodyMotion<field_type,dim> TargetSpace; - typedef typename TargetSpace::ctype RT; - typedef typename GridView::template Codim<0>::Entity Entity; - - // some other sizes - constexpr static int gridDim = GridView::dimension; - constexpr static int dimworld = GridView::dimensionworld; - - /** \brief Compute the (row-wise) curl of a matrix R \f$ - \param DR The partial derivatives of the matrix R - */ - static Dune::FieldMatrix<field_type,dim,dim> curl(const Tensor3<field_type,dim,dim,dim>& DR) - { - Dune::FieldMatrix<field_type,dim,dim> result; - - for (int i=0; i<dim; i++) { - result[i][0] = DR[i][2][1] - DR[i][1][2]; - result[i][1] = DR[i][0][2] - DR[i][2][0]; - result[i][2] = DR[i][1][0] - DR[i][0][1]; - } - - return result; + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef RigidBodyMotion<field_type,dim> TargetSpace; + typedef typename TargetSpace::ctype RT; + typedef typename GridView::template Codim<0>::Entity Entity; + + // some other sizes + constexpr static int gridDim = GridView::dimension; + constexpr static int dimworld = GridView::dimensionworld; + + /** \brief Compute the (row-wise) curl of a matrix R \f$ + \param DR The partial derivatives of the matrix R + */ + static Dune::FieldMatrix<field_type,dim,dim> curl(const Tensor3<field_type,dim,dim,dim>& DR) + { + Dune::FieldMatrix<field_type,dim,dim> result; + + for (int i=0; i<dim; i++) { + result[i][0] = DR[i][2][1] - DR[i][1][2]; + result[i][1] = DR[i][0][2] - DR[i][2][0]; + result[i][2] = DR[i][1][0] - DR[i][0][1]; } + return result; + } + public: - /** \brief Constructor with a set of material parameters - * \param parameters The material parameters - */ - CosseratEnergyLocalStiffness(const Dune::ParameterTree& parameters, - const BoundaryPatch<GridView>* neumannBoundary, - const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> neumannFunction, - const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> volumeLoad) + /** \brief Constructor with a set of material parameters + * \param parameters The material parameters + */ + CosseratEnergyLocalStiffness(const Dune::ParameterTree& parameters, + const BoundaryPatch<GridView>* neumannBoundary, + const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> neumannFunction, + const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> volumeLoad) : neumannBoundary_(neumannBoundary), - neumannFunction_(neumannFunction), - volumeLoad_(volumeLoad) - { - // The shell thickness // only relevant for dim == 2 - thickness_ = parameters.template get<double>("thickness"); + neumannFunction_(neumannFunction), + volumeLoad_(volumeLoad) + { + // The shell thickness // only relevant for dim == 2 + thickness_ = parameters.template get<double>("thickness"); - // Lame constants - mu_ = parameters.template get<double>("mu"); - lambda_ = parameters.template get<double>("lambda"); + // Lame constants + mu_ = parameters.template get<double>("mu"); + lambda_ = parameters.template get<double>("lambda"); - // Cosserat couple modulus - mu_c_ = parameters.template get<double>("mu_c"); + // Cosserat couple modulus + mu_c_ = parameters.template get<double>("mu_c"); - // Length scale parameter - L_c_ = parameters.template get<double>("L_c"); + // Length scale parameter + L_c_ = parameters.template get<double>("L_c"); - // Curvature exponent - q_ = parameters.template get<double>("q"); + // Curvature exponent + q_ = parameters.template get<double>("q"); - // Shear correction factor // only relevant for dim == 2 - kappa_ = parameters.template get<double>("kappa"); + // Shear correction factor // only relevant for dim == 2 + kappa_ = parameters.template get<double>("kappa"); - // Curvature parameters - b1_ = parameters.template get<double>("b1"); - b2_ = parameters.template get<double>("b2"); - b3_ = parameters.template get<double>("b3"); - } + // Curvature parameters + b1_ = parameters.template get<double>("b1"); + b2_ = parameters.template get<double>("b2"); + b3_ = parameters.template get<double>("b3"); + } - /** \brief Assemble the energy for a single element */ - RT energy (const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution) const override; - - /** \brief Assemble the energy for a single element */ - RT energy (const typename Basis::LocalView& localView, - const std::vector<RealTuple<field_type,dim> >& localDisplacementConfiguration, - const std::vector<Rotation<field_type,dim> >& localOrientationConfiguration) const override; - - /** \brief The energy \f$ W_{mp}(\overline{U}) \f$, as written in - * the first equation of (4.4) in Neff's paper from 2006: A geometrically exact planar Cosserat shell model with microstructure: Existence of minimizers for zero Cosserat couple modulus - * OR: the equation (2.27) of 2019: Refined dimensional reduction for isotropic elastic Cosserat shells with initial curvature - */ - RT quadraticMembraneEnergy(const Dune::GFE::CosseratStrain<field_type,3,gridDim>& U) const - { - Dune::FieldMatrix<field_type,3,3> UMinus1 = U.matrix(); - for (int i=0; i<dim; i++) - UMinus1[i][i] -= 1; - - return mu_ * Dune::GFE::sym(UMinus1).frobenius_norm2() - + mu_c_ * Dune::GFE::skew(UMinus1).frobenius_norm2() + /** \brief Assemble the energy for a single element */ + RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution) const override; + + /** \brief Assemble the energy for a single element */ + RT energy (const typename Basis::LocalView& localView, + const std::vector<RealTuple<field_type,dim> >& localDisplacementConfiguration, + const std::vector<Rotation<field_type,dim> >& localOrientationConfiguration) const override; + + /** \brief The energy \f$ W_{mp}(\overline{U}) \f$, as written in + * the first equation of (4.4) in Neff's paper from 2006: A geometrically exact planar Cosserat shell model with microstructure: Existence of minimizers for zero Cosserat couple modulus + * OR: the equation (2.27) of 2019: Refined dimensional reduction for isotropic elastic Cosserat shells with initial curvature + */ + RT quadraticMembraneEnergy(const Dune::GFE::CosseratStrain<field_type,3,gridDim>& U) const + { + Dune::FieldMatrix<field_type,3,3> UMinus1 = U.matrix(); + for (int i=0; i<dim; i++) + UMinus1[i][i] -= 1; + + return mu_ * Dune::GFE::sym(UMinus1).frobenius_norm2() + + mu_c_ * Dune::GFE::skew(UMinus1).frobenius_norm2() #ifdef QUADRATIC_2006 - + (mu_*lambda_)/(2*mu_ + lambda_) * Dune::GFE::traceSquared(UMinus1); // Dune::GFE::traceSquared(UMinus1) = Dune::GFE::traceSquared(Dune::GFE::sym(UMinus1)) + + (mu_*lambda_)/(2*mu_ + lambda_) * Dune::GFE::traceSquared(UMinus1); // Dune::GFE::traceSquared(UMinus1) = Dune::GFE::traceSquared(Dune::GFE::sym(UMinus1)) #else - + lambda_/2 * Dune::GFE::traceSquared(UMinus1); // Dune::GFE::traceSquared(UMinus1) = Dune::GFE::traceSquared(Dune::GFE::sym(UMinus1)) + + lambda_/2 * Dune::GFE::traceSquared(UMinus1); // Dune::GFE::traceSquared(UMinus1) = Dune::GFE::traceSquared(Dune::GFE::sym(UMinus1)) #endif - } + } - /** \brief The energy \f$ W_{mp}(\overline{U}) \f$, as written in - * the second equation of (4.4) in Neff's paper - */ - RT longQuadraticMembraneEnergy(const Dune::GFE::CosseratStrain<field_type,3,gridDim>& U) const - { - RT result = 0; + /** \brief The energy \f$ W_{mp}(\overline{U}) \f$, as written in + * the second equation of (4.4) in Neff's paper + */ + RT longQuadraticMembraneEnergy(const Dune::GFE::CosseratStrain<field_type,3,gridDim>& U) const + { + RT result = 0; - // shear-stretch energy - Dune::FieldMatrix<field_type,dim-1,dim-1> sym2x2; - for (int i=0; i<dim-1; i++) - for (int j=0; j<dim-1; j++) - sym2x2[i][j] = 0.5 * (U.matrix()[i][j] + U.matrix()[j][i]) - (i==j); + // shear-stretch energy + Dune::FieldMatrix<field_type,dim-1,dim-1> sym2x2; + for (int i=0; i<dim-1; i++) + for (int j=0; j<dim-1; j++) + sym2x2[i][j] = 0.5 * (U.matrix()[i][j] + U.matrix()[j][i]) - (i==j); - result += mu_ * sym2x2.frobenius_norm2(); + result += mu_ * sym2x2.frobenius_norm2(); - // first order drill energy - Dune::FieldMatrix<field_type,dim-1,dim-1> skew2x2; - for (int i=0; i<dim-1; i++) - for (int j=0; j<dim-1; j++) - skew2x2[i][j] = 0.5 * (U.matrix()[i][j] - U.matrix()[j][i]); + // first order drill energy + Dune::FieldMatrix<field_type,dim-1,dim-1> skew2x2; + for (int i=0; i<dim-1; i++) + for (int j=0; j<dim-1; j++) + skew2x2[i][j] = 0.5 * (U.matrix()[i][j] - U.matrix()[j][i]); - result += mu_c_ * skew2x2.frobenius_norm2(); + result += mu_c_ * skew2x2.frobenius_norm2(); - // classical transverse shear energy - result += kappa_ * (mu_ + mu_c_)/2 * (U.matrix()[2][0]*U.matrix()[2][0] + U.matrix()[2][1]*U.matrix()[2][1]); + // classical transverse shear energy + result += kappa_ * (mu_ + mu_c_)/2 * (U.matrix()[2][0]*U.matrix()[2][0] + U.matrix()[2][1]*U.matrix()[2][1]); - // elongational stretch energy - result += mu_*lambda_ / (2*mu_ + lambda_) * traceSquared(sym2x2); + // elongational stretch energy + result += mu_*lambda_ / (2*mu_ + lambda_) * traceSquared(sym2x2); - return result; - } + return result; + } - /** \brief Energy for large-deformation problems (private communication by Patrizio Neff) - */ - RT nonquadraticMembraneEnergy(const Dune::GFE::CosseratStrain<field_type,3,gridDim>& U) const - { - Dune::FieldMatrix<field_type,3,3> UMinus1 = U.matrix(); - for (int i=0; i<dim; i++) - UMinus1[i][i] -= 1; + /** \brief Energy for large-deformation problems (private communication by Patrizio Neff) + */ + RT nonquadraticMembraneEnergy(const Dune::GFE::CosseratStrain<field_type,3,gridDim>& U) const + { + Dune::FieldMatrix<field_type,3,3> UMinus1 = U.matrix(); + for (int i=0; i<dim; i++) + UMinus1[i][i] -= 1; - RT detU = U.determinant(); + RT detU = U.determinant(); - return mu_ * Dune::GFE::sym(UMinus1).frobenius_norm2() + mu_c_ * Dune::GFE::skew(UMinus1).frobenius_norm2() - + (mu_*lambda_)/(2*mu_ + lambda_) * 0.5 * ((detU-1)*(detU-1) + (1.0/detU -1)*(1.0/detU -1)); - } + return mu_ * Dune::GFE::sym(UMinus1).frobenius_norm2() + mu_c_ * Dune::GFE::skew(UMinus1).frobenius_norm2() + + (mu_*lambda_)/(2*mu_ + lambda_) * 0.5 * ((detU-1)*(detU-1) + (1.0/detU -1)*(1.0/detU -1)); + } - /** \brief The energy \f$ W_{mp}(\overline{U}) \f$, as written in - * the second equation of (4.4) in Neff's paper - */ - RT longNonquadraticMembraneEnergy(const Dune::GFE::CosseratStrain<field_type,3,gridDim>& U) const - { - RT result = 0; + /** \brief The energy \f$ W_{mp}(\overline{U}) \f$, as written in + * the second equation of (4.4) in Neff's paper + */ + RT longNonquadraticMembraneEnergy(const Dune::GFE::CosseratStrain<field_type,3,gridDim>& U) const + { + RT result = 0; - // shear-stretch energy - Dune::FieldMatrix<field_type,dim-1,dim-1> sym2x2; - for (int i=0; i<dim-1; i++) - for (int j=0; j<dim-1; j++) - sym2x2[i][j] = 0.5 * (U.matrix()[i][j] + U.matrix()[j][i]) - (i==j); + // shear-stretch energy + Dune::FieldMatrix<field_type,dim-1,dim-1> sym2x2; + for (int i=0; i<dim-1; i++) + for (int j=0; j<dim-1; j++) + sym2x2[i][j] = 0.5 * (U.matrix()[i][j] + U.matrix()[j][i]) - (i==j); - result += mu_ * sym2x2.frobenius_norm2(); + result += mu_ * sym2x2.frobenius_norm2(); - // first order drill energy - Dune::FieldMatrix<field_type,dim-1,dim-1> skew2x2; - for (int i=0; i<dim-1; i++) - for (int j=0; j<dim-1; j++) - skew2x2[i][j] = 0.5 * (U.matrix()[i][j] - U.matrix()[j][i]); + // first order drill energy + Dune::FieldMatrix<field_type,dim-1,dim-1> skew2x2; + for (int i=0; i<dim-1; i++) + for (int j=0; j<dim-1; j++) + skew2x2[i][j] = 0.5 * (U.matrix()[i][j] - U.matrix()[j][i]); - result += mu_c_ * skew2x2.frobenius_norm2(); + result += mu_c_ * skew2x2.frobenius_norm2(); - // classical transverse shear energy - result += kappa_ * (mu_ + mu_c_)/2 * (U.matrix()[2][0]*U.matrix()[2][0] + U.matrix()[2][1]*U.matrix()[2][1]); + // classical transverse shear energy + result += kappa_ * (mu_ + mu_c_)/2 * (U.matrix()[2][0]*U.matrix()[2][0] + U.matrix()[2][1]*U.matrix()[2][1]); - // elongational stretch energy - RT detU = U.determinant(); - result += (mu_*lambda_)/(2*mu_ + lambda_) * 0.5 * ((detU-1)*(detU-1) + (1.0/detU -1)*(1.0/detU -1)); + // elongational stretch energy + RT detU = U.determinant(); + result += (mu_*lambda_)/(2*mu_ + lambda_) * 0.5 * ((detU-1)*(detU-1) + (1.0/detU -1)*(1.0/detU -1)); - return result; - } + return result; + } - RT curvatureEnergy(const Tensor3<field_type,3,3,gridDim>& DR) const - { - using std::pow; + RT curvatureEnergy(const Tensor3<field_type,3,3,gridDim>& DR) const + { + using std::pow; #ifdef DONT_USE_CURL - return mu_ * pow(L_c_ * L_c_ * DR.frobenius_norm2(),q_/2.0); + return mu_ * pow(L_c_ * L_c_ * DR.frobenius_norm2(),q_/2.0); #else - return mu_ * pow(L_c_ * L_c_ * curl(DR).frobenius_norm2(),q_/2.0); + return mu_ * pow(L_c_ * L_c_ * curl(DR).frobenius_norm2(),q_/2.0); #endif - } + } - RT curvatureWithWryness(const Dune::FieldMatrix<field_type,dim,dim>& R, const Tensor3<field_type,3,3,gridDim>& DR) const - { - // construct Wryness tensor \Gamma as in "Refined dimensional reduction for isotropic elastic Cosserat shells with initial curvature" - Dune::FieldMatrix<field_type,3,3> dRx1(0); //Derivative of R with respect to x1 - Dune::FieldMatrix<field_type,3,3> dRx2(0); //Derivative of R with respect to x2 - Dune::FieldMatrix<field_type,3,3> dRx3(0); //Derivative of R with respect to x3, this of course only exists if gridDim = 3 (then dim = 3 also, because dim >= gridDim, always) - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) { - dRx1[i][j] = DR[i][j][0]; - dRx2[i][j] = DR[i][j][1]; - dRx3[i][j] = gridDim == 3 ? DR[i][j][2] : 0; - } - - Dune::FieldMatrix<field_type,3,3> wryness(0); + RT curvatureWithWryness(const Dune::FieldMatrix<field_type,dim,dim>& R, const Tensor3<field_type,3,3,gridDim>& DR) const + { + // construct Wryness tensor \Gamma as in "Refined dimensional reduction for isotropic elastic Cosserat shells with initial curvature" + Dune::FieldMatrix<field_type,3,3> dRx1(0); //Derivative of R with respect to x1 + Dune::FieldMatrix<field_type,3,3> dRx2(0); //Derivative of R with respect to x2 + Dune::FieldMatrix<field_type,3,3> dRx3(0); //Derivative of R with respect to x3, this of course only exists if gridDim = 3 (then dim = 3 also, because dim >= gridDim, always) + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) { + dRx1[i][j] = DR[i][j][0]; + dRx2[i][j] = DR[i][j][1]; + dRx3[i][j] = gridDim == 3 ? DR[i][j][2] : 0; + } + + Dune::FieldMatrix<field_type,3,3> wryness(0); #if DUNE_VERSION_LT(DUNE_COMMON, 2, 9) - Dune::FieldMatrix<field_type,dim,dim> transposeR; - Dune::MatrixVector::transpose(R,transposeR); - auto axialVectorx1 = SkewMatrix<field_type,3>(transposeR*dRx1).axial(); - auto axialVectorx2 = SkewMatrix<field_type,3>(transposeR*dRx2).axial(); - auto axialVectorx3 = SkewMatrix<field_type,3>(transposeR*dRx3).axial(); + Dune::FieldMatrix<field_type,dim,dim> transposeR; + Dune::MatrixVector::transpose(R,transposeR); + auto axialVectorx1 = SkewMatrix<field_type,3>(transposeR*dRx1).axial(); + auto axialVectorx2 = SkewMatrix<field_type,3>(transposeR*dRx2).axial(); + auto axialVectorx3 = SkewMatrix<field_type,3>(transposeR*dRx3).axial(); #else - auto axialVectorx1 = SkewMatrix<field_type,3>(transpose(R)*dRx1).axial(); - auto axialVectorx2 = SkewMatrix<field_type,3>(transpose(R)*dRx2).axial(); - auto axialVectorx3 = SkewMatrix<field_type,3>(transpose(R)*dRx3).axial(); + auto axialVectorx1 = SkewMatrix<field_type,3>(transpose(R)*dRx1).axial(); + auto axialVectorx2 = SkewMatrix<field_type,3>(transpose(R)*dRx2).axial(); + auto axialVectorx3 = SkewMatrix<field_type,3>(transpose(R)*dRx3).axial(); #endif - for (int i=0; i<3; i++) { - wryness[i][0] = axialVectorx1[i]; - wryness[i][1] = axialVectorx2[i]; - wryness[i][2] = gridDim == 3 ? axialVectorx3[i] : 0; - } - - // The choice for the Frobenius norm here is b1=b2=1 and b3 = 1/3 - return mu_ * L_c_ * L_c_ * (b1_ * Dune::GFE::dev(Dune::GFE::sym(wryness)).frobenius_norm2() - + b2_ * Dune::GFE::skew(wryness).frobenius_norm2() + b3_ * Dune::GFE::traceSquared(wryness)); + for (int i=0; i<3; i++) { + wryness[i][0] = axialVectorx1[i]; + wryness[i][1] = axialVectorx2[i]; + wryness[i][2] = gridDim == 3 ? axialVectorx3[i] : 0; } + // The choice for the Frobenius norm here is b1=b2=1 and b3 = 1/3 + return mu_ * L_c_ * L_c_ * (b1_ * Dune::GFE::dev(Dune::GFE::sym(wryness)).frobenius_norm2() + + b2_ * Dune::GFE::skew(wryness).frobenius_norm2() + b3_ * Dune::GFE::traceSquared(wryness)); + } - RT bendingEnergy(const Dune::FieldMatrix<field_type,dim,dim>& R, const Tensor3<field_type,3,3,gridDim>& DR) const - { - // left-multiply the derivative of the third director (in DR[][2][]) with R^T - Dune::FieldMatrix<field_type,3,3> RT_DR3(0); - for (int i=0; i<3; i++) - for (int j=0; j<gridDim; j++) - for (int k=0; k<3; k++) - RT_DR3[i][j] += R[k][i] * DR[k][2][j]; - return mu_ * Dune::GFE::sym(RT_DR3).frobenius_norm2() - + mu_c_ * Dune::GFE::skew(RT_DR3).frobenius_norm2() - + mu_*lambda_/(2*mu_+lambda_) * Dune::GFE::traceSquared(RT_DR3); - } + RT bendingEnergy(const Dune::FieldMatrix<field_type,dim,dim>& R, const Tensor3<field_type,3,3,gridDim>& DR) const + { + // left-multiply the derivative of the third director (in DR[][2][]) with R^T + Dune::FieldMatrix<field_type,3,3> RT_DR3(0); + for (int i=0; i<3; i++) + for (int j=0; j<gridDim; j++) + for (int k=0; k<3; k++) + RT_DR3[i][j] += R[k][i] * DR[k][2][j]; + + return mu_ * Dune::GFE::sym(RT_DR3).frobenius_norm2() + + mu_c_ * Dune::GFE::skew(RT_DR3).frobenius_norm2() + + mu_*lambda_/(2*mu_+lambda_) * Dune::GFE::traceSquared(RT_DR3); + } - /** \brief The shell thickness */ - double thickness_; + /** \brief The shell thickness */ + double thickness_; - /** \brief Lame constants */ - double mu_, lambda_; + /** \brief Lame constants */ + double mu_, lambda_; - /** \brief Cosserat couple modulus, preferably 0 */ - double mu_c_; + /** \brief Cosserat couple modulus, preferably 0 */ + double mu_c_; - /** \brief Length scale parameter */ - double L_c_; + /** \brief Length scale parameter */ + double L_c_; - /** \brief Curvature exponent */ - double q_; + /** \brief Curvature exponent */ + double q_; - /** \brief Shear correction factor */ - double kappa_; + /** \brief Shear correction factor */ + double kappa_; - /** \brief Curvature parameters */ - double b1_, b2_, b3_; + /** \brief Curvature parameters */ + double b1_, b2_, b3_; - /** \brief The Neumann boundary */ - const BoundaryPatch<GridView>* neumannBoundary_; + /** \brief The Neumann boundary */ + const BoundaryPatch<GridView>* neumannBoundary_; - /** \brief The function implementing the Neumann data */ - const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> neumannFunction_; + /** \brief The function implementing the Neumann data */ + const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> neumannFunction_; - /** \brief The function implementing a volume load */ - const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> volumeLoad_; + /** \brief The function implementing a volume load */ + const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> volumeLoad_; }; template <class Basis, int dim, class field_type> @@ -348,139 +348,139 @@ CosseratEnergyLocalStiffness<Basis,dim,field_type>:: energy(const typename Basis::LocalView& localView, const std::vector<RigidBodyMotion<field_type,dim> >& localSolution) const { - RT energy = 0; + RT energy = 0; - auto element = localView.element(); + auto element = localView.element(); - using namespace Dune::TypeTree::Indices; - const auto& localFiniteElement = LocalFiniteElementFactory<Basis,0>::get(localView,_0); + using namespace Dune::TypeTree::Indices; + const auto& localFiniteElement = LocalFiniteElementFactory<Basis,0>::get(localView,_0); #ifdef PROJECTED_INTERPOLATION - typedef Dune::GFE::LocalProjectedFEFunction<gridDim, DT, decltype(localFiniteElement), TargetSpace> LocalGFEFunctionType; + typedef Dune::GFE::LocalProjectedFEFunction<gridDim, DT, decltype(localFiniteElement), TargetSpace> LocalGFEFunctionType; #else - typedef LocalGeodesicFEFunction<gridDim, DT, decltype(localFiniteElement), TargetSpace> LocalGFEFunctionType; + typedef LocalGeodesicFEFunction<gridDim, DT, decltype(localFiniteElement), TargetSpace> LocalGFEFunctionType; #endif - LocalGFEFunctionType localGeodesicFEFunction(localFiniteElement,localSolution); + LocalGFEFunctionType localGeodesicFEFunction(localFiniteElement,localSolution); - int quadOrder = (element.type().isSimplex()) ? localFiniteElement.localBasis().order() + int quadOrder = (element.type().isSimplex()) ? localFiniteElement.localBasis().order() : localFiniteElement.localBasis().order() * gridDim; - const auto& quad = Dune::QuadratureRules<DT, gridDim>::rule(element.type(), quadOrder); + const auto& quad = Dune::QuadratureRules<DT, gridDim>::rule(element.type(), quadOrder); - for (size_t pt=0; pt<quad.size(); pt++) { + for (size_t pt=0; pt<quad.size(); pt++) { - // Local position of the quadrature point - const Dune::FieldVector<DT,gridDim>& quadPos = quad[pt].position(); + // Local position of the quadrature point + const Dune::FieldVector<DT,gridDim>& quadPos = quad[pt].position(); - const DT integrationElement = element.geometry().integrationElement(quadPos); + const DT integrationElement = element.geometry().integrationElement(quadPos); - const auto jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos); + const auto jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos); - DT weight = quad[pt].weight() * integrationElement; + DT weight = quad[pt].weight() * integrationElement; - // The value of the local function - RigidBodyMotion<field_type,dim> value = localGeodesicFEFunction.evaluate(quadPos); + // The value of the local function + RigidBodyMotion<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 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); + // 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]); + for (size_t comp=0; comp<referenceDerivative.N(); comp++) + jacobianInverseTransposed.umv(referenceDerivative[comp], derivative[comp]); - ///////////////////////////////////////////////////////// - // compute U, the Cosserat strain - ///////////////////////////////////////////////////////// - static_assert(dim>=gridDim, "Codim of the grid must be nonnegative"); + ///////////////////////////////////////////////////////// + // compute U, the Cosserat strain + ///////////////////////////////////////////////////////// + static_assert(dim>=gridDim, "Codim of the grid must be nonnegative"); - // - Dune::FieldMatrix<field_type,dim,dim> R; - value.q.matrix(R); + // + Dune::FieldMatrix<field_type,dim,dim> R; + value.q.matrix(R); - Dune::GFE::CosseratStrain<field_type,dim,gridDim> U(derivative,R); + Dune::GFE::CosseratStrain<field_type,dim,gridDim> U(derivative,R); - ////////////////////////////////////////////////////////// - // Compute the derivative of the rotation - // Note: we need it in matrix coordinates - ////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////// + // Compute the derivative of the rotation + // Note: we need it in matrix coordinates + ////////////////////////////////////////////////////////// - Tensor3<field_type,3,3,gridDim> DR = value.quaternionTangentToMatrixTangent(derivative); + Tensor3<field_type,3,3,gridDim> DR = value.quaternionTangentToMatrixTangent(derivative); - // Add the local energy density - if (gridDim==2) { + // Add the local energy density + if (gridDim==2) { #ifdef QUADRATIC_MEMBRANE_ENERGY - //energy += weight * thickness_ * quadraticMembraneEnergy(U.matrix()); - energy += weight * thickness_ * longQuadraticMembraneEnergy(U); + //energy += weight * thickness_ * quadraticMembraneEnergy(U.matrix()); + energy += weight * thickness_ * longQuadraticMembraneEnergy(U); #else - //energy += weight * thickness_ * nonquadraticMembraneEnergy(U); - energy += weight * thickness_ * longNonquadraticMembraneEnergy(U); + //energy += weight * thickness_ * nonquadraticMembraneEnergy(U); + energy += weight * thickness_ * longNonquadraticMembraneEnergy(U); #endif - energy += weight * thickness_ * curvatureEnergy(DR); - energy += weight * std::pow(thickness_,3) / 12.0 * bendingEnergy(R,DR); - } else if (gridDim==3) { - energy += weight * quadraticMembraneEnergy(U); + energy += weight * thickness_ * curvatureEnergy(DR); + energy += weight * std::pow(thickness_,3) / 12.0 * bendingEnergy(R,DR); + } else if (gridDim==3) { + energy += weight * quadraticMembraneEnergy(U); #ifdef CURVATURE_WITH_WRYNESS - energy += weight * curvatureWithWryness(R,DR); + energy += weight * curvatureWithWryness(R,DR); #else - energy += weight * curvatureEnergy(DR); + energy += weight * curvatureEnergy(DR); #endif - } else - DUNE_THROW(Dune::NotImplemented, "CosseratEnergyStiffness for 1d grids"); - - /////////////////////////////////////////////////////////// - // Volume load contribution - /////////////////////////////////////////////////////////// + } else + DUNE_THROW(Dune::NotImplemented, "CosseratEnergyStiffness for 1d grids"); - if (not volumeLoad_) - continue; + /////////////////////////////////////////////////////////// + // Volume load contribution + /////////////////////////////////////////////////////////// - // Value of the volume load density at the current position - auto volumeLoadDensity = volumeLoad_(element.geometry().global(quad[pt].position())); + if (not volumeLoad_) + continue; - // Only translational dofs are affected by the volume load - for (size_t i=0; i<volumeLoadDensity.size(); i++) - energy += (volumeLoadDensity[i] * value.r[i]) * quad[pt].weight() * integrationElement; - } + // Value of the volume load density at the current position + auto volumeLoadDensity = volumeLoad_(element.geometry().global(quad[pt].position())); + // Only translational dofs are affected by the volume load + for (size_t i=0; i<volumeLoadDensity.size(); i++) + energy += (volumeLoadDensity[i] * value.r[i]) * quad[pt].weight() * integrationElement; + } - ////////////////////////////////////////////////////////////////////////////// - // Assemble boundary contributions - ////////////////////////////////////////////////////////////////////////////// - if (not neumannFunction_) - return energy; + ////////////////////////////////////////////////////////////////////////////// + // Assemble boundary contributions + ////////////////////////////////////////////////////////////////////////////// - for (auto&& it : intersections(neumannBoundary_->gridView(),element) ) - { - if (not neumannBoundary_ or not neumannBoundary_->contains(it)) - continue; + if (not neumannFunction_) + return energy; - const Dune::QuadratureRule<DT, gridDim-1>& quad - = Dune::QuadratureRules<DT, gridDim-1>::rule(it.type(), quadOrder); + for (auto&& it : intersections(neumannBoundary_->gridView(),element) ) + { + if (not neumannBoundary_ or not neumannBoundary_->contains(it)) + continue; - for (size_t pt=0; pt<quad.size(); pt++) { + const Dune::QuadratureRule<DT, gridDim-1>& quad + = Dune::QuadratureRules<DT, gridDim-1>::rule(it.type(), quadOrder); - // Local position of the quadrature point - const Dune::FieldVector<DT,gridDim>& quadPos = it.geometryInInside().global(quad[pt].position()); + for (size_t pt=0; pt<quad.size(); pt++) { - const DT integrationElement = it.geometry().integrationElement(quad[pt].position()); + // Local position of the quadrature point + const Dune::FieldVector<DT,gridDim>& quadPos = it.geometryInInside().global(quad[pt].position()); - // The value of the local function - RigidBodyMotion<field_type,dim> value = localGeodesicFEFunction.evaluate(quadPos); + const DT integrationElement = it.geometry().integrationElement(quad[pt].position()); - // Value of the Neumann data at the current position - auto neumannValue = neumannFunction_(it.geometry().global(quad[pt].position())); + // The value of the local function + RigidBodyMotion<field_type,dim> value = localGeodesicFEFunction.evaluate(quadPos); - // Only translational dofs are affected by the Neumann force - for (size_t i=0; i<neumannValue.size(); i++) - energy += (neumannValue[i] * value.r[i]) * quad[pt].weight() * integrationElement; + // Value of the Neumann data at the current position + auto neumannValue = neumannFunction_(it.geometry().global(quad[pt].position())); - } + // Only translational dofs are affected by the Neumann force + for (size_t i=0; i<neumannValue.size(); i++) + energy += (neumannValue[i] * value.r[i]) * quad[pt].weight() * integrationElement; } - return energy; + } + + return energy; } template <class Basis, int dim, class field_type> @@ -490,155 +490,154 @@ energy(const typename Basis::LocalView& localView, const std::vector<RealTuple<field_type,dim> >& localDeformationConfiguration, const std::vector<Rotation<field_type,dim> >& localOrientationConfiguration) const { - auto element = localView.element(); + auto element = localView.element(); - RT energy = 0; + RT energy = 0; - using namespace Dune::TypeTree::Indices; - const auto& deformationLocalFiniteElement = LocalFiniteElementFactory<Basis,0>::get(localView,_0); - const auto& orientationLocalFiniteElement = LocalFiniteElementFactory<Basis,1>::get(localView,_1); + using namespace Dune::TypeTree::Indices; + const auto& deformationLocalFiniteElement = LocalFiniteElementFactory<Basis,0>::get(localView,_0); + const auto& orientationLocalFiniteElement = LocalFiniteElementFactory<Basis,1>::get(localView,_1); #ifdef PROJECTED_INTERPOLATION - typedef Dune::GFE::LocalProjectedFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RealTuple<field_type,dim> > + typedef Dune::GFE::LocalProjectedFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RealTuple<field_type,dim> > LocalDeformationGFEFunctionType; #else - typedef LocalGeodesicFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RealTuple<field_type,dim> > + typedef LocalGeodesicFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RealTuple<field_type,dim> > LocalDeformationGFEFunctionType; #endif - LocalDeformationGFEFunctionType localDeformationGFEFunction(deformationLocalFiniteElement,localDeformationConfiguration); + LocalDeformationGFEFunctionType localDeformationGFEFunction(deformationLocalFiniteElement,localDeformationConfiguration); #ifdef PROJECTED_INTERPOLATION - typedef Dune::GFE::LocalProjectedFEFunction<gridDim, DT, decltype(orientationLocalFiniteElement), Rotation<field_type,dim> > LocalOrientationGFEFunctionType; + typedef Dune::GFE::LocalProjectedFEFunction<gridDim, DT, decltype(orientationLocalFiniteElement), Rotation<field_type,dim> > LocalOrientationGFEFunctionType; #else - typedef LocalGeodesicFEFunction<gridDim, DT, decltype(orientationLocalFiniteElement), Rotation<field_type,dim> > LocalOrientationGFEFunctionType; + typedef LocalGeodesicFEFunction<gridDim, DT, decltype(orientationLocalFiniteElement), Rotation<field_type,dim> > LocalOrientationGFEFunctionType; #endif - LocalOrientationGFEFunctionType localOrientationGFEFunction(orientationLocalFiniteElement,localOrientationConfiguration); + LocalOrientationGFEFunctionType localOrientationGFEFunction(orientationLocalFiniteElement,localOrientationConfiguration); - // \todo Implement smarter quadrature rule selection for more efficiency, i.e., less evaluations of the Rotation GFE function - int quadOrder = deformationLocalFiniteElement.localBasis().order() * ((element.type().isSimplex()) ? 1 : gridDim); + // \todo Implement smarter quadrature rule selection for more efficiency, i.e., less evaluations of the Rotation GFE function + int quadOrder = deformationLocalFiniteElement.localBasis().order() * ((element.type().isSimplex()) ? 1 : gridDim); - const auto& quad = Dune::QuadratureRules<DT, gridDim>::rule(element.type(), quadOrder); + const auto& 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(); + 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 DT integrationElement = element.geometry().integrationElement(quadPos); - const auto jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos); + const auto jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos); - DT weight = quad[pt].weight() * integrationElement; + DT weight = quad[pt].weight() * integrationElement; - // The value of the local deformation - RealTuple<field_type,dim> deformationValue = localDeformationGFEFunction.evaluate(quadPos); - Rotation<field_type,dim> orientationValue = localOrientationGFEFunction.evaluate(quadPos); + // The value of the local deformation + RealTuple<field_type,dim> deformationValue = localDeformationGFEFunction.evaluate(quadPos); + Rotation<field_type,dim> orientationValue = localOrientationGFEFunction.evaluate(quadPos); - // The derivative of the local function defined on the reference element - typename LocalDeformationGFEFunctionType::DerivativeType deformationReferenceDerivative = localDeformationGFEFunction.evaluateDerivative(quadPos,deformationValue); - typename LocalOrientationGFEFunctionType::DerivativeType orientationReferenceDerivative = localOrientationGFEFunction.evaluateDerivative(quadPos,orientationValue); + // The derivative of the local function defined on the reference element + typename LocalDeformationGFEFunctionType::DerivativeType deformationReferenceDerivative = localDeformationGFEFunction.evaluateDerivative(quadPos,deformationValue); + typename LocalOrientationGFEFunctionType::DerivativeType orientationReferenceDerivative = localOrientationGFEFunction.evaluateDerivative(quadPos,orientationValue); - // The derivative of the function defined on the actual element - typename LocalDeformationGFEFunctionType::DerivativeType deformationDerivative; - typename LocalOrientationGFEFunctionType::DerivativeType orientationDerivative; + // The derivative of the function defined on the actual element + typename LocalDeformationGFEFunctionType::DerivativeType deformationDerivative; + typename LocalOrientationGFEFunctionType::DerivativeType orientationDerivative; - for (size_t comp=0; comp<deformationReferenceDerivative.N(); comp++) - jacobianInverseTransposed.mv(deformationReferenceDerivative[comp], deformationDerivative[comp]); + for (size_t comp=0; comp<deformationReferenceDerivative.N(); comp++) + jacobianInverseTransposed.mv(deformationReferenceDerivative[comp], deformationDerivative[comp]); - for (size_t comp=0; comp<orientationReferenceDerivative.N(); comp++) - jacobianInverseTransposed.mv(orientationReferenceDerivative[comp], orientationDerivative[comp]); + for (size_t comp=0; comp<orientationReferenceDerivative.N(); comp++) + jacobianInverseTransposed.mv(orientationReferenceDerivative[comp], orientationDerivative[comp]); - ///////////////////////////////////////////////////////// - // compute U, the Cosserat strain - ///////////////////////////////////////////////////////// - static_assert(dim>=gridDim, "Codim of the grid must be nonnegative"); + ///////////////////////////////////////////////////////// + // compute U, the Cosserat strain + ///////////////////////////////////////////////////////// + static_assert(dim>=gridDim, "Codim of the grid must be nonnegative"); - // - Dune::FieldMatrix<field_type,dim,dim> R; - orientationValue.matrix(R); + // + Dune::FieldMatrix<field_type,dim,dim> R; + orientationValue.matrix(R); - Dune::GFE::CosseratStrain<field_type,dim,gridDim> U(deformationDerivative,R); + Dune::GFE::CosseratStrain<field_type,dim,gridDim> U(deformationDerivative,R); - ////////////////////////////////////////////////////////// - // Compute the derivative of the rotation - // Note: we need it in matrix coordinates - ////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////// + // Compute the derivative of the rotation + // Note: we need it in matrix coordinates + ////////////////////////////////////////////////////////// - Tensor3<field_type,3,3,gridDim> DR = orientationValue.quaternionTangentToMatrixTangent(orientationDerivative); + Tensor3<field_type,3,3,gridDim> DR = orientationValue.quaternionTangentToMatrixTangent(orientationDerivative); - // Add the local energy density - if (gridDim==2) { + // Add the local energy density + if (gridDim==2) { #ifdef QUADRATIC_MEMBRANE_ENERGY - //energy += weight * thickness_ * quadraticMembraneEnergy(U.matrix()); - energy += weight * thickness_ * longQuadraticMembraneEnergy(U); + //energy += weight * thickness_ * quadraticMembraneEnergy(U.matrix()); + energy += weight * thickness_ * longQuadraticMembraneEnergy(U); #else - energy += weight * thickness_ * nonquadraticMembraneEnergy(U); + energy += weight * thickness_ * nonquadraticMembraneEnergy(U); #endif - energy += weight * thickness_ * curvatureEnergy(DR); - energy += weight * std::pow(thickness_,3) / 12.0 * bendingEnergy(R,DR); - } else if (gridDim==3) { - energy += weight * quadraticMembraneEnergy(U); + energy += weight * thickness_ * curvatureEnergy(DR); + energy += weight * std::pow(thickness_,3) / 12.0 * bendingEnergy(R,DR); + } else if (gridDim==3) { + energy += weight * quadraticMembraneEnergy(U); #ifdef CURVATURE_WITH_WRYNESS - energy += weight * curvatureWithWryness(R,DR); + energy += weight * curvatureWithWryness(R,DR); #else - energy += weight * curvatureEnergy(DR); + energy += weight * curvatureEnergy(DR); #endif - } else - DUNE_THROW(Dune::NotImplemented, "CosseratEnergyStiffness for 1d grids"); - - /////////////////////////////////////////////////////////// - // Volume load contribution - /////////////////////////////////////////////////////////// - if (not volumeLoad_) - continue; + } else + DUNE_THROW(Dune::NotImplemented, "CosseratEnergyStiffness for 1d grids"); - // Value of the volume load density at the current position - auto volumeLoadDensity = volumeLoad_(element.geometry().global(quad[pt].position())); + /////////////////////////////////////////////////////////// + // Volume load contribution + /////////////////////////////////////////////////////////// + if (not volumeLoad_) + continue; + // Value of the volume load density at the current position + auto volumeLoadDensity = volumeLoad_(element.geometry().global(quad[pt].position())); - // Only translational dofs are affected by the volume load - for (size_t i=0; i<volumeLoadDensity.size(); i++) - energy += (volumeLoadDensity[i] * deformationValue[i]) * quad[pt].weight() * integrationElement; - } + // Only translational dofs are affected by the volume load + for (size_t i=0; i<volumeLoadDensity.size(); i++) + energy += (volumeLoadDensity[i] * deformationValue[i]) * quad[pt].weight() * integrationElement; - ////////////////////////////////////////////////////////////////////////////// - // Assemble boundary contributions - ////////////////////////////////////////////////////////////////////////////// + } - if (not neumannFunction_) - return energy; + ////////////////////////////////////////////////////////////////////////////// + // Assemble boundary contributions + ////////////////////////////////////////////////////////////////////////////// - for (auto&& it : intersections(neumannBoundary_->gridView(),element) ) - { - if (not neumannBoundary_ or not neumannBoundary_->contains(it)) - continue; + if (not neumannFunction_) + return energy; - const auto& quad = Dune::QuadratureRules<DT, gridDim-1>::rule(it.type(), quadOrder); + for (auto&& it : intersections(neumannBoundary_->gridView(),element) ) + { + if (not neumannBoundary_ or not neumannBoundary_->contains(it)) + continue; - for (size_t pt=0; pt<quad.size(); pt++) { + const auto& quad = Dune::QuadratureRules<DT, gridDim-1>::rule(it.type(), quadOrder); - // Local position of the quadrature point - const Dune::FieldVector<DT,gridDim>& quadPos = it.geometryInInside().global(quad[pt].position()); + for (size_t pt=0; pt<quad.size(); pt++) { - const DT integrationElement = it.geometry().integrationElement(quad[pt].position()); + // Local position of the quadrature point + const Dune::FieldVector<DT,gridDim>& quadPos = it.geometryInInside().global(quad[pt].position()); - // The value of the local function - RealTuple<field_type,dim> deformationValue = localDeformationGFEFunction.evaluate(quadPos); + const DT integrationElement = it.geometry().integrationElement(quad[pt].position()); - // Value of the Neumann data at the current position - auto neumannValue = neumannFunction_(it.geometry().global(quad[pt].position())); + // The value of the local function + RealTuple<field_type,dim> deformationValue = localDeformationGFEFunction.evaluate(quadPos); - // Only translational dofs are affected by the Neumann force - for (size_t i=0; i<neumannValue.size(); i++) - energy += (neumannValue[i] * deformationValue.globalCoordinates()[i]) * quad[pt].weight() * integrationElement; + // Value of the Neumann data at the current position + auto neumannValue = neumannFunction_(it.geometry().global(quad[pt].position())); - } + // Only translational dofs are affected by the Neumann force + for (size_t i=0; i<neumannValue.size(); i++) + energy += (neumannValue[i] * deformationValue.globalCoordinates()[i]) * quad[pt].weight() * integrationElement; } - return energy; + } + + return energy; } #endif //#ifndef COSSERAT_ENERGY_LOCAL_STIFFNESS_HH - diff --git a/dune/gfe/assemblers/cosseratrodenergy.hh b/dune/gfe/assemblers/cosseratrodenergy.hh index 11707523..b2c6dced 100644 --- a/dune/gfe/assemblers/cosseratrodenergy.hh +++ b/dune/gfe/assemblers/cosseratrodenergy.hh @@ -21,10 +21,10 @@ namespace Dune::GFE { -template<class Basis, class LocalInterpolationRule, class RT> -class CosseratRodEnergy -: public LocalEnergy<Basis, RigidBodyMotion<RT,3> > -{ + template<class Basis, class LocalInterpolationRule, class RT> + class CosseratRodEnergy + : public LocalEnergy<Basis, RigidBodyMotion<RT,3> > + { typedef RigidBodyMotion<RT,3> TargetSpace; // grid types @@ -41,18 +41,18 @@ class CosseratRodEnergy // Quadrature order used for the bending and torsion energy constexpr static int bendingQuadOrder = 2; -public: + public: /** \brief The stress-free configuration - The number type cannot be RT, because RT become `adouble` when - using RodLocalStiffness together with an AD system. - The referenceConfiguration is not a variable, and we don't - want to use `adouble` for it. + The number type cannot be RT, because RT become `adouble` when + using RodLocalStiffness together with an AD system. + The referenceConfiguration is not a variable, and we don't + want to use `adouble` for it. */ std::vector<RigidBodyMotion<double,3> > referenceConfiguration_; -public: + public: //! Each block is x, y, theta in 2d, T (R^3 \times SO(3)) in 3d static constexpr auto blocksize = TargetSpace::EmbeddedTangentVector::dimension; @@ -70,9 +70,9 @@ public: //! Constructor CosseratRodEnergy(const GridView& gridView, const std::array<double,3>& K, const std::array<double,3>& A) - : K_(K), - A_(A), - gridView_(gridView) + : K_(K), + A_(A), + gridView_(gridView) {} /** \brief Constructor setting shape constants and material parameters @@ -80,27 +80,27 @@ public: \param J1, J2 The geometric moments (Flächenträgheitsmomente) \param E Young's modulus \param nu Poisson number - */ + */ CosseratRodEnergy(const GridView& gridView, double A, double J1, double J2, double E, double nu) - : gridView_(gridView) + : gridView_(gridView) { - // shear modulus - double G = E/(2+2*nu); + // shear modulus + double G = E/(2+2*nu); - K_[0] = E * J1; - K_[1] = E * J2; - K_[2] = G * (J1 + J2); + K_[0] = E * J1; + K_[1] = E * J2; + K_[2] = G * (J1 + J2); - A_[0] = G * A; - A_[1] = G * A; - A_[2] = E * A; + A_[0] = G * A; + A_[1] = G * A; + A_[2] = E * A; } /** \brief Set the stress-free configuration */ void setReferenceConfiguration(const std::vector<RigidBodyMotion<double,3> >& referenceConfiguration) { - referenceConfiguration_ = referenceConfiguration; + referenceConfiguration_ = referenceConfiguration; } /** \brief Compute local element energy */ @@ -113,8 +113,8 @@ public: */ template<class ReboundLocalInterpolationRule> auto getStrain(const ReboundLocalInterpolationRule& localSolution, - const Entity& element, - const FieldVector<double,1>& pos) const; + const Entity& element, + const FieldVector<double,1>& pos) const; /** \brief Get the rod stress at one point in the rod * @@ -122,8 +122,8 @@ public: */ template<class Number> auto getStress(const std::vector<RigidBodyMotion<Number,3> >& localSolution, - const Entity& element, - const FieldVector<double,1>& pos) const; + const Entity& element, + const FieldVector<double,1>& pos) const; /** \brief Get average strain for each element */ void getStrain(const std::vector<RigidBodyMotion<double,3> >& sol, @@ -135,43 +135,43 @@ public: /** \brief Return resultant force across boundary in canonical coordinates - \note Linear run-time in the size of the grid */ + \note Linear run-time in the size of the grid */ template <class PatchGridView> auto getResultantForce(const BoundaryPatch<PatchGridView>& boundary, - const std::vector<RigidBodyMotion<double,3> >& sol) const; + const std::vector<RigidBodyMotion<double,3> >& sol) const; -protected: + protected: std::vector<RigidBodyMotion<double,3> > getLocalReferenceConfiguration(const typename Basis::LocalView& localView) const { - unsigned int numOfBaseFct = localView.size(); - std::vector<RigidBodyMotion<double,3> > localReferenceConfiguration(numOfBaseFct); + unsigned int numOfBaseFct = localView.size(); + std::vector<RigidBodyMotion<double,3> > localReferenceConfiguration(numOfBaseFct); - for (size_t i=0; i<numOfBaseFct; i++) - localReferenceConfiguration[i] = referenceConfiguration_[localView.index(i)]; + for (size_t i=0; i<numOfBaseFct; i++) + localReferenceConfiguration[i] = referenceConfiguration_[localView.index(i)]; - return localReferenceConfiguration; + return localReferenceConfiguration; } - template <class T> + template <class T> static FieldVector<T,3> darboux(const Rotation<T,3>& q, const FieldVector<T,4>& q_s) { - FieldVector<T,3> u; // The Darboux vector + FieldVector<T,3> u; // The Darboux vector - u[0] = 2 * (q.B(0) * q_s); - u[1] = 2 * (q.B(1) * q_s); - u[2] = 2 * (q.B(2) * q_s); + u[0] = 2 * (q.B(0) * q_s); + u[1] = 2 * (q.B(1) * q_s); + u[2] = 2 * (q.B(2) * q_s); - return u; + return u; } -}; + }; -template<class Basis, class LocalInterpolationRule, class RT> -RT CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: -energy(const typename Basis::LocalView& localView, - const std::vector<RigidBodyMotion<RT,3> >& localCoefficients) const -{ + template<class Basis, class LocalInterpolationRule, class RT> + RT CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: + energy(const typename Basis::LocalView& localView, + const std::vector<RigidBodyMotion<RT,3> >& localCoefficients) const + { const auto& localFiniteElement = localView.tree().finiteElement(); LocalInterpolationRule localConfiguration(localFiniteElement, localCoefficients); @@ -193,20 +193,20 @@ energy(const typename Basis::LocalView& localView, for (size_t pt=0; pt<shearingQuad.size(); pt++) { - // Local position of the quadrature point - const auto quadPos = shearingQuad[pt].position(); + // Local position of the quadrature point + const auto quadPos = shearingQuad[pt].position(); - const double integrationElement = element.geometry().integrationElement(quadPos); + const double integrationElement = element.geometry().integrationElement(quadPos); - double weight = shearingQuad[pt].weight() * integrationElement; + double weight = shearingQuad[pt].weight() * integrationElement; - auto strain = getStrain(localConfiguration, element, quadPos); + auto strain = getStrain(localConfiguration, element, quadPos); - // The reference strain - auto referenceStrain = getStrain(localReferenceConfiguration, element, quadPos); + // The reference strain + auto referenceStrain = getStrain(localReferenceConfiguration, element, quadPos); - for (int i=0; i<3; i++) - energy += weight * 0.5 * A_[i] * (strain[i] - referenceStrain[i]) * (strain[i] - referenceStrain[i]); + for (int i=0; i<3; i++) + energy += weight * 0.5 * A_[i] * (strain[i] - referenceStrain[i]) * (strain[i] - referenceStrain[i]); } @@ -215,33 +215,33 @@ energy(const typename Basis::LocalView& localView, for (size_t pt=0; pt<bendingQuad.size(); pt++) { - // Local position of the quadrature point - const FieldVector<double,1>& quadPos = bendingQuad[pt].position(); + // Local position of the quadrature point + const FieldVector<double,1>& quadPos = bendingQuad[pt].position(); - double weight = bendingQuad[pt].weight() * element.geometry().integrationElement(quadPos); + double weight = bendingQuad[pt].weight() * element.geometry().integrationElement(quadPos); - auto strain = getStrain(localConfiguration, element, quadPos); + auto strain = getStrain(localConfiguration, element, quadPos); - // The reference strain - auto referenceStrain = getStrain(localReferenceConfiguration, element, quadPos); + // The reference strain + auto referenceStrain = getStrain(localReferenceConfiguration, element, quadPos); - // Part II: the bending and twisting energy - for (int i=0; i<3; i++) - energy += weight * 0.5 * K_[i] * (strain[i+3] - referenceStrain[i+3]) * (strain[i+3] - referenceStrain[i+3]); + // Part II: the bending and twisting energy + for (int i=0; i<3; i++) + energy += weight * 0.5 * K_[i] * (strain[i+3] - referenceStrain[i+3]) * (strain[i+3] - referenceStrain[i+3]); } return energy; -} + } -template<class Basis, class LocalInterpolationRule, class RT> -template <class ReboundLocalInterpolationRule> -auto CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: -getStrain(const ReboundLocalInterpolationRule& localInterpolation, - const Entity& element, - const FieldVector<double,1>& pos) const -{ + template<class Basis, class LocalInterpolationRule, class RT> + template <class ReboundLocalInterpolationRule> + auto CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: + getStrain(const ReboundLocalInterpolationRule& localInterpolation, + const Entity& element, + const FieldVector<double,1>& pos) const + { const auto jit = element.geometry().jacobianInverseTransposed(pos); auto value = localInterpolation.evaluate(pos); @@ -282,15 +282,15 @@ getStrain(const ReboundLocalInterpolationRule& localInterpolation, strain[5] = u[2]; return strain; -} - -template<class Basis, class LocalInterpolationRule, class RT> -template <class Number> -auto CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: -getStress(const std::vector<RigidBodyMotion<Number,3> >& localSolution, - const Entity& element, - const FieldVector<double, 1>& pos) const -{ + } + + template<class Basis, class LocalInterpolationRule, class RT> + template <class Number> + auto CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: + getStress(const std::vector<RigidBodyMotion<Number,3> >& localSolution, + const Entity& element, + const FieldVector<double, 1>& pos) const + { const auto& indexSet = gridView_.indexSet(); std::vector<TargetSpace> localRefConf = {referenceConfiguration_[indexSet.subIndex(element, 0, 1)], referenceConfiguration_[indexSet.subIndex(element, 1, 1)]}; @@ -300,22 +300,22 @@ getStress(const std::vector<RigidBodyMotion<Number,3> >& localSolution, FieldVector<RT, 6> stress; for (int i=0; i < dim; i++) - stress[i] = (strain[i] - referenceStrain[i]) * A_[i]; + stress[i] = (strain[i] - referenceStrain[i]) * A_[i]; for (int i=0; i < dim; i++) - stress[i+3] = (strain[i+3] - referenceStrain[i+3]) * K_[i]; + stress[i+3] = (strain[i+3] - referenceStrain[i+3]) * K_[i]; return stress; -} + } -template<class Basis, class LocalInterpolationRule, class RT> -void CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: -getStrain(const std::vector<RigidBodyMotion<double,3> >& sol, - BlockVector<FieldVector<double, blocksize> >& strain) const -{ + template<class Basis, class LocalInterpolationRule, class RT> + void CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: + getStrain(const std::vector<RigidBodyMotion<double,3> >& sol, + BlockVector<FieldVector<double, blocksize> >& strain) const + { const typename GridView::Traits::IndexSet& indexSet = this->basis_.gridView().indexSet(); if (sol.size()!=this->basis_.size()) - DUNE_THROW(Exception, "Solution vector doesn't match the grid!"); + DUNE_THROW(Exception, "Solution vector doesn't match the grid!"); // Strain defined on each element strain.resize(indexSet.size(0)); @@ -324,49 +324,49 @@ getStrain(const std::vector<RigidBodyMotion<double,3> >& sol, // Loop over all elements for (const auto& element : elements(this->basis_.gridView())) { - int elementIdx = indexSet.index(element); + int elementIdx = indexSet.index(element); - // Extract local solution on this element - Dune::LagrangeSimplexLocalFiniteElement<double, double, 1, 1> localFiniteElement; - int numOfBaseFct = localFiniteElement.localCoefficients().size(); + // Extract local solution on this element + Dune::LagrangeSimplexLocalFiniteElement<double, double, 1, 1> localFiniteElement; + int numOfBaseFct = localFiniteElement.localCoefficients().size(); - std::vector<RigidBodyMotion<double,3> > localSolution(2); + std::vector<RigidBodyMotion<double,3> > localSolution(2); - for (int i=0; i<numOfBaseFct; i++) - localSolution[i] = sol[indexSet.subIndex(element,i,1)]; + for (int i=0; i<numOfBaseFct; i++) + localSolution[i] = sol[indexSet.subIndex(element,i,1)]; - // Get quadrature rule - const int polOrd = 2; - const auto& quad = QuadratureRules<double, 1>::rule(element.type(), polOrd); + // Get quadrature rule + const int polOrd = 2; + const auto& quad = QuadratureRules<double, 1>::rule(element.type(), polOrd); - for (std::size_t pt=0; pt<quad.size(); pt++) - { - // Local position of the quadrature point - const auto quadPos = quad[pt].position(); + for (std::size_t pt=0; pt<quad.size(); pt++) + { + // Local position of the quadrature point + const auto quadPos = quad[pt].position(); - double weight = quad[pt].weight() * element.geometry().integrationElement(quadPos); + double weight = quad[pt].weight() * element.geometry().integrationElement(quadPos); - auto localStrain = getStrain(localSolution, element, quad[pt].position()); + auto localStrain = getStrain(localSolution, element, quad[pt].position()); - // Sum it all up - strain[elementIdx].axpy(weight, localStrain); - } + // Sum it all up + strain[elementIdx].axpy(weight, localStrain); + } - // ///////////////////////////////////////////////////////////////////////// - // We want the average strain per element. Therefore we have to divide - // the integral we just computed by the element volume. - // ///////////////////////////////////////////////////////////////////////// - // we know the element is a line, therefore the integration element is the volume - FieldVector<double,1> dummyPos(0.5); - strain[elementIdx] /= element.geometry().integrationElement(dummyPos); + // ///////////////////////////////////////////////////////////////////////// + // We want the average strain per element. Therefore we have to divide + // the integral we just computed by the element volume. + // ///////////////////////////////////////////////////////////////////////// + // we know the element is a line, therefore the integration element is the volume + FieldVector<double,1> dummyPos(0.5); + strain[elementIdx] /= element.geometry().integrationElement(dummyPos); } -} + } -template<class Basis, class LocalInterpolationRule, class RT> -void CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: -getStress(const std::vector<RigidBodyMotion<double,3> >& sol, - BlockVector<FieldVector<double, blocksize> >& stress) const -{ + template<class Basis, class LocalInterpolationRule, class RT> + void CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: + getStress(const std::vector<RigidBodyMotion<double,3> >& sol, + BlockVector<FieldVector<double, blocksize> >& stress) const + { // Get the strain getStrain(sol,stress); @@ -377,24 +377,24 @@ getStress(const std::vector<RigidBodyMotion<double,3> >& sol, // Linear diagonal constitutive law for (size_t i=0; i<stress.size(); i++) { - for (int j=0; j<3; j++) - { - stress[i][j] = (stress[i][j] - referenceStrain[i][j]) * A_[j]; - stress[i][j+3] = (stress[i][j+3] - referenceStrain[i][j+3]) * K_[j]; - } + for (int j=0; j<3; j++) + { + stress[i][j] = (stress[i][j] - referenceStrain[i][j]) * A_[j]; + stress[i][j+3] = (stress[i][j+3] - referenceStrain[i][j+3]) * K_[j]; + } } -} - -template<class Basis, class LocalInterpolationRule, class RT> -template <class PatchGridView> -auto CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: -getResultantForce(const BoundaryPatch<PatchGridView>& boundary, - const std::vector<RigidBodyMotion<double,3> >& sol) const -{ + } + + template<class Basis, class LocalInterpolationRule, class RT> + template <class PatchGridView> + auto CosseratRodEnergy<Basis, LocalInterpolationRule, RT>:: + getResultantForce(const BoundaryPatch<PatchGridView>& boundary, + const std::vector<RigidBodyMotion<double,3> >& sol) const + { const typename GridView::Traits::IndexSet& indexSet = this->basis_.gridView().indexSet(); if (sol.size()!=indexSet.size(1)) - DUNE_THROW(Exception, "Solution vector doesn't match the grid!"); + DUNE_THROW(Exception, "Solution vector doesn't match the grid!"); FieldVector<double,3> canonicalStress(0); FieldVector<double,3> canonicalTorque(0); @@ -402,57 +402,56 @@ getResultantForce(const BoundaryPatch<PatchGridView>& boundary, // Loop over the given boundary for (auto facet : boundary) { - // ////////////////////////////////////////////// - // Compute force across this boundary face - // ////////////////////////////////////////////// + // ////////////////////////////////////////////// + // Compute force across this boundary face + // ////////////////////////////////////////////// - double pos = facet.geometryInInside().corner(0); + double pos = facet.geometryInInside().corner(0); - std::vector<RigidBodyMotion<double,3> > localSolution(2); - localSolution[0] = sol[indexSet.subIndex(*facet.inside(),0,1)]; - localSolution[1] = sol[indexSet.subIndex(*facet.inside(),1,1)]; + std::vector<RigidBodyMotion<double,3> > localSolution(2); + localSolution[0] = sol[indexSet.subIndex(*facet.inside(),0,1)]; + localSolution[1] = sol[indexSet.subIndex(*facet.inside(),1,1)]; - std::vector<RigidBodyMotion<double,3> > localRefConf(2); - localRefConf[0] = referenceConfiguration_[indexSet.subIndex(*facet.inside(),0,1)]; - localRefConf[1] = referenceConfiguration_[indexSet.subIndex(*facet.inside(),1,1)]; + std::vector<RigidBodyMotion<double,3> > localRefConf(2); + localRefConf[0] = referenceConfiguration_[indexSet.subIndex(*facet.inside(),0,1)]; + localRefConf[1] = referenceConfiguration_[indexSet.subIndex(*facet.inside(),1,1)]; - auto strain = getStrain(localSolution, *facet.inside(), pos); - auto referenceStrain = getStrain(localRefConf, *facet.inside(), pos); + auto strain = getStrain(localSolution, *facet.inside(), pos); + auto referenceStrain = getStrain(localRefConf, *facet.inside(), pos); - FieldVector<double,3> localStress; - for (int i=0; i<3; i++) - localStress[i] = (strain[i] - referenceStrain[i]) * A_[i]; + FieldVector<double,3> localStress; + for (int i=0; i<3; i++) + localStress[i] = (strain[i] - referenceStrain[i]) * A_[i]; - FieldVector<double,3> localTorque; - for (int i=0; i<3; i++) - localTorque[i] = (strain[i+3] - referenceStrain[i+3]) * K_[i]; + FieldVector<double,3> localTorque; + for (int i=0; i<3; i++) + localTorque[i] = (strain[i+3] - referenceStrain[i+3]) * K_[i]; - // Transform stress given with respect to the basis given by the three directors to - // the canonical basis of R^3 + // Transform stress given with respect to the basis given by the three directors to + // the canonical basis of R^3 - FieldMatrix<double,3,3> orientationMatrix; - sol[indexSet.subIndex(*facet.inside(),facet.indexInInside(),1)].q.matrix(orientationMatrix); + FieldMatrix<double,3,3> orientationMatrix; + sol[indexSet.subIndex(*facet.inside(),facet.indexInInside(),1)].q.matrix(orientationMatrix); - orientationMatrix.umv(localStress, canonicalStress); + orientationMatrix.umv(localStress, canonicalStress); - orientationMatrix.umv(localTorque, canonicalTorque); + orientationMatrix.umv(localTorque, canonicalTorque); - // Multiply force times boundary normal to get the transmitted force - canonicalStress *= facet.unitOuterNormal(FieldVector<double,0>(0))[0]; - canonicalTorque *= facet.unitOuterNormal(FieldVector<double,0>(0))[0]; + // Multiply force times boundary normal to get the transmitted force + canonicalStress *= facet.unitOuterNormal(FieldVector<double,0>(0))[0]; + canonicalTorque *= facet.unitOuterNormal(FieldVector<double,0>(0))[0]; } FieldVector<double,6> result; for (int i=0; i<3; i++) { - result[i] = canonicalStress[i]; - result[i+3] = canonicalTorque[i]; + result[i] = canonicalStress[i]; + result[i+3] = canonicalTorque[i]; } return result; -} + } } // namespace Dune::GFE #endif - diff --git a/dune/gfe/assemblers/geodesicfeassembler.hh b/dune/gfe/assemblers/geodesicfeassembler.hh index 01d0d279..f75264cf 100644 --- a/dune/gfe/assemblers/geodesicfeassembler.hh +++ b/dune/gfe/assemblers/geodesicfeassembler.hh @@ -15,83 +15,83 @@ template <class Basis, class TargetSpace> class GeodesicFEAssembler { - using field_type = typename TargetSpace::field_type; - typedef typename Basis::GridView GridView; - using LocalStiffness = LocalGeodesicFEStiffness<Basis, TargetSpace>; + using field_type = typename TargetSpace::field_type; + typedef typename Basis::GridView GridView; + using LocalStiffness = LocalGeodesicFEStiffness<Basis, TargetSpace>; - //! Dimension of the grid. - constexpr static int gridDim = GridView::dimension; + //! Dimension of the grid. + constexpr static int gridDim = GridView::dimension; - //! Dimension of a tangent space - constexpr static int blocksize = TargetSpace::TangentVector::dimension; + //! Dimension of a tangent space + constexpr static int blocksize = TargetSpace::TangentVector::dimension; - //! - typedef Dune::FieldMatrix<double, blocksize, blocksize> MatrixBlock; + //! + typedef Dune::FieldMatrix<double, blocksize, blocksize> MatrixBlock; protected: - //! The global basis - const Basis basis_; + //! The global basis + const Basis basis_; - //! The local stiffness operator - std::shared_ptr<LocalStiffness> localStiffness_; + //! The local stiffness operator + std::shared_ptr<LocalStiffness> localStiffness_; public: - /** \brief Constructor for a given grid */ - GeodesicFEAssembler(const Basis& basis) - : basis_(basis) - {} - - /** \brief Constructor for a given grid */ - template <class LocalStiffnessT> - GeodesicFEAssembler(const Basis& basis, - LocalStiffnessT&& localStiffness) - : basis_(basis), - localStiffness_(Dune::Solvers::wrap_own_share<LocalStiffness>(std::forward<LocalStiffnessT>(localStiffness))) - {} - - /** \brief Set the local stiffness assembler. This can be a temporary, l-value or shared pointer. */ - template <class LocalStiffnessT> - void setLocalStiffness(LocalStiffnessT&& localStiffness) { - localStiffness_ = Dune::Solvers::wrap_own_share<LocalStiffness>(std::forward<LocalStiffnessT>(localStiffness)); - } - - /** \brief Get the local stiffness operator. */ - const LocalStiffness& getLocalStiffness() const { - return *localStiffness_; - } - - /** \brief Get the local stiffness operator. */ - LocalStiffness& getLocalStiffness() { - return *localStiffness_; - } - - /** \brief Get the basis. */ - const Basis& getBasis() const { - return basis_; - } - - /** \brief Assemble the tangent stiffness matrix and the functional gradient together - * - * This is more efficient than computing them separately, because you need the gradient - * anyway to compute the Riemannian Hessian. - */ - virtual void assembleGradientAndHessian(const std::vector<TargetSpace>& sol, - Dune::BlockVector<Dune::FieldVector<field_type, blocksize> >& gradient, - Dune::BCRSMatrix<MatrixBlock>& hessian, - bool computeOccupationPattern=true) const; - - /** \brief Assemble the gradient */ - virtual void assembleGradient(const std::vector<TargetSpace>& sol, - Dune::BlockVector<Dune::FieldVector<double, blocksize> >& grad) const; - - /** \brief Compute the energy of a deformation state */ - virtual double computeEnergy(const std::vector<TargetSpace>& sol) const; - - //protected: - void getNeighborsPerVertex(Dune::MatrixIndexSet& nb) const; + /** \brief Constructor for a given grid */ + GeodesicFEAssembler(const Basis& basis) + : basis_(basis) + {} + + /** \brief Constructor for a given grid */ + template <class LocalStiffnessT> + GeodesicFEAssembler(const Basis& basis, + LocalStiffnessT&& localStiffness) + : basis_(basis), + localStiffness_(Dune::Solvers::wrap_own_share<LocalStiffness>(std::forward<LocalStiffnessT>(localStiffness))) + {} + + /** \brief Set the local stiffness assembler. This can be a temporary, l-value or shared pointer. */ + template <class LocalStiffnessT> + void setLocalStiffness(LocalStiffnessT&& localStiffness) { + localStiffness_ = Dune::Solvers::wrap_own_share<LocalStiffness>(std::forward<LocalStiffnessT>(localStiffness)); + } + + /** \brief Get the local stiffness operator. */ + const LocalStiffness& getLocalStiffness() const { + return *localStiffness_; + } + + /** \brief Get the local stiffness operator. */ + LocalStiffness& getLocalStiffness() { + return *localStiffness_; + } + + /** \brief Get the basis. */ + const Basis& getBasis() const { + return basis_; + } + + /** \brief Assemble the tangent stiffness matrix and the functional gradient together + * + * This is more efficient than computing them separately, because you need the gradient + * anyway to compute the Riemannian Hessian. + */ + virtual void assembleGradientAndHessian(const std::vector<TargetSpace>& sol, + Dune::BlockVector<Dune::FieldVector<field_type, blocksize> >& gradient, + Dune::BCRSMatrix<MatrixBlock>& hessian, + bool computeOccupationPattern=true) const; + + /** \brief Assemble the gradient */ + virtual void assembleGradient(const std::vector<TargetSpace>& sol, + Dune::BlockVector<Dune::FieldVector<double, blocksize> >& grad) const; + + /** \brief Compute the energy of a deformation state */ + virtual double computeEnergy(const std::vector<TargetSpace>& sol) const; + + //protected: + void getNeighborsPerVertex(Dune::MatrixIndexSet& nb) const; }; // end class @@ -101,35 +101,35 @@ template <class Basis, class TargetSpace> void GeodesicFEAssembler<Basis,TargetSpace>:: getNeighborsPerVertex(Dune::MatrixIndexSet& nb) const { - auto n = basis_.size(); - - nb.resize(n, n); + auto n = basis_.size(); - // A view on the FE basis on a single element - auto localView = basis_.localView(); + nb.resize(n, n); - for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) - { - // Bind the local FE basis view to the current element - localView.bind(element); + // A view on the FE basis on a single element + auto localView = basis_.localView(); - const auto& lfe = localView.tree().finiteElement(); + for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) + { + // Bind the local FE basis view to the current element + localView.bind(element); - for (size_t i=0; i<lfe.size(); i++) { + const auto& lfe = localView.tree().finiteElement(); - for (size_t j=0; j<lfe.size(); j++) { + for (size_t i=0; i<lfe.size(); i++) { - auto iIdx = localView.index(i); - auto jIdx = localView.index(j); + for (size_t j=0; j<lfe.size(); j++) { - nb.add(iIdx, jIdx); + auto iIdx = localView.index(i); + auto jIdx = localView.index(j); - } + nb.add(iIdx, jIdx); - } + } } + } + } template <class Basis, class TargetSpace> @@ -139,57 +139,57 @@ assembleGradientAndHessian(const std::vector<TargetSpace>& sol, Dune::BCRSMatrix<MatrixBlock>& hessian, bool computeOccupationPattern) const { - if (computeOccupationPattern) { + if (computeOccupationPattern) { - Dune::MatrixIndexSet neighborsPerVertex; - getNeighborsPerVertex(neighborsPerVertex); - neighborsPerVertex.exportIdx(hessian); + Dune::MatrixIndexSet neighborsPerVertex; + getNeighborsPerVertex(neighborsPerVertex); + neighborsPerVertex.exportIdx(hessian); - } + } - hessian = 0; + hessian = 0; - gradient.resize(sol.size()); - gradient = 0; + gradient.resize(sol.size()); + gradient = 0; - // A view on the FE basis on a single element - auto localView = basis_.localView(); + // A view on the FE basis on a single element + auto localView = basis_.localView(); - for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) - { - localView.bind(element); + for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) + { + localView.bind(element); - const int numOfBaseFct = localView.tree().size(); + const int numOfBaseFct = localView.tree().size(); - // Extract local solution - std::vector<TargetSpace> localSolution(numOfBaseFct); + // Extract local solution + std::vector<TargetSpace> localSolution(numOfBaseFct); - for (int i=0; i<numOfBaseFct; i++) - localSolution[i] = sol[localView.index(i)]; + for (int i=0; i<numOfBaseFct; i++) + localSolution[i] = sol[localView.index(i)]; - std::vector<Dune::FieldVector<double,blocksize> > localGradient(numOfBaseFct); + std::vector<Dune::FieldVector<double,blocksize> > localGradient(numOfBaseFct); - // setup local matrix and gradient - localStiffness_->assembleGradientAndHessian(localView, localSolution, localGradient); + // setup local matrix and gradient + localStiffness_->assembleGradientAndHessian(localView, localSolution, localGradient); - // Add element matrix to global stiffness matrix - for(int i=0; i<numOfBaseFct; i++) { + // Add element matrix to global stiffness matrix + for(int i=0; i<numOfBaseFct; i++) { - auto row = localView.index(i); + auto row = localView.index(i); - for (int j=0; j<numOfBaseFct; j++ ) { + for (int j=0; j<numOfBaseFct; j++ ) { - auto col = localView.index(j); - hessian[row][col] += localStiffness_->A_[i][j]; + auto col = localView.index(j); + hessian[row][col] += localStiffness_->A_[i][j]; - } - } + } + } - // Add local gradient to global gradient - for (int i=0; i<numOfBaseFct; i++) - gradient[localView.index(i)] += localGradient[i]; + // Add local gradient to global gradient + for (int i=0; i<numOfBaseFct; i++) + gradient[localView.index(i)] += localGradient[i]; - } + } } @@ -198,38 +198,38 @@ void GeodesicFEAssembler<Basis,TargetSpace>:: assembleGradient(const std::vector<TargetSpace>& sol, Dune::BlockVector<Dune::FieldVector<double, blocksize> >& grad) const { - if (sol.size()!=basis_.size()) - DUNE_THROW(Dune::Exception, "Solution vector doesn't match the grid!"); + if (sol.size()!=basis_.size()) + DUNE_THROW(Dune::Exception, "Solution vector doesn't match the grid!"); - grad.resize(sol.size()); - grad = 0; + grad.resize(sol.size()); + grad = 0; - // A view on the FE basis on a single element - auto localView = basis_.localView(); + // A view on the FE basis on a single element + auto localView = basis_.localView(); - // Loop over all elements - for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) - { - localView.bind(element); + // Loop over all elements + for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) + { + localView.bind(element); - // The number of degrees of freedom of the current element - const auto nDofs = localView.tree().size(); + // The number of degrees of freedom of the current element + const auto nDofs = localView.tree().size(); - // Extract local solution - std::vector<TargetSpace> localSolution(nDofs); + // Extract local solution + std::vector<TargetSpace> localSolution(nDofs); - for (size_t i=0; i<nDofs; i++) - localSolution[i] = sol[localView.index(i)]; + for (size_t i=0; i<nDofs; i++) + localSolution[i] = sol[localView.index(i)]; - // Assemble local gradient - std::vector<Dune::FieldVector<double,blocksize> > localGradient(nDofs); + // Assemble local gradient + std::vector<Dune::FieldVector<double,blocksize> > localGradient(nDofs); - localStiffness_->assembleGradient(localView, localSolution, localGradient); + localStiffness_->assembleGradient(localView, localSolution, localGradient); - // Add to global gradient - for (size_t i=0; i<nDofs; i++) - grad[localView.index(i)[0]] += localGradient[i]; - } + // Add to global gradient + for (size_t i=0; i<nDofs; i++) + grad[localView.index(i)[0]] += localGradient[i]; + } } @@ -238,36 +238,35 @@ template <class Basis, class TargetSpace> double GeodesicFEAssembler<Basis, TargetSpace>:: computeEnergy(const std::vector<TargetSpace>& sol) const { - double energy = 0; + double energy = 0; - if (sol.size() != basis_.size()) - DUNE_THROW(Dune::Exception, "Coefficient vector doesn't match the function space basis!"); + if (sol.size() != basis_.size()) + DUNE_THROW(Dune::Exception, "Coefficient vector doesn't match the function space basis!"); - // A view on the FE basis on a single element - auto localView = basis_.localView(); + // A view on the FE basis on a single element + auto localView = basis_.localView(); - // Loop over all elements - for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) - { - localView.bind(element); + // Loop over all elements + for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) + { + localView.bind(element); - // Number of degrees of freedom on this element - size_t nDofs = localView.tree().size(); + // Number of degrees of freedom on this element + size_t nDofs = localView.tree().size(); - std::vector<TargetSpace> localSolution(nDofs); + std::vector<TargetSpace> localSolution(nDofs); - for (size_t i=0; i<nDofs; i++) - localSolution[i] = sol[localView.index(i)[0]]; + for (size_t i=0; i<nDofs; i++) + localSolution[i] = sol[localView.index(i)[0]]; - energy += localStiffness_->energy(localView, localSolution); + energy += localStiffness_->energy(localView, localSolution); - } + } - return energy; + return energy; } #endif - diff --git a/dune/gfe/assemblers/geodesicfeassemblerwrapper.hh b/dune/gfe/assemblers/geodesicfeassemblerwrapper.hh index 3294bb50..e6a3fdda 100644 --- a/dune/gfe/assemblers/geodesicfeassemblerwrapper.hh +++ b/dune/gfe/assemblers/geodesicfeassemblerwrapper.hh @@ -8,14 +8,14 @@ namespace Dune::GFE { -/** \brief A wrapper that wraps a MixedGFEAssembler into an assembler that does not distinguish between the two finite element spaces + /** \brief A wrapper that wraps a MixedGFEAssembler into an assembler that does not distinguish between the two finite element spaces - It reimplements the same methods as these two and transfers the structure of the gradient and the Hessian - */ + It reimplements the same methods as these two and transfers the structure of the gradient and the Hessian + */ -template <class Basis, class ScalarBasis, class TargetSpace, class MixedSpace0, class MixedSpace1> -class -GeodesicFEAssemblerWrapper { + template <class Basis, class ScalarBasis, class TargetSpace, class MixedSpace0, class MixedSpace1> + class + GeodesicFEAssemblerWrapper { typedef typename Basis::GridView GridView; @@ -31,21 +31,21 @@ GeodesicFEAssemblerWrapper { typedef Dune::FieldMatrix<double, blocksize, blocksize> MatrixBlock; typedef typename MixedGFEAssembler<Basis, MixedSpace0, MixedSpace1>::MatrixType MatrixType; -protected: + protected: MixedGFEAssembler<Basis, MixedSpace0, MixedSpace1>* mixedAssembler_; -public: + public: const ScalarBasis& basis_; /** \brief Constructor for a given grid */ GeodesicFEAssemblerWrapper(MixedGFEAssembler<Basis, MixedSpace0, MixedSpace1>* mixedAssembler, ScalarBasis& basis) - : mixedAssembler_(mixedAssembler), - basis_(basis) + : mixedAssembler_(mixedAssembler), + basis_(basis) { - hessianMixed_ = std::make_unique<MatrixType>(); - // The two spaces from the mixed version need to have the same embeddedDim as the Target Space - assert(MixedSpace0::embeddedDim + MixedSpace1::embeddedDim == TargetSpace::embeddedDim); - assert(blocksize0 + blocksize1 == blocksize); + hessianMixed_ = std::make_unique<MatrixType>(); + // The two spaces from the mixed version need to have the same embeddedDim as the Target Space + assert(MixedSpace0::embeddedDim + MixedSpace1::embeddedDim == TargetSpace::embeddedDim); + assert(blocksize0 + blocksize1 == blocksize); } /** \brief Assemble the tangent stiffness matrix and the functional gradient together @@ -66,56 +66,56 @@ public: /** \brief Get the basis. */ const ScalarBasis& getBasis() const { - return basis_; + return basis_; } -private: - Dune::TupleVector<std::vector<MixedSpace0>,std::vector<MixedSpace1>> splitVector(const std::vector<TargetSpace>& sol) const; + private: + Dune::TupleVector<std::vector<MixedSpace0>,std::vector<MixedSpace1> > splitVector(const std::vector<TargetSpace>& sol) const; std::unique_ptr<MatrixType> hessianMixed_; -}; // end class + }; // end class } //end namespace template <class Basis, class ScalarBasis, class TargetSpace, class MixedSpace0, class MixedSpace1> -Dune::TupleVector<std::vector<MixedSpace0>,std::vector<MixedSpace1>> Dune::GFE::GeodesicFEAssemblerWrapper<Basis, ScalarBasis, TargetSpace,MixedSpace0,MixedSpace1>:: +Dune::TupleVector<std::vector<MixedSpace0>,std::vector<MixedSpace1> > Dune::GFE::GeodesicFEAssemblerWrapper<Basis, ScalarBasis, TargetSpace,MixedSpace0,MixedSpace1>:: splitVector(const std::vector<TargetSpace>& sol) const { - using namespace Indices; - // Split the solution into the Deformation and the Rotational part - auto n = basis_.size(); - assert (sol.size() == n); - Dune::TupleVector<std::vector<MixedSpace0>,std::vector<MixedSpace1>> solutionSplit; - solutionSplit[_0].resize(n); - solutionSplit[_1].resize(n); - for (std::size_t i = 0; i < n; i++) { - if constexpr(std::is_base_of<TargetSpace,RigidBodyMotion<double, 3>>::value) { - solutionSplit[_0][i] = sol[i].r; // Deformation part - solutionSplit[_1][i] = sol[i].q; // Rotational part - } else if constexpr(std::is_base_of<TargetSpace,ProductManifold<RealTuple<double,3>,UnitVector<double,3>>>::value) { - solutionSplit[_0][i] = sol[i][_0]; // Deformation part - solutionSplit[_1][i] = sol[i][_1]; // Rotational part - } + using namespace Indices; + // Split the solution into the Deformation and the Rotational part + auto n = basis_.size(); + assert (sol.size() == n); + Dune::TupleVector<std::vector<MixedSpace0>,std::vector<MixedSpace1> > solutionSplit; + solutionSplit[_0].resize(n); + solutionSplit[_1].resize(n); + for (std::size_t i = 0; i < n; i++) { + if constexpr(std::is_base_of<TargetSpace,RigidBodyMotion<double, 3> >::value) { + solutionSplit[_0][i] = sol[i].r; // Deformation part + solutionSplit[_1][i] = sol[i].q; // Rotational part + } else if constexpr(std::is_base_of<TargetSpace,ProductManifold<RealTuple<double,3>,UnitVector<double,3> > >::value) { + solutionSplit[_0][i] = sol[i][_0]; // Deformation part + solutionSplit[_1][i] = sol[i][_1]; // Rotational part } - return solutionSplit; + } + return solutionSplit; } template <class Basis, class ScalarBasis, class TargetSpace, class MixedSpace0, class MixedSpace1> void Dune::GFE::GeodesicFEAssemblerWrapper<Basis, ScalarBasis, TargetSpace,MixedSpace0,MixedSpace1>:: getNeighborsPerVertex(Dune::MatrixIndexSet& nb) const { - auto n = basis_.size(); - nb.resize(n, n); - //Retrieve occupation structure for the mixed space and convert it to the non-mixed space - //In the mixed space, each index set is for one part of the composite basis - //So: nb00 is for the displacement part, nb11 is for the rotation part and nb01 and nb10 (where nb01^T = nb10) is for the coupling - Dune::MatrixIndexSet nb00, nb01, nb10, nb11; - mixedAssembler_->getMatrixPattern(nb00, nb01, nb10, nb11); - auto size0 = nb00.rows(); - auto size1 = nb11.rows(); - assert(size0 == size1); - assert(size0 == n); - //After checking if the sizes match, we can just copy over the occupation pattern - //as all occupation patterns work on the same basis combination, so they are all equal - nb = nb00; + auto n = basis_.size(); + nb.resize(n, n); + //Retrieve occupation structure for the mixed space and convert it to the non-mixed space + //In the mixed space, each index set is for one part of the composite basis + //So: nb00 is for the displacement part, nb11 is for the rotation part and nb01 and nb10 (where nb01^T = nb10) is for the coupling + Dune::MatrixIndexSet nb00, nb01, nb10, nb11; + mixedAssembler_->getMatrixPattern(nb00, nb01, nb10, nb11); + auto size0 = nb00.rows(); + auto size1 = nb11.rows(); + assert(size0 == size1); + assert(size0 == n); + //After checking if the sizes match, we can just copy over the occupation pattern + //as all occupation patterns work on the same basis combination, so they are all equal + nb = nb00; } template <class Basis, class ScalarBasis, class TargetSpace, class MixedSpace0, class MixedSpace1> @@ -125,77 +125,77 @@ assembleGradientAndHessian(const std::vector<TargetSpace>& sol, Dune::BCRSMatrix<MatrixBlock>& hessian, bool computeOccupationPattern) const { - using namespace Dune::TypeTree::Indices; - auto n = basis_.size(); - - // Get a split up version of the input - auto solutionSplit = splitVector(sol); - - // Define the matrix and the gradient in the block structure - Dune::BlockVector<Dune::FieldVector<double, blocksize0> > gradient0(n); - Dune::BlockVector<Dune::FieldVector<double, blocksize1> > gradient1(n); - - if (computeOccupationPattern) { - Dune::MatrixIndexSet neighborsPerVertex; - getNeighborsPerVertex(neighborsPerVertex); - neighborsPerVertex.exportIdx((*hessianMixed_)[_0][_0]); - neighborsPerVertex.exportIdx((*hessianMixed_)[_0][_1]); - neighborsPerVertex.exportIdx((*hessianMixed_)[_1][_0]); - neighborsPerVertex.exportIdx((*hessianMixed_)[_1][_1]); - neighborsPerVertex.exportIdx(hessian); - } - - *hessianMixed_ = 0; - mixedAssembler_->assembleGradientAndHessian(solutionSplit[_0], solutionSplit[_1], gradient0, gradient1, *hessianMixed_, false); - - // Transform gradient and hessian back to the non-mixed structure - hessian = 0; - gradient.resize(n); - gradient = 0; - for (std::size_t i = 0; i < n; i++) { - for (int j = 0; j < blocksize0 + blocksize1; j++) - gradient[i][j] = j < blocksize0 ? gradient0[i][j] : gradient1[i][j - blocksize0]; - } - - // All 4 matrices are nxn; - // Each FieldMatrix in (*hessianMixed_)[_0][_0] is blocksize0 x blocksize0 - // Each FieldMatrix in (*hessianMixed_)[_1][_0] is blocksize1 x blocksize0 - // Each FieldMatrix in (*hessianMixed_)[_0][_1] is blocksize0 x blocksize1 - // Each FieldMatrix in (*hessianMixed_)[_1][_1] is blocksize1 x blocksize1 - // The hessian will then be a nxn matrix where each FieldMatrix is (blocksize0+blocksize1)x(blocksize0+blocksize1) - - for (size_t l = 0; l < blocksize0; l++) { - // Extract Upper Left Block of mixed matrix - for (auto rowIt = (*hessianMixed_)[_0][_0].begin(); rowIt != (*hessianMixed_)[_0][_0].end(); ++rowIt) - for (auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt) - for(size_t k = 0; k < blocksize0; k++) - hessian[rowIt.index()][colIt.index()][k][l] = (*colIt)[k][l]; - // Extract Lower Left Block of mixed matrix - for (auto rowIt = (*hessianMixed_)[_1][_0].begin(); rowIt != (*hessianMixed_)[_1][_0].end(); ++rowIt) - for (auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt) - for(size_t k = 0; k < blocksize1; k++) - hessian[rowIt.index()][colIt.index()][k + blocksize0][l] = (*colIt)[k][l]; - } - for (size_t l = 0; l < blocksize1; l++) { - // Extract Upper Right Block of mixed matrix - for (auto rowIt = (*hessianMixed_)[_0][_1].begin(); rowIt != (*hessianMixed_)[_0][_1].end(); ++rowIt) - for (auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt) - for(size_t k = 0; k < blocksize0; k++) - hessian[rowIt.index()][colIt.index()][k][l + blocksize0] = (*colIt)[k][l]; - // Extract Lower Right Block of mixed matrix - for (auto rowIt = (*hessianMixed_)[_1][_1].begin(); rowIt != (*hessianMixed_)[_1][_1].end(); ++rowIt) - for (auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt) - for(size_t k = 0; k < blocksize1; k++) - hessian[rowIt.index()][colIt.index()][k + blocksize0][l + blocksize0] = (*colIt)[k][l]; - } + using namespace Dune::TypeTree::Indices; + auto n = basis_.size(); + + // Get a split up version of the input + auto solutionSplit = splitVector(sol); + + // Define the matrix and the gradient in the block structure + Dune::BlockVector<Dune::FieldVector<double, blocksize0> > gradient0(n); + Dune::BlockVector<Dune::FieldVector<double, blocksize1> > gradient1(n); + + if (computeOccupationPattern) { + Dune::MatrixIndexSet neighborsPerVertex; + getNeighborsPerVertex(neighborsPerVertex); + neighborsPerVertex.exportIdx((*hessianMixed_)[_0][_0]); + neighborsPerVertex.exportIdx((*hessianMixed_)[_0][_1]); + neighborsPerVertex.exportIdx((*hessianMixed_)[_1][_0]); + neighborsPerVertex.exportIdx((*hessianMixed_)[_1][_1]); + neighborsPerVertex.exportIdx(hessian); + } + + *hessianMixed_ = 0; + mixedAssembler_->assembleGradientAndHessian(solutionSplit[_0], solutionSplit[_1], gradient0, gradient1, *hessianMixed_, false); + + // Transform gradient and hessian back to the non-mixed structure + hessian = 0; + gradient.resize(n); + gradient = 0; + for (std::size_t i = 0; i < n; i++) { + for (int j = 0; j < blocksize0 + blocksize1; j++) + gradient[i][j] = j < blocksize0 ? gradient0[i][j] : gradient1[i][j - blocksize0]; + } + + // All 4 matrices are nxn; + // Each FieldMatrix in (*hessianMixed_)[_0][_0] is blocksize0 x blocksize0 + // Each FieldMatrix in (*hessianMixed_)[_1][_0] is blocksize1 x blocksize0 + // Each FieldMatrix in (*hessianMixed_)[_0][_1] is blocksize0 x blocksize1 + // Each FieldMatrix in (*hessianMixed_)[_1][_1] is blocksize1 x blocksize1 + // The hessian will then be a nxn matrix where each FieldMatrix is (blocksize0+blocksize1)x(blocksize0+blocksize1) + + for (size_t l = 0; l < blocksize0; l++) { + // Extract Upper Left Block of mixed matrix + for (auto rowIt = (*hessianMixed_)[_0][_0].begin(); rowIt != (*hessianMixed_)[_0][_0].end(); ++rowIt) + for (auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt) + for(size_t k = 0; k < blocksize0; k++) + hessian[rowIt.index()][colIt.index()][k][l] = (*colIt)[k][l]; + // Extract Lower Left Block of mixed matrix + for (auto rowIt = (*hessianMixed_)[_1][_0].begin(); rowIt != (*hessianMixed_)[_1][_0].end(); ++rowIt) + for (auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt) + for(size_t k = 0; k < blocksize1; k++) + hessian[rowIt.index()][colIt.index()][k + blocksize0][l] = (*colIt)[k][l]; + } + for (size_t l = 0; l < blocksize1; l++) { + // Extract Upper Right Block of mixed matrix + for (auto rowIt = (*hessianMixed_)[_0][_1].begin(); rowIt != (*hessianMixed_)[_0][_1].end(); ++rowIt) + for (auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt) + for(size_t k = 0; k < blocksize0; k++) + hessian[rowIt.index()][colIt.index()][k][l + blocksize0] = (*colIt)[k][l]; + // Extract Lower Right Block of mixed matrix + for (auto rowIt = (*hessianMixed_)[_1][_1].begin(); rowIt != (*hessianMixed_)[_1][_1].end(); ++rowIt) + for (auto colIt = rowIt->begin(); colIt != rowIt->end(); ++colIt) + for(size_t k = 0; k < blocksize1; k++) + hessian[rowIt.index()][colIt.index()][k + blocksize0][l + blocksize0] = (*colIt)[k][l]; + } } template <class Basis, class ScalarBasis, class TargetSpace, class MixedSpace0, class MixedSpace1> double Dune::GFE::GeodesicFEAssemblerWrapper<Basis, ScalarBasis, TargetSpace,MixedSpace0,MixedSpace1>:: computeEnergy(const std::vector<TargetSpace>& sol) const { - using namespace Dune::TypeTree::Indices; - auto solutionSplit = splitVector(sol); - return mixedAssembler_->computeEnergy(solutionSplit[_0], solutionSplit[_1]); + using namespace Dune::TypeTree::Indices; + auto solutionSplit = splitVector(sol); + return mixedAssembler_->computeEnergy(solutionSplit[_0], solutionSplit[_1]); } #endif //GLOBAL_GEODESIC_FE_ASSEMBLERWRAPPER_HH diff --git a/dune/gfe/assemblers/harmonicenergy.hh b/dune/gfe/assemblers/harmonicenergy.hh index f157a319..e909ee60 100644 --- a/dune/gfe/assemblers/harmonicenergy.hh +++ b/dune/gfe/assemblers/harmonicenergy.hh @@ -8,21 +8,21 @@ template<class Basis, class LocalInterpolationRule, class TargetSpace> class HarmonicEnergy - : public Dune::GFE::LocalEnergy<Basis,TargetSpace> + : public Dune::GFE::LocalEnergy<Basis,TargetSpace> { - // grid types - typedef typename Basis::GridView GridView; - typedef typename GridView::ctype DT; - typedef typename TargetSpace::ctype RT; + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef typename TargetSpace::ctype RT; - // some other sizes - constexpr static int gridDim = GridView::dimension; + // some other sizes + constexpr static int gridDim = GridView::dimension; public: - /** \brief Assemble the energy for a single element */ - RT energy (const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution) const override; + /** \brief Assemble the energy for a single element */ + RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution) const override; }; @@ -32,47 +32,46 @@ HarmonicEnergy<Basis, LocalInterpolationRule, TargetSpace>:: energy(const typename Basis::LocalView& localView, const std::vector<TargetSpace>& localSolution) const { - RT energy = 0; + RT energy = 0; - const auto& localFiniteElement = localView.tree().finiteElement(); - LocalInterpolationRule localInterpolationRule(localFiniteElement,localSolution); + const auto& localFiniteElement = localView.tree().finiteElement(); + LocalInterpolationRule localInterpolationRule(localFiniteElement,localSolution); - int quadOrder = (localFiniteElement.type().isSimplex()) ? (localFiniteElement.localBasis().order()-1) * 2 + int quadOrder = (localFiniteElement.type().isSimplex()) ? (localFiniteElement.localBasis().order()-1) * 2 : (localFiniteElement.localBasis().order() * gridDim - 1) * 2; - const auto element = localView.element(); + const auto element = localView.element(); - const auto& quad = Dune::QuadratureRules<double, gridDim>::rule(localFiniteElement.type(), quadOrder); + const auto& quad = Dune::QuadratureRules<double, gridDim>::rule(localFiniteElement.type(), quadOrder); - for (size_t pt=0; pt<quad.size(); pt++) { + for (size_t pt=0; pt<quad.size(); pt++) { - // Local position of the quadrature point - const Dune::FieldVector<double,gridDim>& quadPos = quad[pt].position(); + // Local position of the quadrature point + const Dune::FieldVector<double,gridDim>& quadPos = quad[pt].position(); - const auto integrationElement = element.geometry().integrationElement(quadPos); + const auto integrationElement = element.geometry().integrationElement(quadPos); - const auto jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos); + const auto jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos); - auto weight = quad[pt].weight() * integrationElement; + auto weight = quad[pt].weight() * integrationElement; - // The derivative of the local function defined on the reference element - auto referenceDerivative = localInterpolationRule.evaluateDerivative(quadPos); + // The derivative of the local function defined on the reference element + auto referenceDerivative = localInterpolationRule.evaluateDerivative(quadPos); - // Compute the Frobenius norm squared of the derivative. This is the correct term - // if both domain and target space use the metric inherited from an embedding. - for (size_t i=0; i<jacobianInverseTransposed.N(); i++) - for (int j=0; j<TargetSpace::embeddedDim; j++) - { - RT entry = 0; - for (size_t k=0; k<jacobianInverseTransposed.M(); k++) - entry += jacobianInverseTransposed[i][k] * referenceDerivative[j][k]; - energy += weight * entry * entry; - } + // Compute the Frobenius norm squared of the derivative. This is the correct term + // if both domain and target space use the metric inherited from an embedding. + for (size_t i=0; i<jacobianInverseTransposed.N(); i++) + for (int j=0; j<TargetSpace::embeddedDim; j++) + { + RT entry = 0; + for (size_t k=0; k<jacobianInverseTransposed.M(); k++) + entry += jacobianInverseTransposed[i][k] * referenceDerivative[j][k]; + energy += weight * entry * entry; + } - } + } - return 0.5 * energy; + return 0.5 * energy; } #endif - diff --git a/dune/gfe/assemblers/l2distancesquaredenergy.hh b/dune/gfe/assemblers/l2distancesquaredenergy.hh index 46d526bc..1fe1ff61 100644 --- a/dune/gfe/assemblers/l2distancesquaredenergy.hh +++ b/dune/gfe/assemblers/l2distancesquaredenergy.hh @@ -83,4 +83,3 @@ public: }; #endif - diff --git a/dune/gfe/assemblers/localenergy.hh b/dune/gfe/assemblers/localenergy.hh index d80c0f0f..58c122c5 100644 --- a/dune/gfe/assemblers/localenergy.hh +++ b/dune/gfe/assemblers/localenergy.hh @@ -5,35 +5,34 @@ namespace Dune { -namespace GFE { - -/** \brief Base class for energies defined by integrating over one grid element */ -template<class Basis, class... TargetSpaces> -class LocalEnergy -{ -public: - using RT = typename std::common_type<typename TargetSpaces::ctype...>::type; - - /** \brief Compute the energy - * - * \param localView Local view specifying the current element and the FE space there - * \param coefficients The coefficients of a FE function on the current element - */ - virtual RT - energy (const typename Basis::LocalView& localView, - const std::vector<TargetSpaces>&... localSolution) const = 0; - - /** Empty virtual default destructor - * - * To allow proper destruction of derived classes through a base class pointer - */ - virtual ~LocalEnergy() = default; - -}; - -} // namespace GFE + namespace GFE { + + /** \brief Base class for energies defined by integrating over one grid element */ + template<class Basis, class ... TargetSpaces> + class LocalEnergy + { + public: + using RT = typename std::common_type<typename TargetSpaces::ctype...>::type; + + /** \brief Compute the energy + * + * \param localView Local view specifying the current element and the FE space there + * \param coefficients The coefficients of a FE function on the current element + */ + virtual RT + energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpaces>& ... localSolution) const = 0; + + /** Empty virtual default destructor + * + * To allow proper destruction of derived classes through a base class pointer + */ + virtual ~LocalEnergy() = default; + + }; + + } // namespace GFE } // namespace Dune #endif // DUNE_GFE_LOCALENERGY_HH - diff --git a/dune/gfe/assemblers/localfirstordermodel.hh b/dune/gfe/assemblers/localfirstordermodel.hh index 3c937e8a..44f0eaab 100644 --- a/dune/gfe/assemblers/localfirstordermodel.hh +++ b/dune/gfe/assemblers/localfirstordermodel.hh @@ -5,24 +5,23 @@ namespace Dune { -namespace GFE { + namespace GFE { -template<class Basis, class TargetSpace> -class LocalFirstOrderModel -: public Dune::GFE::LocalEnergy<Basis,TargetSpace> -{ -public: + template<class Basis, class TargetSpace> + class LocalFirstOrderModel + : public Dune::GFE::LocalEnergy<Basis,TargetSpace> + { + public: - /** \brief Assemble the element gradient of the energy functional */ - virtual void assembleGradient(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& solution, - std::vector<typename TargetSpace::TangentVector>& gradient) const = 0; + /** \brief Assemble the element gradient of the energy functional */ + virtual void assembleGradient(const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& solution, + std::vector<typename TargetSpace::TangentVector>& gradient) const = 0; -}; + }; -} // namespace GFE + } // namespace GFE } // namespace Dune #endif // DUNE_GFE_LOCALFIRSTORDERMODEL_HH - diff --git a/dune/gfe/assemblers/localgeodesicfeadolcstiffness.hh b/dune/gfe/assemblers/localgeodesicfeadolcstiffness.hh index 3727c6d5..45a56cc1 100644 --- a/dune/gfe/assemblers/localgeodesicfeadolcstiffness.hh +++ b/dune/gfe/assemblers/localgeodesicfeadolcstiffness.hh @@ -19,54 +19,54 @@ */ template<class Basis, class TargetSpace> class LocalGeodesicFEADOLCStiffness - : public LocalGeodesicFEStiffness<Basis,TargetSpace> + : public LocalGeodesicFEStiffness<Basis,TargetSpace> { - // grid types - typedef typename Basis::GridView GridView; - typedef typename GridView::ctype DT; - typedef typename TargetSpace::ctype RT; - typedef typename GridView::template Codim<0>::Entity Entity; + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef typename TargetSpace::ctype RT; + typedef typename GridView::template Codim<0>::Entity Entity; - typedef typename TargetSpace::template rebind<adouble>::other ATargetSpace; + typedef typename TargetSpace::template rebind<adouble>::other ATargetSpace; - // some other sizes - constexpr static int gridDim = GridView::dimension; + // some other sizes + constexpr static int gridDim = GridView::dimension; public: - //! Dimension of a tangent space - constexpr static int blocksize = TargetSpace::TangentVector::dimension; + //! Dimension of a tangent space + constexpr static int blocksize = TargetSpace::TangentVector::dimension; - //! Dimension of the embedding space - constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; + //! Dimension of the embedding space + constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; - LocalGeodesicFEADOLCStiffness(const Dune::GFE::LocalEnergy<Basis, ATargetSpace>* energy, bool adolcScalarMode = false) + LocalGeodesicFEADOLCStiffness(const Dune::GFE::LocalEnergy<Basis, ATargetSpace>* energy, bool adolcScalarMode = false) : localEnergy_(energy), - adolcScalarMode_(adolcScalarMode) - {} + adolcScalarMode_(adolcScalarMode) + {} - /** \brief Compute the energy at the current configuration */ - virtual RT energy (const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution) const; + /** \brief Compute the energy at the current configuration */ + virtual RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution) const; - /** \brief Assemble the element gradient of the energy functional + /** \brief Assemble the element gradient of the energy functional - This uses the automatic differentiation toolbox ADOL_C. - */ - virtual void assembleGradient(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& solution, - std::vector<typename TargetSpace::TangentVector>& gradient) const; + This uses the automatic differentiation toolbox ADOL_C. + */ + virtual void assembleGradient(const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& solution, + std::vector<typename TargetSpace::TangentVector>& gradient) const; - /** \brief Assemble the local stiffness matrix at the current position + /** \brief Assemble the local stiffness matrix at the current position - This uses the automatic differentiation toolbox ADOL_C. - */ - virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution, - std::vector<typename TargetSpace::TangentVector>& localGradient); + This uses the automatic differentiation toolbox ADOL_C. + */ + virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution, + std::vector<typename TargetSpace::TangentVector>& localGradient); - const Dune::GFE::LocalEnergy<Basis, ATargetSpace>* localEnergy_; - const bool adolcScalarMode_; + const Dune::GFE::LocalEnergy<Basis, ATargetSpace>* localEnergy_; + const bool adolcScalarMode_; }; @@ -76,51 +76,51 @@ LocalGeodesicFEADOLCStiffness<Basis, TargetSpace>:: energy(const typename Basis::LocalView& localView, const std::vector<TargetSpace>& localSolution) const { - double pureEnergy; - int rank = localView.globalBasis().gridView().comm().rank(); - std::vector<ATargetSpace> localASolution(localSolution.size()); - - trace_on(rank); - - 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 - } - - try { - energy = localEnergy_->energy(localView,localASolution); - } catch (Dune::Exception &e) { - trace_off(); - throw e; - } + double pureEnergy; + int rank = localView.globalBasis().gridView().comm().rank(); + std::vector<ATargetSpace> localASolution(localSolution.size()); + + trace_on(rank); + + 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 + } + + try { + energy = localEnergy_->energy(localView,localASolution); + } catch (Dune::Exception &e) { + trace_off(); + throw e; + } - energy >>= pureEnergy; + energy >>= pureEnergy; - trace_off(); + trace_off(); #if 0 - size_t tape_stats[STAT_SIZE]; - tapestats(rank,tape_stats); // reading of tape statistics - cout<<"maxlive "<<tape_stats[NUM_MAX_LIVES]<<"\n"; - cout<<"tay_stack_size "<<tape_stats[TAY_STACK_SIZE]<<"\n"; - cout<<"total number of operations "<<tape_stats[NUM_OPERATIONS]<<"\n"; - // ..... print other tape stats + size_t tape_stats[STAT_SIZE]; + tapestats(rank,tape_stats); // reading of tape statistics + cout<<"maxlive "<<tape_stats[NUM_MAX_LIVES]<<"\n"; + cout<<"tay_stack_size "<<tape_stats[TAY_STACK_SIZE]<<"\n"; + cout<<"total number of operations "<<tape_stats[NUM_OPERATIONS]<<"\n"; + // ..... print other tape stats #endif - return pureEnergy; + return pureEnergy; } @@ -130,41 +130,41 @@ assembleGradient(const typename Basis::LocalView& localView, 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(localView, localSolution); - - int rank = localView.globalBasis().gridView().comm().rank(); + // Tape energy computation. We may not have to do this every time, but it's comparatively cheap. + energy(localView, 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(rank,nDoubles,xp.data(),g.data()); // gradient evaluation - - // Copy into Dune type - std::vector<typename TargetSpace::EmbeddedTangentVector> localEmbeddedGradient(localSolution.size()); + int rank = localView.globalBasis().gridView().comm().rank(); - 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; + // 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]; - // 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 + std::vector<double> g(nDoubles); + gradient(rank,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]); + } } @@ -176,192 +176,191 @@ assembleGradient(const typename Basis::LocalView& localView, template <class Basis, class TargetSpace> void LocalGeodesicFEADOLCStiffness<Basis, TargetSpace>:: assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution, - std::vector<typename TargetSpace::TangentVector>& localGradient) + 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(localView, localSolution); + // Tape energy computation. We may not have to do this every time, but it's comparatively cheap. + energy(localView, localSolution); - int rank = localView.globalBasis().gridView().comm().rank(); + int rank = localView.globalBasis().gridView().comm().rank(); - ///////////////////////////////////////////////////////////////// - // Compute the gradient. It is needed to transform the Hessian - // into the correct coordinates. - ///////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////// + // 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 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(rank,nDoubles,xp.data(),g.data()); // gradient evaluation - - // Copy into Dune type - std::vector<typename TargetSpace::EmbeddedTangentVector> localEmbeddedGradient(localSolution.size()); + std::vector<double> g(nDoubles); + gradient(rank,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 + ///////////////////////////////////////////////////////////////// + + // We compute the Hessian of the energy functional using the ADOL-C system. + // Since ADOL-C does not know about nonlinear spaces, what we get is actually + // the Hessian of a prolongation of the energy functional into the surrounding + // Euclidean space. To obtain the Riemannian Hessian from this we apply the + // formula described in Absil, Mahoney, Trumpf, "An extrinsic look at the Riemannian Hessian". + // This formula consists of two steps: + // 1) Remove all entries of the Hessian pertaining to the normal space of the + // manifold. In the aforementioned paper this is done by projection onto the + // tangent space. Since we want a matrix that is really smaller (but full rank again), + // we can achieve the same effect by multiplying the embedded Hessian from the left + // and from the right by the matrix of orthonormal frames. + // 2) Add a correction involving the Weingarten map. + // + // This works, and is easy to implement using the ADOL-C "hessian" driver. + // However, here we implement a small shortcut. Computing the embedded Hessian and + // multiplying one side by the orthonormal frame is the same as evaluating the Hessian + // (seen as an operator from R^n to R^n) in the directions of the vectors of the + // orthonormal frame. By luck, ADOL-C can compute the evaluations of the Hessian in + // a given direction directly (in fact, this is also how the "hessian" driver works). + // Since there are less frame vectors than the dimension of the embedding space, + // this reinterpretation allows to reduce the number of calls to ADOL-C. + // In my Cosserat shell tests this reduced assembly time by about 10%. + + std::vector<Dune::FieldMatrix<RT,blocksize,embeddedBlocksize> > orthonormalFrame(nDofs); + + for (size_t i=0; i<nDofs; i++) + orthonormalFrame[i] = localSolution[i].orthonormalFrame(); + + Dune::Matrix<Dune::FieldMatrix<double,blocksize, embeddedBlocksize> > embeddedHessian(nDofs,nDofs); + + if (adolcScalarMode_) { + std::vector<double> v(nDoubles); + std::vector<double> w(nDoubles); + + std::fill(v.begin(), v.end(), 0.0); - 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]); + for (int ii=0; ii<blocksize; ii++) + { + // Evaluate Hessian in the direction of each vector of the orthonormal frame + for (size_t k=0; k<embeddedBlocksize; k++) + v[i*embeddedBlocksize + k] = orthonormalFrame[i][ii][k]; + + int rc= 3; + MINDEC(rc, hess_vec(rank, nDoubles, xp.data(), v.data(), w.data())); + if (rc < 0) + DUNE_THROW(Dune::Exception, "ADOL-C has returned with error code " << rc << "!"); + + for (size_t j=0; j<nDoubles; j++) + embeddedHessian[i][j/embeddedBlocksize][ii][j%embeddedBlocksize] = w[j]; + + // Make v the null vector again + std::fill(&v[i*embeddedBlocksize], &v[(i+1)*embeddedBlocksize], 0.0); + } + } else { //ADOL-C vector mode + int n = nDoubles; + int nDirections = nDofs * blocksize; + double* tangent[nDoubles]; + for(size_t i=0; i<nDoubles; i++) + tangent[i] = (double*)malloc(nDirections*sizeof(double)); + + double* rawHessian[nDoubles]; + for(size_t i=0; i<nDoubles; i++) + rawHessian[i] = (double*)malloc(nDirections*sizeof(double)); + + for (int j=0; j<nDirections; j++) + { + for (int i=0; i<n; i++) + tangent[i][j] = 0.0; + + for (int i=0; i<embeddedBlocksize; i++) + tangent[(j/blocksize)*embeddedBlocksize+i][j] = orthonormalFrame[j/blocksize][j%blocksize][i]; } + hess_mat(rank,nDoubles,nDirections,xp.data(),tangent,rawHessian); - ///////////////////////////////////////////////////////////////// - // Compute Hessian - ///////////////////////////////////////////////////////////////// - - // We compute the Hessian of the energy functional using the ADOL-C system. - // Since ADOL-C does not know about nonlinear spaces, what we get is actually - // the Hessian of a prolongation of the energy functional into the surrounding - // Euclidean space. To obtain the Riemannian Hessian from this we apply the - // formula described in Absil, Mahoney, Trumpf, "An extrinsic look at the Riemannian Hessian". - // This formula consists of two steps: - // 1) Remove all entries of the Hessian pertaining to the normal space of the - // manifold. In the aforementioned paper this is done by projection onto the - // tangent space. Since we want a matrix that is really smaller (but full rank again), - // we can achieve the same effect by multiplying the embedded Hessian from the left - // and from the right by the matrix of orthonormal frames. - // 2) Add a correction involving the Weingarten map. - // - // This works, and is easy to implement using the ADOL-C "hessian" driver. - // However, here we implement a small shortcut. Computing the embedded Hessian and - // multiplying one side by the orthonormal frame is the same as evaluating the Hessian - // (seen as an operator from R^n to R^n) in the directions of the vectors of the - // orthonormal frame. By luck, ADOL-C can compute the evaluations of the Hessian in - // a given direction directly (in fact, this is also how the "hessian" driver works). - // Since there are less frame vectors than the dimension of the embedding space, - // this reinterpretation allows to reduce the number of calls to ADOL-C. - // In my Cosserat shell tests this reduced assembly time by about 10%. - - std::vector<Dune::FieldMatrix<RT,blocksize,embeddedBlocksize> > orthonormalFrame(nDofs); + // Copy Hessian into Dune data type + for(size_t i=0; i<nDoubles; i++) + for (int j=0; j<nDirections; j++) + embeddedHessian[j/blocksize][i/embeddedBlocksize][j%blocksize][i%embeddedBlocksize] = rawHessian[i][j]; - for (size_t i=0; i<nDofs; i++) - orthonormalFrame[i] = localSolution[i].orthonormalFrame(); - - Dune::Matrix<Dune::FieldMatrix<double,blocksize, embeddedBlocksize> > embeddedHessian(nDofs,nDofs); - - if (adolcScalarMode_) { - std::vector<double> v(nDoubles); - std::vector<double> w(nDoubles); - - std::fill(v.begin(), v.end(), 0.0); - - for (size_t i=0; i<nDofs; i++) - for (int ii=0; ii<blocksize; ii++) - { - // Evaluate Hessian in the direction of each vector of the orthonormal frame - for (size_t k=0; k<embeddedBlocksize; k++) - v[i*embeddedBlocksize + k] = orthonormalFrame[i][ii][k]; - - int rc= 3; - MINDEC(rc, hess_vec(rank, nDoubles, xp.data(), v.data(), w.data())); - if (rc < 0) - DUNE_THROW(Dune::Exception, "ADOL-C has returned with error code " << rc << "!"); - - for (size_t j=0; j<nDoubles; j++) - embeddedHessian[i][j/embeddedBlocksize][ii][j%embeddedBlocksize] = w[j]; - - // Make v the null vector again - std::fill(&v[i*embeddedBlocksize], &v[(i+1)*embeddedBlocksize], 0.0); - } - } else { //ADOL-C vector mode - int n = nDoubles; - int nDirections = nDofs * blocksize; - double* tangent[nDoubles]; - for(size_t i=0; i<nDoubles; i++) - tangent[i] = (double*)malloc(nDirections*sizeof(double)); - - double* rawHessian[nDoubles]; - for(size_t i=0; i<nDoubles; i++) - rawHessian[i] = (double*)malloc(nDirections*sizeof(double)); - - for (int j=0; j<nDirections; j++) - { - for (int i=0; i<n; i++) - tangent[i][j] = 0.0; - - for (int i=0; i<embeddedBlocksize; i++) - tangent[(j/blocksize)*embeddedBlocksize+i][j] = orthonormalFrame[j/blocksize][j%blocksize][i]; - } - hess_mat(rank,nDoubles,nDirections,xp.data(),tangent,rawHessian); - - // Copy Hessian into Dune data type - for(size_t i=0; i<nDoubles; i++) - for (int j=0; j<nDirections; j++) - embeddedHessian[j/blocksize][i/embeddedBlocksize][j%blocksize][i%embeddedBlocksize] = rawHessian[i][j]; - - for(size_t i=0; i<nDoubles; i++) { - free(rawHessian[i]); - free(tangent[i]); - } + for(size_t i=0; i<nDoubles; i++) { + free(rawHessian[i]); + free(tangent[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". + } + // 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; + typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector; + typedef typename TargetSpace::TangentVector TangentVector; - this->A_.setSize(nDofs,nDofs); + this->A_.setSize(nDofs,nDofs); - for (size_t col=0; col<nDofs; col++) { + for (size_t col=0; col<nDofs; col++) { - for (size_t subCol=0; subCol<blocksize; subCol++) { + for (size_t subCol=0; subCol<blocksize; subCol++) { - EmbeddedTangentVector z = orthonormalFrame[col][subCol]; + EmbeddedTangentVector z = orthonormalFrame[col][subCol]; - // P_x \partial^2 f z - for (size_t row=0; row<nDofs; row++) { - TangentVector semiEmbeddedProduct; - embeddedHessian[row][col].mv(z,semiEmbeddedProduct); + // P_x \partial^2 f z + for (size_t row=0; row<nDofs; row++) { + TangentVector semiEmbeddedProduct; + embeddedHessian[row][col].mv(z,semiEmbeddedProduct); - for (int subRow=0; subRow<blocksize; subRow++) - this->A_[row][col][subRow][subCol] = semiEmbeddedProduct[subRow]; - } - - } + for (int subRow=0; subRow<blocksize; subRow++) + this->A_[row][col][subRow][subCol] = semiEmbeddedProduct[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]); + ////////////////////////////////////////////////////////////////////////////////////// + // Further correction due to non-planar configuration space + // + \mathfrak{A}_x(z,P^\orth_x \partial f) + ////////////////////////////////////////////////////////////////////////////////////// - for (size_t row=0; row<nDofs; row++) { + // 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 subRow=0; subRow<blocksize; subRow++) { + for (size_t row=0; row<nDofs; row++) { - EmbeddedTangentVector z = orthonormalFrame[row][subRow]; - EmbeddedTangentVector tmp1 = localSolution[row].weingarten(z,projectedGradient[row]); + for (size_t subRow=0; subRow<blocksize; subRow++) { - TangentVector tmp2; - orthonormalFrame[row].mv(tmp1,tmp2); + EmbeddedTangentVector z = orthonormalFrame[row][subRow]; + EmbeddedTangentVector tmp1 = localSolution[row].weingarten(z,projectedGradient[row]); - this->A_[row][row][subRow] += tmp2; - } + 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", "--"); + } + + // std::cout << "ADOL-C stiffness:\n"; + // printmatrix(std::cout, this->A_, "foo", "--"); } #endif - diff --git a/dune/gfe/assemblers/localgeodesicfefdstiffness.hh b/dune/gfe/assemblers/localgeodesicfefdstiffness.hh index 5eaa6094..6ad5ad3e 100644 --- a/dune/gfe/assemblers/localgeodesicfefdstiffness.hh +++ b/dune/gfe/assemblers/localgeodesicfefdstiffness.hh @@ -10,63 +10,63 @@ */ template<class Basis, class TargetSpace, class field_type=double> class LocalGeodesicFEFDStiffness - : public LocalGeodesicFEStiffness<Basis,TargetSpace> + : public LocalGeodesicFEStiffness<Basis,TargetSpace> { - // grid types - typedef typename Basis::GridView GridView; - typedef typename GridView::ctype DT; - typedef typename TargetSpace::ctype RT; - typedef typename GridView::template Codim<0>::Entity Entity; + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef typename TargetSpace::ctype RT; + typedef typename GridView::template Codim<0>::Entity Entity; - typedef typename TargetSpace::template rebind<field_type>::other ATargetSpace; + typedef typename TargetSpace::template rebind<field_type>::other ATargetSpace; - // some other sizes - constexpr static int gridDim = GridView::dimension; + // some other sizes + constexpr static int gridDim = GridView::dimension; public: - //! Dimension of a tangent space - constexpr static int blocksize = TargetSpace::TangentVector::dimension; + //! Dimension of a tangent space + constexpr static int blocksize = TargetSpace::TangentVector::dimension; - //! Dimension of the embedding space - constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; + //! Dimension of the embedding space + constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; - LocalGeodesicFEFDStiffness(const Dune::GFE::LocalEnergy<Basis, ATargetSpace>* energy) + LocalGeodesicFEFDStiffness(const Dune::GFE::LocalEnergy<Basis, ATargetSpace>* energy) : localEnergy_(energy) - {} + {} - /** \brief Compute the energy at the current configuration */ - virtual RT energy (const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution) const override - { - return localEnergy_->energy(localView,localSolution); - } + /** \brief Compute the energy at the current configuration */ + virtual RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution) const override + { + return localEnergy_->energy(localView,localSolution); + } - /** \brief Assemble the element gradient of the energy functional + /** \brief Assemble the element gradient of the energy functional - The default implementation in this class uses a finite difference approximation */ - virtual void assembleGradient(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& solution, - std::vector<typename TargetSpace::TangentVector>& gradient) const override; + The default implementation in this class uses a finite difference approximation */ + virtual void assembleGradient(const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& solution, + std::vector<typename TargetSpace::TangentVector>& gradient) const override; - /** \brief Assemble the local tangent matrix and gradient at the current position + /** \brief Assemble the local tangent matrix and gradient at the current position - This implementation uses finite-difference approximations + This implementation uses finite-difference approximations - The formula for the Riemannian Hessian has been taken from Absil, Mahony, Sepulchre: - 'Optimization algorithms on matrix manifolds', page 107. There it says that - \f[ - \langle Hess f(x)[\xi], \eta \rangle - = \frac 12 \frac{d^2}{dt^2} \Big(f(\exp_x(t(\xi + \eta))) - f(\exp_x(t\xi)) - f(\exp_x(t\eta))\Big)\Big|_{t=0}. - \f] - We compute that using a finite difference approximation. - */ - virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution, - std::vector<typename TargetSpace::TangentVector>& localGradient) override; + The formula for the Riemannian Hessian has been taken from Absil, Mahony, Sepulchre: + 'Optimization algorithms on matrix manifolds', page 107. There it says that + \f[ + \langle Hess f(x)[\xi], \eta \rangle + = \frac 12 \frac{d^2}{dt^2} \Big(f(\exp_x(t(\xi + \eta))) - f(\exp_x(t\xi)) - f(\exp_x(t\eta))\Big)\Big|_{t=0}. + \f] + We compute that using a finite difference approximation. + */ + virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution, + std::vector<typename TargetSpace::TangentVector>& localGradient) override; - const Dune::GFE::LocalEnergy<Basis, ATargetSpace>* localEnergy_; + const Dune::GFE::LocalEnergy<Basis, ATargetSpace>* localEnergy_; }; @@ -77,55 +77,55 @@ assembleGradient(const typename Basis::LocalView& localView, std::vector<typename TargetSpace::TangentVector>& localGradient) const { - // /////////////////////////////////////////////////////////// - // Compute gradient by finite-difference approximation - // /////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////// + // Compute gradient by finite-difference approximation + // /////////////////////////////////////////////////////////// - field_type eps = 1e-6; + field_type eps = 1e-6; - std::vector<ATargetSpace> localASolution(localSolution.size()); - 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 - } + std::vector<ATargetSpace> localASolution(localSolution.size()); + 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 + } - localGradient.resize(localSolution.size()); + localGradient.resize(localSolution.size()); - std::vector<ATargetSpace> forwardSolution = localASolution; - std::vector<ATargetSpace> backwardSolution = localASolution; + std::vector<ATargetSpace> forwardSolution = localASolution; + std::vector<ATargetSpace> backwardSolution = localASolution; - for (size_t i=0; i<localSolution.size(); i++) { + for (size_t i=0; i<localSolution.size(); i++) { - // basis vectors of the tangent space of the i-th entry of localSolution - const Dune::FieldMatrix<field_type,blocksize,embeddedBlocksize> B = localSolution[i].orthonormalFrame(); + // basis vectors of the tangent space of the i-th entry of localSolution + const Dune::FieldMatrix<field_type,blocksize,embeddedBlocksize> B = localSolution[i].orthonormalFrame(); - for (int j=0; j<blocksize; j++) { + for (int j=0; j<blocksize; j++) { - typename ATargetSpace::EmbeddedTangentVector forwardCorrection = B[j]; - forwardCorrection *= eps; + typename ATargetSpace::EmbeddedTangentVector forwardCorrection = B[j]; + forwardCorrection *= eps; - typename ATargetSpace::EmbeddedTangentVector backwardCorrection = B[j]; - backwardCorrection *= -eps; + typename ATargetSpace::EmbeddedTangentVector backwardCorrection = B[j]; + backwardCorrection *= -eps; - forwardSolution[i] = ATargetSpace::exp(localASolution[i], forwardCorrection); - backwardSolution[i] = ATargetSpace::exp(localASolution[i], backwardCorrection); + forwardSolution[i] = ATargetSpace::exp(localASolution[i], forwardCorrection); + backwardSolution[i] = ATargetSpace::exp(localASolution[i], backwardCorrection); - field_type foo = (localEnergy_->energy(localView,forwardSolution) - localEnergy_->energy(localView, backwardSolution)) / (2*eps); + field_type foo = (localEnergy_->energy(localView,forwardSolution) - localEnergy_->energy(localView, backwardSolution)) / (2*eps); #ifdef MULTIPRECISION - localGradient[i][j] = foo.template convert_to<double>(); + localGradient[i][j] = foo.template convert_to<double>(); #else - localGradient[i][j] = foo; + localGradient[i][j] = foo; #endif - } + } - forwardSolution[i] = localASolution[i]; - backwardSolution[i] = localASolution[i]; + forwardSolution[i] = localASolution[i]; + backwardSolution[i] = localASolution[i]; - } + } } @@ -138,130 +138,129 @@ assembleGradient(const typename Basis::LocalView& localView, template <class Basis, class TargetSpace, class field_type> void LocalGeodesicFEFDStiffness<Basis, TargetSpace, field_type>:: assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution, - std::vector<typename TargetSpace::TangentVector>& localGradient) + const std::vector<TargetSpace>& localSolution, + std::vector<typename TargetSpace::TangentVector>& localGradient) { - // Number of degrees of freedom for this element - size_t nDofs = localSolution.size(); + // Number of degrees of freedom for this element + size_t nDofs = localSolution.size(); - // Clear assemble data - this->A_.setSize(nDofs, nDofs); + // Clear assemble data + this->A_.setSize(nDofs, nDofs); - this->A_ = 0; + this->A_ = 0; #ifdef MULTIPRECISION - const field_type eps = 1e-10; + const field_type eps = 1e-10; #else - const field_type eps = 1e-4; + const field_type eps = 1e-4; #endif - std::vector<ATargetSpace> localASolution(localSolution.size()); - 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]; - } - - std::vector<Dune::FieldMatrix<double,blocksize,embeddedBlocksize> > B(localSolution.size()); - for (size_t i=0; i<B.size(); i++) - B[i] = localSolution[i].orthonormalFrame(); + std::vector<ATargetSpace> localASolution(localSolution.size()); + 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]; + } - // 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 = -localEnergy_->energy(localView, localASolution); + std::vector<Dune::FieldMatrix<double,blocksize,embeddedBlocksize> > B(localSolution.size()); + for (size_t i=0; i<B.size(); i++) + B[i] = localSolution[i].orthonormalFrame(); - // Precompute energy infinitesimal corrections in the directions of the local basis vectors - std::vector<std::array<field_type,blocksize> > forwardEnergy(nDofs); - std::vector<std::array<field_type,blocksize> > backwardEnergy(nDofs); + // 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 = -localEnergy_->energy(localView, localASolution); - //#pragma omp parallel for schedule (dynamic) - for (size_t i=0; i<localSolution.size(); i++) { - for (size_t i2=0; i2<blocksize; i2++) { - typename ATargetSpace::EmbeddedTangentVector epsXi = B[i][i2]; - epsXi *= eps; - typename ATargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; - minusEpsXi *= -1; + // Precompute energy infinitesimal corrections in the directions of the local basis vectors + std::vector<std::array<field_type,blocksize> > forwardEnergy(nDofs); + std::vector<std::array<field_type,blocksize> > backwardEnergy(nDofs); - std::vector<ATargetSpace> forwardSolution = localASolution; - std::vector<ATargetSpace> backwardSolution = localASolution; + //#pragma omp parallel for schedule (dynamic) + for (size_t i=0; i<localSolution.size(); i++) { + for (size_t i2=0; i2<blocksize; i2++) { + typename ATargetSpace::EmbeddedTangentVector epsXi = B[i][i2]; + epsXi *= eps; + typename ATargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; + minusEpsXi *= -1; - forwardSolution[i] = ATargetSpace::exp(localASolution[i],epsXi); - backwardSolution[i] = ATargetSpace::exp(localASolution[i],minusEpsXi); + std::vector<ATargetSpace> forwardSolution = localASolution; + std::vector<ATargetSpace> backwardSolution = localASolution; - forwardEnergy[i][i2] = localEnergy_->energy(localView, forwardSolution); - backwardEnergy[i][i2] = localEnergy_->energy(localView, backwardSolution); + forwardSolution[i] = ATargetSpace::exp(localASolution[i],epsXi); + backwardSolution[i] = ATargetSpace::exp(localASolution[i],minusEpsXi); - } + forwardEnergy[i][i2] = localEnergy_->energy(localView, forwardSolution); + backwardEnergy[i][i2] = localEnergy_->energy(localView, backwardSolution); } - ////////////////////////////////////////////////////////////// - // Compute gradient by finite-difference approximation - ////////////////////////////////////////////////////////////// + } - localGradient.resize(localSolution.size()); + ////////////////////////////////////////////////////////////// + // Compute gradient by finite-difference approximation + ////////////////////////////////////////////////////////////// - for (size_t i=0; i<localSolution.size(); i++) - for (int j=0; j<blocksize; j++) - { - field_type foo = (forwardEnergy[i][j] - backwardEnergy[i][j]) / (2*eps); + localGradient.resize(localSolution.size()); + + for (size_t i=0; i<localSolution.size(); i++) + for (int j=0; j<blocksize; j++) + { + field_type foo = (forwardEnergy[i][j] - backwardEnergy[i][j]) / (2*eps); #ifdef MULTIPRECISION - localGradient[i][j] = foo.template convert_to<double>(); + localGradient[i][j] = foo.template convert_to<double>(); #else - localGradient[i][j] = foo; + localGradient[i][j] = foo; #endif - } + } - /////////////////////////////////////////////////////////////////////////// - // 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<blocksize; i2++) { - for (size_t j=0; j<=i; j++) { - for (size_t j2=0; j2<((i==j) ? i2+1 : blocksize); j2++) { - - std::vector<ATargetSpace> forwardSolutionXiEta = localASolution; - std::vector<ATargetSpace> backwardSolutionXiEta = localASolution; - - typename ATargetSpace::EmbeddedTangentVector epsXi = B[i][i2]; epsXi *= eps; - typename ATargetSpace::EmbeddedTangentVector epsEta = B[j][j2]; epsEta *= eps; - - typename ATargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; minusEpsXi *= -1; - typename ATargetSpace::EmbeddedTangentVector minusEpsEta = epsEta; minusEpsEta *= -1; - - if (i==j) - forwardSolutionXiEta[i] = ATargetSpace::exp(localASolution[i],epsXi+epsEta); - else { - forwardSolutionXiEta[i] = ATargetSpace::exp(localASolution[i],epsXi); - forwardSolutionXiEta[j] = ATargetSpace::exp(localASolution[j],epsEta); - } - - if (i==j) - backwardSolutionXiEta[i] = ATargetSpace::exp(localASolution[i],minusEpsXi+minusEpsEta); - else { - backwardSolutionXiEta[i] = ATargetSpace::exp(localASolution[i],minusEpsXi); - backwardSolutionXiEta[j] = ATargetSpace::exp(localASolution[j],minusEpsEta); - } - - field_type forwardValue = localEnergy_->energy(localView, forwardSolutionXiEta) - forwardEnergy[i][i2] - forwardEnergy[j][j2]; - field_type backwardValue = localEnergy_->energy(localView, backwardSolutionXiEta) - backwardEnergy[i][i2] - backwardEnergy[j][j2]; - - field_type foo = 0.5 * (forwardValue - 2*centerValue + backwardValue) / (eps*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<blocksize; i2++) { + for (size_t j=0; j<=i; j++) { + for (size_t j2=0; j2<((i==j) ? i2+1 : blocksize); j2++) { + + std::vector<ATargetSpace> forwardSolutionXiEta = localASolution; + std::vector<ATargetSpace> backwardSolutionXiEta = localASolution; + + typename ATargetSpace::EmbeddedTangentVector epsXi = B[i][i2]; epsXi *= eps; + typename ATargetSpace::EmbeddedTangentVector epsEta = B[j][j2]; epsEta *= eps; + + typename ATargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; minusEpsXi *= -1; + typename ATargetSpace::EmbeddedTangentVector minusEpsEta = epsEta; minusEpsEta *= -1; + + if (i==j) + forwardSolutionXiEta[i] = ATargetSpace::exp(localASolution[i],epsXi+epsEta); + else { + forwardSolutionXiEta[i] = ATargetSpace::exp(localASolution[i],epsXi); + forwardSolutionXiEta[j] = ATargetSpace::exp(localASolution[j],epsEta); + } + + if (i==j) + backwardSolutionXiEta[i] = ATargetSpace::exp(localASolution[i],minusEpsXi+minusEpsEta); + else { + backwardSolutionXiEta[i] = ATargetSpace::exp(localASolution[i],minusEpsXi); + backwardSolutionXiEta[j] = ATargetSpace::exp(localASolution[j],minusEpsEta); + } + + field_type forwardValue = localEnergy_->energy(localView, forwardSolutionXiEta) - forwardEnergy[i][i2] - forwardEnergy[j][j2]; + field_type backwardValue = localEnergy_->energy(localView, backwardSolutionXiEta) - backwardEnergy[i][i2] - backwardEnergy[j][j2]; + + field_type foo = 0.5 * (forwardValue - 2*centerValue + backwardValue) / (eps*eps); #ifdef MULTIPRECISION - this->A_[i][j][i2][j2] = this->A_[j][i][j2][i2] = foo.template convert_to<double>(); + this->A_[i][j][i2][j2] = this->A_[j][i][j2][i2] = foo.template convert_to<double>(); #else - this->A_[i][j][i2][j2] = this->A_[j][i][j2][i2] = foo; + this->A_[i][j][i2][j2] = this->A_[j][i][j2][i2] = foo; #endif - } - } } + } } + } } #endif - diff --git a/dune/gfe/assemblers/localgeodesicfestiffness.hh b/dune/gfe/assemblers/localgeodesicfestiffness.hh index a916fbdb..9dd98819 100644 --- a/dune/gfe/assemblers/localgeodesicfestiffness.hh +++ b/dune/gfe/assemblers/localgeodesicfestiffness.hh @@ -8,47 +8,46 @@ template<class Basis, class TargetSpace> class LocalGeodesicFEStiffness -: public Dune::GFE::LocalFirstOrderModel<Basis,TargetSpace> + : public Dune::GFE::LocalFirstOrderModel<Basis,TargetSpace> { - // grid types - typedef typename Basis::GridView GridView; - typedef typename GridView::ctype DT; - typedef typename TargetSpace::ctype RT; - typedef typename GridView::template Codim<0>::Entity Entity; + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef typename TargetSpace::ctype RT; + typedef typename GridView::template Codim<0>::Entity Entity; - // some other sizes - constexpr static int gridDim = GridView::dimension; + // some other sizes + constexpr static int gridDim = GridView::dimension; public: - //! Dimension of a tangent space - constexpr static int blocksize = TargetSpace::TangentVector::dimension; + //! Dimension of a tangent space + constexpr static int blocksize = TargetSpace::TangentVector::dimension; - //! Dimension of the embedding space - constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; + //! Dimension of the embedding space + constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; - /** \brief Assemble the local gradient and stiffness matrix at the current position + /** \brief Assemble the local gradient and stiffness matrix at the current position - */ - virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution, - std::vector<typename TargetSpace::TangentVector>& localGradient) = 0; + */ + virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution, + std::vector<typename TargetSpace::TangentVector>& localGradient) = 0; - /** \brief Compute the energy at the current configuration */ - virtual RT energy (const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution) const = 0; + /** \brief Compute the energy at the current configuration */ + virtual RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution) const = 0; - /** \brief Assemble the element gradient of the energy functional + /** \brief Assemble the element gradient of the energy functional - The default implementation in this class uses a finite difference approximation */ - virtual void assembleGradient(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& solution, - std::vector<typename TargetSpace::TangentVector>& gradient) const = 0; + The default implementation in this class uses a finite difference approximation */ + virtual void assembleGradient(const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& solution, + std::vector<typename TargetSpace::TangentVector>& gradient) const = 0; - // assembled data - Dune::Matrix<Dune::FieldMatrix<RT,blocksize,blocksize> > A_; + // assembled data + Dune::Matrix<Dune::FieldMatrix<RT,blocksize,blocksize> > A_; }; #endif - diff --git a/dune/gfe/assemblers/localintegralenergy.hh b/dune/gfe/assemblers/localintegralenergy.hh index 420dd196..40fb241b 100644 --- a/dune/gfe/assemblers/localintegralenergy.hh +++ b/dune/gfe/assemblers/localintegralenergy.hh @@ -22,79 +22,79 @@ namespace Dune::GFE { /** - \brief Assembles the elastic energy for a single element integrating the localdensity over one element. + \brief Assembles the elastic energy for a single element integrating the localdensity over one element. This class works similarly to the class Dune::Elasticity::LocalIntegralEnergy, where Dune::Elasticity::LocalIntegralEnergy extends Dune::Elasticity::LocalEnergy and Dune::GFE::LocalIntegralEnergy extends Dune::GFE::LocalEnergy. - */ -template<class Basis, class... TargetSpaces> -class LocalIntegralEnergy - : public Dune::GFE::LocalEnergy<Basis,TargetSpaces...> -{ - using LocalView = typename Basis::LocalView; - using GridView = typename LocalView::GridView; - using DT = typename GridView::Grid::ctype; - using RT = typename GFE::LocalEnergy<Basis,TargetSpaces...>::RT; - - constexpr static int gridDim = GridView::dimension; - -public: - - /** \brief Constructor with a Dune::Elasticity::LocalDensity - */ - LocalIntegralEnergy(const std::shared_ptr<Elasticity::LocalDensity<gridDim,RT,DT>>& ld) - : localDensityElasticity_(ld) - {} - - /** \brief Constructor with a Dune::GFE::LocalDensity - */ - LocalIntegralEnergy(const std::shared_ptr<GFE::LocalDensity<gridDim,RT,DT>>& ld) - : localDensityGFE_(ld) - {} - -private: + */ + template<class Basis, class ... TargetSpaces> + class LocalIntegralEnergy + : public Dune::GFE::LocalEnergy<Basis,TargetSpaces...> + { + using LocalView = typename Basis::LocalView; + using GridView = typename LocalView::GridView; + using DT = typename GridView::Grid::ctype; + using RT = typename GFE::LocalEnergy<Basis,TargetSpaces...>::RT; + + constexpr static int gridDim = GridView::dimension; + + public: + + /** \brief Constructor with a Dune::Elasticity::LocalDensity + */ + LocalIntegralEnergy(const std::shared_ptr<Elasticity::LocalDensity<gridDim,RT,DT> >& ld) + : localDensityElasticity_(ld) + {} + + /** \brief Constructor with a Dune::GFE::LocalDensity + */ + LocalIntegralEnergy(const std::shared_ptr<GFE::LocalDensity<gridDim,RT,DT> >& ld) + : localDensityGFE_(ld) + {} + + private: /** \brief Assemble the energy for a single element */ - RT energy(const typename Basis::LocalView& localView, - const std::vector<TargetSpaces>&... localSolutions) const - { - const auto& element = localView.element(); + RT energy(const typename Basis::LocalView& localView, + const std::vector<TargetSpaces>& ... localSolutions) const + { + const auto& element = localView.element(); + + static_assert(sizeof...(TargetSpaces) > 1, "LocalGeodesicIntegralEnergy needs at least two TargetSpace!"); - static_assert(sizeof...(TargetSpaces) > 1, "LocalGeodesicIntegralEnergy needs at least two TargetSpace!"); + using TargetSpaceDeformation = typename std::tuple_element<0, std::tuple<TargetSpaces...> >::type; + using TargetSpaceRotation = typename std::tuple_element<1, std::tuple<TargetSpaces...> >::type; - using TargetSpaceDeformation = typename std::tuple_element<0, std::tuple<TargetSpaces...> >::type; - using TargetSpaceRotation = typename std::tuple_element<1, std::tuple<TargetSpaces...> >::type; + static_assert( (std::is_same<TargetSpaceDeformation, RigidBodyMotion<RT,gridDim> >::value) + or (std::is_same<TargetSpaceDeformation, RealTuple<RT,gridDim> >::value), "The first TargetSpace of LocalGeodesicIntegralEnergy needs to be RigidBodyMotion or RealTuple!" ); - static_assert( (std::is_same<TargetSpaceDeformation, RigidBodyMotion<RT,gridDim>>::value) - or (std::is_same<TargetSpaceDeformation, RealTuple<RT,gridDim>>::value), "The first TargetSpace of LocalGeodesicIntegralEnergy needs to be RigidBodyMotion or RealTuple!" ); - - const std::vector<TargetSpaceDeformation>& localDeformationConfiguration = std::get<0>(std::forward_as_tuple(localSolutions...)); - const std::vector<TargetSpaceRotation>& localOrientationConfiguration = std::get<1>(std::forward_as_tuple(localSolutions...)); + const std::vector<TargetSpaceDeformation>& localDeformationConfiguration = std::get<0>(std::forward_as_tuple(localSolutions ...)); + const std::vector<TargetSpaceRotation>& localOrientationConfiguration = std::get<1>(std::forward_as_tuple(localSolutions ...)); - using namespace Indices; - // composite Basis: grab the finite element of the first child - const auto& deformationLocalFiniteElement = localView.tree().child(_0,0).finiteElement(); - const auto& orientationLocalFiniteElement = localView.tree().child(_1,0).finiteElement(); + using namespace Indices; + // composite Basis: grab the finite element of the first child + const auto& deformationLocalFiniteElement = localView.tree().child(_0,0).finiteElement(); + const auto& orientationLocalFiniteElement = localView.tree().child(_1,0).finiteElement(); #ifdef PROJECTED_INTERPOLATION - using LocalDeformationGFEFunctionType = GFE::LocalProjectedFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RealTuple<RT,gridDim> >; - using LocalOrientationGFEFunctionType = GFE::LocalProjectedFEFunction<gridDim, DT, decltype(orientationLocalFiniteElement), Rotation<RT,gridDim> >; + using LocalDeformationGFEFunctionType = GFE::LocalProjectedFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RealTuple<RT,gridDim> >; + using LocalOrientationGFEFunctionType = GFE::LocalProjectedFEFunction<gridDim, DT, decltype(orientationLocalFiniteElement), Rotation<RT,gridDim> >; #else - using LocalDeformationGFEFunctionType = LocalGeodesicFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RealTuple<RT,gridDim> >; - using LocalOrientationGFEFunctionType = LocalGeodesicFEFunction<gridDim, DT, decltype(orientationLocalFiniteElement), Rotation<RT,gridDim> >; + using LocalDeformationGFEFunctionType = LocalGeodesicFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RealTuple<RT,gridDim> >; + using LocalOrientationGFEFunctionType = LocalGeodesicFEFunction<gridDim, DT, decltype(orientationLocalFiniteElement), Rotation<RT,gridDim> >; #endif - LocalDeformationGFEFunctionType localDeformationGFEFunction(deformationLocalFiniteElement,localDeformationConfiguration); - LocalOrientationGFEFunctionType localOrientationGFEFunction(orientationLocalFiniteElement,localOrientationConfiguration); + LocalDeformationGFEFunctionType localDeformationGFEFunction(deformationLocalFiniteElement,localDeformationConfiguration); + LocalOrientationGFEFunctionType localOrientationGFEFunction(orientationLocalFiniteElement,localOrientationConfiguration); - RT energy = 0; + RT energy = 0; - int quadOrder = (element.type().isSimplex()) ? deformationLocalFiniteElement.localBasis().order() + int quadOrder = (element.type().isSimplex()) ? deformationLocalFiniteElement.localBasis().order() : deformationLocalFiniteElement.localBasis().order() * gridDim; - const auto& quad = QuadratureRules<DT, gridDim>::rule(element.type(), quadOrder); + const auto& quad = QuadratureRules<DT, gridDim>::rule(element.type(), quadOrder); - for (size_t pt=0; pt<quad.size(); pt++) - { + for (size_t pt=0; pt<quad.size(); pt++) + { // Local position of the quadrature point const FieldVector<DT,gridDim>& quadPos = quad[pt].position(); @@ -115,7 +115,7 @@ private: // The derivative of the deformation defined on the actual element typename LocalDeformationGFEFunctionType::DerivativeType deformationDerivative; for (size_t comp=0; comp<deformationReferenceDerivative.N(); comp++) - jacobianInverseTransposed.mv(deformationReferenceDerivative[comp], deformationDerivative[comp]); + jacobianInverseTransposed.mv(deformationReferenceDerivative[comp], deformationDerivative[comp]); // Integrate the energy density if (localDensityElasticity_) @@ -130,7 +130,7 @@ private: // The derivative of the rotation defined on the actual element typename LocalOrientationGFEFunctionType::DerivativeType orientationDerivative; for (size_t comp=0; comp<orientationReferenceDerivative.N(); comp++) - jacobianInverseTransposed.mv(orientationReferenceDerivative[comp], orientationDerivative[comp]); + jacobianInverseTransposed.mv(orientationReferenceDerivative[comp], orientationDerivative[comp]); energy += weightWithintegrationElement * (*localDensityGFE_)(x, deformationValue, @@ -138,15 +138,15 @@ private: orientationValue, orientationDerivative); } - } + } - return energy; - } + return energy; + } -protected: - const std::shared_ptr<Elasticity::LocalDensity<gridDim,RT,DT>> localDensityElasticity_ = nullptr; - const std::shared_ptr<GFE::LocalDensity<gridDim,RT,DT>> localDensityGFE_ = nullptr; -}; + protected: + const std::shared_ptr<Elasticity::LocalDensity<gridDim,RT,DT> > localDensityElasticity_ = nullptr; + const std::shared_ptr<GFE::LocalDensity<gridDim,RT,DT> > localDensityGFE_ = nullptr; + }; } // namespace Dune::GFE diff --git a/dune/gfe/assemblers/mixedgfeassembler.hh b/dune/gfe/assemblers/mixedgfeassembler.hh index fe7b1f5b..a57b86d7 100644 --- a/dune/gfe/assemblers/mixedgfeassembler.hh +++ b/dune/gfe/assemblers/mixedgfeassembler.hh @@ -15,65 +15,65 @@ template <class Basis, class TargetSpace0, class TargetSpace1> class MixedGFEAssembler { - typedef typename Basis::GridView GridView; - typedef typename GridView::template Codim<0>::template Partition<Dune::Interior_Partition>::Iterator ElementIterator; + typedef typename Basis::GridView GridView; + typedef typename GridView::template Codim<0>::template Partition<Dune::Interior_Partition>::Iterator ElementIterator; - //! Dimension of the grid. - constexpr static int gridDim = GridView::dimension; + //! Dimension of the grid. + constexpr static int gridDim = GridView::dimension; - //! Dimension of a tangent space - constexpr static int blocksize0 = TargetSpace0::TangentVector::dimension; - constexpr static int blocksize1 = TargetSpace1::TangentVector::dimension; + //! Dimension of a tangent space + constexpr static int blocksize0 = TargetSpace0::TangentVector::dimension; + constexpr static int blocksize1 = TargetSpace1::TangentVector::dimension; - //! - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double, blocksize0, blocksize0> > MatrixBlock00; - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double, blocksize0, blocksize1> > MatrixBlock01; - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double, blocksize1, blocksize0> > MatrixBlock10; - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double, blocksize1, blocksize1> > MatrixBlock11; + //! + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double, blocksize0, blocksize0> > MatrixBlock00; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double, blocksize0, blocksize1> > MatrixBlock01; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double, blocksize1, blocksize0> > MatrixBlock10; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double, blocksize1, blocksize1> > MatrixBlock11; public: - typedef Dune::MultiTypeBlockMatrix<Dune::MultiTypeBlockVector<MatrixBlock00,MatrixBlock01>, - Dune::MultiTypeBlockVector<MatrixBlock10,MatrixBlock11> > MatrixType; - const Basis basis_; + typedef Dune::MultiTypeBlockMatrix<Dune::MultiTypeBlockVector<MatrixBlock00,MatrixBlock01>, + Dune::MultiTypeBlockVector<MatrixBlock10,MatrixBlock11> > MatrixType; + const Basis basis_; - MixedLocalGeodesicFEStiffness<Basis, - TargetSpace0, - TargetSpace1>* localStiffness_; + MixedLocalGeodesicFEStiffness<Basis, + TargetSpace0, + TargetSpace1>* localStiffness_; public: - /** \brief Constructor for a given grid */ - MixedGFEAssembler(const Basis& basis, - MixedLocalGeodesicFEStiffness<Basis, TargetSpace0, TargetSpace1>* localStiffness) - : basis_(basis), - localStiffness_(localStiffness) - {} - - /** \brief Assemble the tangent stiffness matrix and the functional gradient together - * - * This is more efficient than computing them separately, because you need the gradient - * anyway to compute the Riemannian Hessian. - */ - virtual void assembleGradientAndHessian(const std::vector<TargetSpace0>& configuration0, - const std::vector<TargetSpace1>& configuration1, - Dune::BlockVector<Dune::FieldVector<double, blocksize0> >& gradient0, - Dune::BlockVector<Dune::FieldVector<double, blocksize1> >& gradient1, - MatrixType& hessian, - bool computeOccupationPattern=true) const; + /** \brief Constructor for a given grid */ + MixedGFEAssembler(const Basis& basis, + MixedLocalGeodesicFEStiffness<Basis, TargetSpace0, TargetSpace1>* localStiffness) + : basis_(basis), + localStiffness_(localStiffness) + {} + + /** \brief Assemble the tangent stiffness matrix and the functional gradient together + * + * This is more efficient than computing them separately, because you need the gradient + * anyway to compute the Riemannian Hessian. + */ + virtual void assembleGradientAndHessian(const std::vector<TargetSpace0>& configuration0, + const std::vector<TargetSpace1>& configuration1, + Dune::BlockVector<Dune::FieldVector<double, blocksize0> >& gradient0, + Dune::BlockVector<Dune::FieldVector<double, blocksize1> >& gradient1, + MatrixType& hessian, + bool computeOccupationPattern=true) const; #if 0 - /** \brief Assemble the gradient */ - virtual void assembleGradient(const std::vector<TargetSpace>& sol, - Dune::BlockVector<Dune::FieldVector<double, blocksize> >& grad) const; + /** \brief Assemble the gradient */ + virtual void assembleGradient(const std::vector<TargetSpace>& sol, + Dune::BlockVector<Dune::FieldVector<double, blocksize> >& grad) const; #endif - /** \brief Compute the energy of a deformation state */ - virtual double computeEnergy(const std::vector<TargetSpace0>& configuration0, - const std::vector<TargetSpace1>& configuration1) const; + /** \brief Compute the energy of a deformation state */ + virtual double computeEnergy(const std::vector<TargetSpace0>& configuration0, + const std::vector<TargetSpace1>& configuration1) const; - //protected: - void getMatrixPattern(Dune::MatrixIndexSet& nb00, - Dune::MatrixIndexSet& nb01, - Dune::MatrixIndexSet& nb10, - Dune::MatrixIndexSet& nb11) const; + //protected: + void getMatrixPattern(Dune::MatrixIndexSet& nb00, + Dune::MatrixIndexSet& nb01, + Dune::MatrixIndexSet& nb10, + Dune::MatrixIndexSet& nb11) const; }; // end class @@ -86,45 +86,45 @@ getMatrixPattern(Dune::MatrixIndexSet& nb00, Dune::MatrixIndexSet& nb10, Dune::MatrixIndexSet& nb11) const { - nb00.resize(basis_.size({0}), basis_.size({0})); - nb01.resize(basis_.size({0}), basis_.size({1})); - nb10.resize(basis_.size({1}), basis_.size({0})); - nb11.resize(basis_.size({1}), basis_.size({1})); + nb00.resize(basis_.size({0}), basis_.size({0})); + nb01.resize(basis_.size({0}), basis_.size({1})); + nb10.resize(basis_.size({1}), basis_.size({0})); + nb11.resize(basis_.size({1}), basis_.size({1})); + + // A view on the FE basis on a single element + auto localView = basis_.localView(); + + // Loop over grid elements + for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) + { + // Bind the local FE basis view to the current element + localView.bind(element); + // Add element stiffness matrix onto the global stiffness matrix + for (size_t i=0; i<localView.size(); i++) + { + // The global index of the i-th local degree of freedom of the element 'e' + auto row = localView.index(i); - // A view on the FE basis on a single element - auto localView = basis_.localView(); + for (size_t j=0; j<localView.size(); j++ ) + { + // The global index of the j-th local degree of freedom of the element 'e' + auto col = localView.index(j); - // Loop over grid elements - for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) - { - // Bind the local FE basis view to the current element - localView.bind(element); - // Add element stiffness matrix onto the global stiffness matrix - for (size_t i=0; i<localView.size(); i++) - { - // The global index of the i-th local degree of freedom of the element 'e' - auto row = localView.index(i); - - for (size_t j=0; j<localView.size(); j++ ) - { - // The global index of the j-th local degree of freedom of the element 'e' - auto col = localView.index(j); - - if (row[0]==0 and col[0]==0) - nb00.add(row[1],col[1]); - if (row[0]==0 and col[0]==1) - nb01.add(row[1],col[1]); - if (row[0]==1 and col[0]==0) - nb10.add(row[1],col[1]); - if (row[0]==1 and col[0]==1) - nb11.add(row[1],col[1]); - - } + if (row[0]==0 and col[0]==0) + nb00.add(row[1],col[1]); + if (row[0]==0 and col[0]==1) + nb01.add(row[1],col[1]); + if (row[0]==1 and col[0]==0) + nb10.add(row[1],col[1]); + if (row[0]==1 and col[0]==1) + nb11.add(row[1],col[1]); - } + } } + } + } template <class Basis, class TargetSpace0, class TargetSpace1> @@ -136,123 +136,123 @@ assembleGradientAndHessian(const std::vector<TargetSpace0>& configuration0, MatrixType& hessian, bool computeOccupationPattern) const { - if (computeOccupationPattern) { + if (computeOccupationPattern) { + + Dune::MatrixIndexSet pattern00; + Dune::MatrixIndexSet pattern01; + Dune::MatrixIndexSet pattern10; + Dune::MatrixIndexSet pattern11; + + getMatrixPattern(pattern00, pattern01, pattern10, pattern11); + + using namespace Dune::TypeTree::Indices; + pattern00.exportIdx(hessian[_0][_0]); + pattern01.exportIdx(hessian[_0][_1]); + pattern10.exportIdx(hessian[_1][_0]); + pattern11.exportIdx(hessian[_1][_1]); + + } + + hessian = 0; + + gradient0.resize(configuration0.size()); + gradient0 = 0; + gradient1.resize(configuration1.size()); + gradient1 = 0; + + // A view on the FE basis on a single element + auto localView = basis_.localView(); + for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) + { + // Bind the local FE basis view to the current element + localView.bind(element); + using namespace Dune::TypeTree::Indices; + + const int nDofs0 = localView.tree().child(_0,0).finiteElement().size(); + const int nDofs1 = localView.tree().child(_1,0).finiteElement().size(); + // This loop reads out the pattern for a local matrix; in each element, we have localView.size() degrees of freedom; from the composite and powerbasis layers + // nDofs0 are the degrees of freedom for *one* subspacebasis of the power basis of the displacement part; + // nDofs1 are the degrees of freedom for *one* subspacebasis of the power basis of the rotational part + // this is why the indices (_0,0) and (_1,0) are used: _0 takes the whole displacement part and _1 the whole rotational part; and 0 the first subspacebasis respectively + // Extract local solution + std::vector<TargetSpace0> localConfiguration0(nDofs0); + std::vector<TargetSpace1> localConfiguration1(nDofs1); + + for (int i=0; i<nDofs0+nDofs1; i++) + { + int localIndexI = 0; + if (i < nDofs0) { + auto& node = localView.tree().child(_0,0); + localIndexI = node.localIndex(i); + } else { + auto& node = localView.tree().child(_1,0); + localIndexI = node.localIndex(i-nDofs0); + } + auto multiIndex = localView.index(localIndexI); + //CompositeBasis number is contained in multiIndex[0], the Subspacebasis is contained in multiIndex[2] + //multiIndex[1] contains the actual index + if (multiIndex[0] == 0) + localConfiguration0[i] = configuration0[multiIndex[1]]; + else if (multiIndex[0] == 1) + localConfiguration1[i-nDofs0] = configuration1[multiIndex[1]]; + } - Dune::MatrixIndexSet pattern00; - Dune::MatrixIndexSet pattern01; - Dune::MatrixIndexSet pattern10; - Dune::MatrixIndexSet pattern11; + std::vector<Dune::FieldVector<double,blocksize0> > localGradient0(nDofs0); + std::vector<Dune::FieldVector<double,blocksize1> > localGradient1(nDofs1); - getMatrixPattern(pattern00, pattern01, pattern10, pattern11); + // setup local matrix and gradient + localStiffness_->assembleGradientAndHessian(localView, + localConfiguration0, localConfiguration1, + localGradient0, localGradient1); - using namespace Dune::TypeTree::Indices; - pattern00.exportIdx(hessian[_0][_0]); - pattern01.exportIdx(hessian[_0][_1]); - pattern10.exportIdx(hessian[_1][_0]); - pattern11.exportIdx(hessian[_1][_1]); + // Add element matrix to global stiffness matrix + for (int i=0; i<nDofs0+nDofs1; i++) + { + int localIndexRow = 0; + if (i < nDofs0) { + auto& node = localView.tree().child(_0,0); + localIndexRow = node.localIndex(i); + } else { + auto& node = localView.tree().child(_1,0); + localIndexRow = node.localIndex(i-nDofs0); + } + + auto row = localView.index(localIndexRow); + + for (int j=0; j<nDofs0+nDofs1; j++ ) + { + int localIndexCol = 0; + if (j < nDofs0) { + auto& node = localView.tree().child(_0,0); + localIndexCol = node.localIndex(j); + } else { + auto& node = localView.tree().child(_1,0); + localIndexCol = node.localIndex(j-nDofs0); + } - } + auto col = localView.index(localIndexCol); - hessian = 0; + if (row[0]==0 and col[0]==0) + hessian[_0][_0][row[1]][col[1]] += localStiffness_->A00_[i][j]; - gradient0.resize(configuration0.size()); - gradient0 = 0; - gradient1.resize(configuration1.size()); - gradient1 = 0; + if (row[0]==0 and col[0]==1) + hessian[_0][_1][row[1]][col[1]] += localStiffness_->A01_[i][j-nDofs0]; - // A view on the FE basis on a single element - auto localView = basis_.localView(); - for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) - { - // Bind the local FE basis view to the current element - localView.bind(element); - using namespace Dune::TypeTree::Indices; - - const int nDofs0 = localView.tree().child(_0,0).finiteElement().size(); - const int nDofs1 = localView.tree().child(_1,0).finiteElement().size(); - // This loop reads out the pattern for a local matrix; in each element, we have localView.size() degrees of freedom; from the composite and powerbasis layers - // nDofs0 are the degrees of freedom for *one* subspacebasis of the power basis of the displacement part; - // nDofs1 are the degrees of freedom for *one* subspacebasis of the power basis of the rotational part - // this is why the indices (_0,0) and (_1,0) are used: _0 takes the whole displacement part and _1 the whole rotational part; and 0 the first subspacebasis respectively - // Extract local solution - std::vector<TargetSpace0> localConfiguration0(nDofs0); - std::vector<TargetSpace1> localConfiguration1(nDofs1); - - for (int i=0; i<nDofs0+nDofs1; i++) - { - int localIndexI = 0; - if (i < nDofs0) { - auto& node = localView.tree().child(_0,0); - localIndexI = node.localIndex(i); - } else { - auto& node = localView.tree().child(_1,0); - localIndexI = node.localIndex(i-nDofs0); - } - auto multiIndex = localView.index(localIndexI); - //CompositeBasis number is contained in multiIndex[0], the Subspacebasis is contained in multiIndex[2] - //multiIndex[1] contains the actual index - if (multiIndex[0] == 0) - localConfiguration0[i] = configuration0[multiIndex[1]]; - else if (multiIndex[0] == 1) - localConfiguration1[i-nDofs0] = configuration1[multiIndex[1]]; - } + if (row[0]==1 and col[0]==0) + hessian[_1][_0][row[1]][col[1]] += localStiffness_->A10_[i-nDofs0][j]; - std::vector<Dune::FieldVector<double,blocksize0> > localGradient0(nDofs0); - std::vector<Dune::FieldVector<double,blocksize1> > localGradient1(nDofs1); - - // setup local matrix and gradient - localStiffness_->assembleGradientAndHessian(localView, - localConfiguration0, localConfiguration1, - localGradient0, localGradient1); - - // Add element matrix to global stiffness matrix - for (int i=0; i<nDofs0+nDofs1; i++) - { - int localIndexRow = 0; - if (i < nDofs0) { - auto& node = localView.tree().child(_0,0); - localIndexRow = node.localIndex(i); - } else { - auto& node = localView.tree().child(_1,0); - localIndexRow = node.localIndex(i-nDofs0); - } - - auto row = localView.index(localIndexRow); - - for (int j=0; j<nDofs0+nDofs1; j++ ) - { - int localIndexCol = 0; - if (j < nDofs0) { - auto& node = localView.tree().child(_0,0); - localIndexCol = node.localIndex(j); - } else { - auto& node = localView.tree().child(_1,0); - localIndexCol = node.localIndex(j-nDofs0); - } - - auto col = localView.index(localIndexCol); - - if (row[0]==0 and col[0]==0) - hessian[_0][_0][row[1]][col[1]] += localStiffness_->A00_[i][j]; - - if (row[0]==0 and col[0]==1) - hessian[_0][_1][row[1]][col[1]] += localStiffness_->A01_[i][j-nDofs0]; - - if (row[0]==1 and col[0]==0) - hessian[_1][_0][row[1]][col[1]] += localStiffness_->A10_[i-nDofs0][j]; - - if (row[0]==1 and col[0]==1) - hessian[_1][_1][row[1]][col[1]] += localStiffness_->A11_[i-nDofs0][j-nDofs0]; - } - - // Add local gradient to global gradient - if (row[0] == 0) - gradient0[row[1]] += localGradient0[i]; - else - gradient1[row[1]] += localGradient1[i-nDofs0]; - } + if (row[0]==1 and col[0]==1) + hessian[_1][_1][row[1]][col[1]] += localStiffness_->A11_[i-nDofs0][j-nDofs0]; + } + // Add local gradient to global gradient + if (row[0] == 0) + gradient0[row[1]] += localGradient0[i]; + else + gradient1[row[1]] += localGradient1[i-nDofs0]; } + + } } #if 0 @@ -261,37 +261,37 @@ void GeodesicFEAssembler<Basis,TargetSpace>:: assembleGradient(const std::vector<TargetSpace>& sol, Dune::BlockVector<Dune::FieldVector<double, blocksize> >& grad) const { - if (sol.size()!=basis_.size()) - DUNE_THROW(Dune::Exception, "Solution vector doesn't match the grid!"); + if (sol.size()!=basis_.size()) + DUNE_THROW(Dune::Exception, "Solution vector doesn't match the grid!"); - grad.resize(sol.size()); - grad = 0; + grad.resize(sol.size()); + grad = 0; - ElementIterator it = basis_.getGridView().template begin<0,Dune::Interior_Partition>(); - ElementIterator endIt = basis_.getGridView().template end<0,Dune::Interior_Partition>(); + ElementIterator it = basis_.getGridView().template begin<0,Dune::Interior_Partition>(); + ElementIterator endIt = basis_.getGridView().template end<0,Dune::Interior_Partition>(); - // Loop over all elements - for (; it!=endIt; ++it) { + // Loop over all elements + for (; it!=endIt; ++it) { - // A 1d grid has two vertices - const int nDofs = basis_.getLocalFiniteElement(*it).localBasis().size(); + // A 1d grid has two vertices + const int nDofs = basis_.getLocalFiniteElement(*it).localBasis().size(); - // Extract local solution - std::vector<TargetSpace> localSolution(nDofs); + // Extract local solution + std::vector<TargetSpace> localSolution(nDofs); - for (int i=0; i<nDofs; i++) - localSolution[i] = sol[basis_.index(*it,i)]; + for (int i=0; i<nDofs; i++) + localSolution[i] = sol[basis_.index(*it,i)]; - // Assemble local gradient - std::vector<Dune::FieldVector<double,blocksize> > localGradient(nDofs); + // Assemble local gradient + std::vector<Dune::FieldVector<double,blocksize> > localGradient(nDofs); - localStiffness_->assembleGradient(*it, basis_.getLocalFiniteElement(*it), localSolution, localGradient); + localStiffness_->assembleGradient(*it, basis_.getLocalFiniteElement(*it), localSolution, localGradient); - // Add to global gradient - for (int i=0; i<nDofs; i++) - grad[basis_.index(*it,i)] += localGradient[i]; + // Add to global gradient + for (int i=0; i<nDofs; i++) + grad[basis_.index(*it,i)] += localGradient[i]; - } + } } #endif @@ -301,61 +301,60 @@ double MixedGFEAssembler<Basis, TargetSpace0, TargetSpace1>:: computeEnergy(const std::vector<TargetSpace0>& configuration0, const std::vector<TargetSpace1>& configuration1) const { - double energy = 0; + double energy = 0; - if (configuration0.size()!=basis_.size({0})) - DUNE_THROW(Dune::Exception, "Configuration vector 0 doesn't match the basis!"); + if (configuration0.size()!=basis_.size({0})) + DUNE_THROW(Dune::Exception, "Configuration vector 0 doesn't match the basis!"); - if (configuration1.size()!=basis_.size({1})) - DUNE_THROW(Dune::Exception, "Configuration vector 1 doesn't match the basis!"); + if (configuration1.size()!=basis_.size({1})) + DUNE_THROW(Dune::Exception, "Configuration vector 1 doesn't match the basis!"); - // A view on the FE basis on a single element - auto localView = basis_.localView(); + // A view on the FE basis on a single element + auto localView = basis_.localView(); - // Loop over all elements - for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) - { - // Bind the local FE basis view to the current element - localView.bind(element); - - // Number of degrees of freedom on this element - using namespace Dune::TypeTree::Indices; - const int nDofs0 = localView.tree().child(_0,0).finiteElement().size(); - const int nDofs1 = localView.tree().child(_1,0).finiteElement().size(); - - std::vector<TargetSpace0> localConfiguration0(nDofs0); - std::vector<TargetSpace1> localConfiguration1(nDofs1); - - for (int i=0; i<nDofs0+nDofs1; i++) - { - int localIndexI = 0; - if (i < nDofs0) { - auto& node = localView.tree().child(_0,0); - localIndexI = node.localIndex(i); - } else { - auto& node = localView.tree().child(_1,0); - localIndexI = node.localIndex(i-nDofs0); - } - - auto multiIndex = localView.index(localIndexI); - - // The CompositeBasis number is contained in multiIndex[0] - // multiIndex[1] contains the actual index - if (multiIndex[0] == 0) - localConfiguration0[i] = configuration0[multiIndex[1]]; - else if (multiIndex[0] == 1) - localConfiguration1[i-nDofs0] = configuration1[multiIndex[1]]; - } + // Loop over all elements + for (const auto& element : elements(basis_.gridView(), Dune::Partitions::interior)) + { + // Bind the local FE basis view to the current element + localView.bind(element); - energy += localStiffness_->energy(localView, - localConfiguration0, - localConfiguration1); + // Number of degrees of freedom on this element + using namespace Dune::TypeTree::Indices; + const int nDofs0 = localView.tree().child(_0,0).finiteElement().size(); + const int nDofs1 = localView.tree().child(_1,0).finiteElement().size(); + std::vector<TargetSpace0> localConfiguration0(nDofs0); + std::vector<TargetSpace1> localConfiguration1(nDofs1); + + for (int i=0; i<nDofs0+nDofs1; i++) + { + int localIndexI = 0; + if (i < nDofs0) { + auto& node = localView.tree().child(_0,0); + localIndexI = node.localIndex(i); + } else { + auto& node = localView.tree().child(_1,0); + localIndexI = node.localIndex(i-nDofs0); + } + + auto multiIndex = localView.index(localIndexI); + + // The CompositeBasis number is contained in multiIndex[0] + // multiIndex[1] contains the actual index + if (multiIndex[0] == 0) + localConfiguration0[i] = configuration0[multiIndex[1]]; + else if (multiIndex[0] == 1) + localConfiguration1[i-nDofs0] = configuration1[multiIndex[1]]; } - return energy; + energy += localStiffness_->energy(localView, + localConfiguration0, + localConfiguration1); + + } + + return energy; } #endif - diff --git a/dune/gfe/assemblers/mixedlocalgeodesicfestiffness.hh b/dune/gfe/assemblers/mixedlocalgeodesicfestiffness.hh index d0245be4..59c946a5 100644 --- a/dune/gfe/assemblers/mixedlocalgeodesicfestiffness.hh +++ b/dune/gfe/assemblers/mixedlocalgeodesicfestiffness.hh @@ -8,66 +8,65 @@ template<class Basis, class DeformationTargetSpace, class OrientationTargetSpace> class MixedLocalGeodesicFEStiffness { - // grid types - typedef typename Basis::GridView GridView; - typedef typename GridView::ctype DT; - typedef typename DeformationTargetSpace::ctype RT; - typedef typename GridView::template Codim<0>::Entity Entity; + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef typename DeformationTargetSpace::ctype RT; + typedef typename GridView::template Codim<0>::Entity Entity; - // some other sizes - constexpr static int gridDim = GridView::dimension; + // some other sizes + constexpr static int gridDim = GridView::dimension; public: - //! Dimension of a tangent space - constexpr static int deformationBlocksize = DeformationTargetSpace::TangentVector::dimension; - constexpr static int orientationBlocksize = OrientationTargetSpace::TangentVector::dimension; + //! Dimension of a tangent space + constexpr static int deformationBlocksize = DeformationTargetSpace::TangentVector::dimension; + constexpr static int orientationBlocksize = OrientationTargetSpace::TangentVector::dimension; - /** \brief Assemble the local stiffness matrix at the current position + /** \brief Assemble the local stiffness matrix at the current position - This default implementation used finite-difference approximations to compute the second derivatives + This default implementation used finite-difference approximations to compute the second derivatives - The formula for the Riemannian Hessian has been taken from Absil, Mahony, Sepulchre: - 'Optimization algorithms on matrix manifolds', page 107. There it says that - \f[ - \langle Hess f(x)[\xi], \eta \rangle - = \frac 12 \frac{d^2}{dt^2} \Big(f(\exp_x(t(\xi + \eta))) - f(\exp_x(t\xi)) - f(\exp_x(t\eta))\Big)\Big|_{t=0}. - \f] - We compute that using a finite difference approximation. + The formula for the Riemannian Hessian has been taken from Absil, Mahony, Sepulchre: + 'Optimization algorithms on matrix manifolds', page 107. There it says that + \f[ + \langle Hess f(x)[\xi], \eta \rangle + = \frac 12 \frac{d^2}{dt^2} \Big(f(\exp_x(t(\xi + \eta))) - f(\exp_x(t\xi)) - f(\exp_x(t\eta))\Big)\Big|_{t=0}. + \f] + We compute that using a finite difference approximation. - */ - virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<DeformationTargetSpace>& localDisplacementConfiguration, - const std::vector<OrientationTargetSpace>& localOrientationConfiguration, - std::vector<typename DeformationTargetSpace::TangentVector>& localDeformationGradient, - std::vector<typename OrientationTargetSpace::TangentVector>& localOrientationGradient) - { - DUNE_THROW(Dune::NotImplemented, "!"); - } + */ + virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, + const std::vector<DeformationTargetSpace>& localDisplacementConfiguration, + const std::vector<OrientationTargetSpace>& localOrientationConfiguration, + std::vector<typename DeformationTargetSpace::TangentVector>& localDeformationGradient, + std::vector<typename OrientationTargetSpace::TangentVector>& localOrientationGradient) + { + DUNE_THROW(Dune::NotImplemented, "!"); + } - /** \brief Compute the energy at the current configuration */ - virtual RT energy (const typename Basis::LocalView& localView, - const std::vector<DeformationTargetSpace>& localDeformationConfiguration, - const std::vector<OrientationTargetSpace>& localOrientationConfiguration) const = 0; + /** \brief Compute the energy at the current configuration */ + virtual RT energy (const typename Basis::LocalView& localView, + const std::vector<DeformationTargetSpace>& localDeformationConfiguration, + const std::vector<OrientationTargetSpace>& localOrientationConfiguration) const = 0; #if 0 - /** \brief Assemble the element gradient of the energy functional + /** \brief Assemble the element gradient of the energy functional - The default implementation in this class uses a finite difference approximation */ - virtual void assembleGradient(const Entity& element, - const LocalFiniteElement& localFiniteElement, - const std::vector<TargetSpace>& solution, - std::vector<typename TargetSpace::TangentVector>& gradient) const; - { - DUNE_THROW(Dune::NotImplemented, "!"); - } + The default implementation in this class uses a finite difference approximation */ + virtual void assembleGradient(const Entity& element, + const LocalFiniteElement& localFiniteElement, + const std::vector<TargetSpace>& solution, + std::vector<typename TargetSpace::TangentVector>& gradient) const; + { + DUNE_THROW(Dune::NotImplemented, "!"); + } #endif - // assembled data - Dune::Matrix<Dune::FieldMatrix<RT, deformationBlocksize, deformationBlocksize> > A00_; - Dune::Matrix<Dune::FieldMatrix<RT, deformationBlocksize, orientationBlocksize> > A01_; - Dune::Matrix<Dune::FieldMatrix<RT, orientationBlocksize, deformationBlocksize> > A10_; - Dune::Matrix<Dune::FieldMatrix<RT, orientationBlocksize, orientationBlocksize> > A11_; + // assembled data + Dune::Matrix<Dune::FieldMatrix<RT, deformationBlocksize, deformationBlocksize> > A00_; + Dune::Matrix<Dune::FieldMatrix<RT, deformationBlocksize, orientationBlocksize> > A01_; + Dune::Matrix<Dune::FieldMatrix<RT, orientationBlocksize, deformationBlocksize> > A10_; + Dune::Matrix<Dune::FieldMatrix<RT, orientationBlocksize, orientationBlocksize> > A11_; }; #endif - diff --git a/dune/gfe/assemblers/mixedlocalgfeadolcstiffness.hh b/dune/gfe/assemblers/mixedlocalgfeadolcstiffness.hh index 73265796..ec81c11b 100644 --- a/dune/gfe/assemblers/mixedlocalgfeadolcstiffness.hh +++ b/dune/gfe/assemblers/mixedlocalgfeadolcstiffness.hh @@ -18,63 +18,63 @@ */ template<class Basis, class TargetSpace0, class TargetSpace1> class MixedLocalGFEADOLCStiffness - : public MixedLocalGeodesicFEStiffness<Basis,TargetSpace0,TargetSpace1> + : public MixedLocalGeodesicFEStiffness<Basis,TargetSpace0,TargetSpace1> { - // grid types - typedef typename Basis::GridView GridView; - typedef typename GridView::ctype DT; - typedef typename TargetSpace0::ctype RT; - typedef typename GridView::template Codim<0>::Entity Entity; + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef typename TargetSpace0::ctype RT; + typedef typename GridView::template Codim<0>::Entity Entity; - // The 'active' target spaces, i.e., the number type is replaced by adouble - typedef typename TargetSpace0::template rebind<adouble>::other ATargetSpace0; - typedef typename TargetSpace1::template rebind<adouble>::other ATargetSpace1; + // The 'active' target spaces, i.e., the number type is replaced by adouble + typedef typename TargetSpace0::template rebind<adouble>::other ATargetSpace0; + typedef typename TargetSpace1::template rebind<adouble>::other ATargetSpace1; - // some other sizes - constexpr static int gridDim = GridView::dimension; + // some other sizes + constexpr static int gridDim = GridView::dimension; public: - //! Dimension of a tangent space - constexpr static int blocksize0 = TargetSpace0::TangentVector::dimension; - constexpr static int blocksize1 = TargetSpace1::TangentVector::dimension; + //! Dimension of a tangent space + constexpr static int blocksize0 = TargetSpace0::TangentVector::dimension; + constexpr static int blocksize1 = TargetSpace1::TangentVector::dimension; - //! Dimension of the embedding space - constexpr static int embeddedBlocksize0 = TargetSpace0::EmbeddedTangentVector::dimension; - constexpr static int embeddedBlocksize1 = TargetSpace1::EmbeddedTangentVector::dimension; + //! Dimension of the embedding space + constexpr static int embeddedBlocksize0 = TargetSpace0::EmbeddedTangentVector::dimension; + constexpr static int embeddedBlocksize1 = TargetSpace1::EmbeddedTangentVector::dimension; - MixedLocalGFEADOLCStiffness(const MixedLocalGeodesicFEStiffness<Basis, ATargetSpace0, - ATargetSpace1>* energy, bool adolcScalarMode = false) + MixedLocalGFEADOLCStiffness(const MixedLocalGeodesicFEStiffness<Basis, ATargetSpace0, + ATargetSpace1>* energy, bool adolcScalarMode = false) : localEnergy_(energy), - adolcScalarMode_(adolcScalarMode) - {} + adolcScalarMode_(adolcScalarMode) + {} - /** \brief Compute the energy at the current configuration */ - virtual RT energy (const typename Basis::LocalView& localView, - const std::vector<TargetSpace0>& localConfiguration0, - const std::vector<TargetSpace1>& localConfiguration1) const; + /** \brief Compute the energy at the current configuration */ + virtual RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace0>& localConfiguration0, + const std::vector<TargetSpace1>& localConfiguration1) const; #if 0 - /** \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 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; #endif - /** \brief Assemble the local stiffness matrix at the current position - - This uses the automatic differentiation toolbox ADOL_C. - */ - virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<TargetSpace0>& localConfiguration0, - const std::vector<TargetSpace1>& localConfiguration1, - std::vector<typename TargetSpace0::TangentVector>& localGradient0, - std::vector<typename TargetSpace1::TangentVector>& localGradient1); - - const MixedLocalGeodesicFEStiffness<Basis, ATargetSpace0, ATargetSpace1>* localEnergy_; - const bool adolcScalarMode_; + /** \brief Assemble the local stiffness matrix at the current position + + This uses the automatic differentiation toolbox ADOL_C. + */ + virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, + const std::vector<TargetSpace0>& localConfiguration0, + const std::vector<TargetSpace1>& localConfiguration1, + std::vector<typename TargetSpace0::TangentVector>& localGradient0, + std::vector<typename TargetSpace1::TangentVector>& localGradient1); + + const MixedLocalGeodesicFEStiffness<Basis, ATargetSpace0, ATargetSpace1>* localEnergy_; + const bool adolcScalarMode_; }; @@ -85,63 +85,63 @@ energy(const typename Basis::LocalView& localView, const std::vector<TargetSpace0>& localConfiguration0, const std::vector<TargetSpace1>& localConfiguration1) const { - int rank = Dune::MPIHelper::getCommunication().rank(); - double pureEnergy; - - std::vector<ATargetSpace0> localAConfiguration0(localConfiguration0.size()); - std::vector<ATargetSpace1> localAConfiguration1(localConfiguration1.size()); - - trace_on(rank); - - 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 ATargetSpace0::CoordinateType> aRaw0(localConfiguration0.size()); - for (size_t i=0; i<localConfiguration0.size(); i++) { - typename TargetSpace0::CoordinateType raw = localConfiguration0[i].globalCoordinates(); - for (size_t j=0; j<raw.size(); j++) - aRaw0[i][j] <<= raw[j]; - localAConfiguration0[i] = aRaw0[i]; // may contain a projection onto M -- needs to be done in adouble - } - - std::vector<typename ATargetSpace1::CoordinateType> aRaw1(localConfiguration1.size()); - for (size_t i=0; i<localConfiguration1.size(); i++) { - typename TargetSpace1::CoordinateType raw = localConfiguration1[i].globalCoordinates(); - for (size_t j=0; j<raw.size(); j++) - aRaw1[i][j] <<= raw[j]; - localAConfiguration1[i] = aRaw1[i]; // may contain a projection onto M -- needs to be done in adouble - } - - using namespace Dune::TypeTree::Indices; - try { - energy = localEnergy_->energy(localView, + int rank = Dune::MPIHelper::getCommunication().rank(); + double pureEnergy; + + std::vector<ATargetSpace0> localAConfiguration0(localConfiguration0.size()); + std::vector<ATargetSpace1> localAConfiguration1(localConfiguration1.size()); + + trace_on(rank); + + 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 ATargetSpace0::CoordinateType> aRaw0(localConfiguration0.size()); + for (size_t i=0; i<localConfiguration0.size(); i++) { + typename TargetSpace0::CoordinateType raw = localConfiguration0[i].globalCoordinates(); + for (size_t j=0; j<raw.size(); j++) + aRaw0[i][j] <<= raw[j]; + localAConfiguration0[i] = aRaw0[i]; // may contain a projection onto M -- needs to be done in adouble + } + + std::vector<typename ATargetSpace1::CoordinateType> aRaw1(localConfiguration1.size()); + for (size_t i=0; i<localConfiguration1.size(); i++) { + typename TargetSpace1::CoordinateType raw = localConfiguration1[i].globalCoordinates(); + for (size_t j=0; j<raw.size(); j++) + aRaw1[i][j] <<= raw[j]; + localAConfiguration1[i] = aRaw1[i]; // may contain a projection onto M -- needs to be done in adouble + } + + using namespace Dune::TypeTree::Indices; + try { + energy = localEnergy_->energy(localView, localAConfiguration0, localAConfiguration1); - } catch (Dune::Exception &e) { - trace_off(); - throw e; - } - energy >>= pureEnergy; - + } catch (Dune::Exception &e) { trace_off(); + throw e; + } + 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"; - cout<<"tay_stack_size "<<tape_stats[TAY_STACK_SIZE]<<"\n"; - cout<<"total number of operations "<<tape_stats[NUM_OPERATIONS]<<"\n"; - // ..... print other tape stats + size_t tape_stats[STAT_SIZE]; + tapestats(1,tape_stats); // reading of tape statistics + cout<<"maxlive "<<tape_stats[NUM_MAX_LIVES]<<"\n"; + cout<<"tay_stack_size "<<tape_stats[TAY_STACK_SIZE]<<"\n"; + cout<<"total number of operations "<<tape_stats[NUM_OPERATIONS]<<"\n"; + // ..... print other tape stats #endif - return pureEnergy; + return pureEnergy; } #if 0 @@ -152,39 +152,39 @@ assembleGradient(const Entity& element, 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]; + // 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]); - } + 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]); + } } #endif @@ -201,344 +201,343 @@ assembleGradientAndHessian(const typename Basis::LocalView& localView, std::vector<typename TargetSpace0::TangentVector>& localGradient0, std::vector<typename TargetSpace1::TangentVector>& localGradient1) { - int rank = Dune::MPIHelper::getCommunication().rank(); - // Tape energy computation. We may not have to do this every time, but it's comparatively cheap. - energy(localView, localConfiguration0, localConfiguration1); - - ///////////////////////////////////////////////////////////////// - // Compute the gradient. It is needed to transform the Hessian - // into the correct coordinates. - ///////////////////////////////////////////////////////////////// - - // Compute the actual gradient - size_t nDofs0 = localConfiguration0.size(); - size_t nDofs1 = localConfiguration1.size(); - size_t nDoubles = nDofs0*embeddedBlocksize0 + nDofs1*embeddedBlocksize1; - - std::vector<double> xp(nDoubles); - int idx=0; - for (size_t i=0; i<localConfiguration0.size(); i++) - for (size_t j=0; j<embeddedBlocksize0; j++) - xp[idx++] = localConfiguration0[i].globalCoordinates()[j]; - - for (size_t i=0; i<localConfiguration1.size(); i++) - for (size_t j=0; j<embeddedBlocksize1; j++) - xp[idx++] = localConfiguration1[i].globalCoordinates()[j]; + int rank = Dune::MPIHelper::getCommunication().rank(); + // Tape energy computation. We may not have to do this every time, but it's comparatively cheap. + energy(localView, localConfiguration0, localConfiguration1); + + ///////////////////////////////////////////////////////////////// + // Compute the gradient. It is needed to transform the Hessian + // into the correct coordinates. + ///////////////////////////////////////////////////////////////// + + // Compute the actual gradient + size_t nDofs0 = localConfiguration0.size(); + size_t nDofs1 = localConfiguration1.size(); + size_t nDoubles = nDofs0*embeddedBlocksize0 + nDofs1*embeddedBlocksize1; + + std::vector<double> xp(nDoubles); + int idx=0; + for (size_t i=0; i<localConfiguration0.size(); i++) + for (size_t j=0; j<embeddedBlocksize0; j++) + xp[idx++] = localConfiguration0[i].globalCoordinates()[j]; + + for (size_t i=0; i<localConfiguration1.size(); i++) + for (size_t j=0; j<embeddedBlocksize1; j++) + xp[idx++] = localConfiguration1[i].globalCoordinates()[j]; // Compute gradient - std::vector<double> g(nDoubles); - gradient(rank,nDoubles,xp.data(),g.data()); // gradient evaluation + std::vector<double> g(nDoubles); + gradient(rank,nDoubles,xp.data(),g.data()); // gradient evaluation - // Copy into Dune type - std::vector<typename TargetSpace0::EmbeddedTangentVector> localEmbeddedGradient0(localConfiguration0.size()); - std::vector<typename TargetSpace1::EmbeddedTangentVector> localEmbeddedGradient1(localConfiguration1.size()); + // Copy into Dune type + std::vector<typename TargetSpace0::EmbeddedTangentVector> localEmbeddedGradient0(localConfiguration0.size()); + std::vector<typename TargetSpace1::EmbeddedTangentVector> localEmbeddedGradient1(localConfiguration1.size()); - idx=0; - for (size_t i=0; i<localConfiguration0.size(); i++) { - for (size_t j=0; j<embeddedBlocksize0; j++) - localEmbeddedGradient0[i][j] = g[idx++]; + idx=0; + for (size_t i=0; i<localConfiguration0.size(); i++) { + for (size_t j=0; j<embeddedBlocksize0; j++) + localEmbeddedGradient0[i][j] = g[idx++]; - // Express gradient in local coordinate system - localConfiguration0[i].orthonormalFrame().mv(localEmbeddedGradient0[i],localGradient0[i]); - } - - for (size_t i=0; i<localConfiguration1.size(); i++) { - for (size_t j=0; j<embeddedBlocksize1; j++) - localEmbeddedGradient1[i][j] = g[idx++]; + // Express gradient in local coordinate system + localConfiguration0[i].orthonormalFrame().mv(localEmbeddedGradient0[i],localGradient0[i]); + } - // Express gradient in local coordinate system - localConfiguration1[i].orthonormalFrame().mv(localEmbeddedGradient1[i],localGradient1[i]); - } + for (size_t i=0; i<localConfiguration1.size(); i++) { + for (size_t j=0; j<embeddedBlocksize1; j++) + localEmbeddedGradient1[i][j] = g[idx++]; - ///////////////////////////////////////////////////////////////// - // Compute Hessian - ///////////////////////////////////////////////////////////////// - - // We compute the Hessian of the energy functional using the ADOL-C system. - // Since ADOL-C does not know about nonlinear spaces, what we get is actually - // the Hessian of a prolongation of the energy functional into the surrounding - // Euclidean space. To obtain the Riemannian Hessian from this we apply the - // formula described in Absil, Mahoney, Trumpf, "An extrinsic look at the Riemannian Hessian". - // This formula consists of two steps: - // 1) Remove all entries of the Hessian pertaining to the normal space of the - // manifold. In the aforementioned paper this is done by projection onto the - // tangent space. Since we want a matrix that is really smaller (but full rank again), - // we can achieve the same effect by multiplying the embedded Hessian from the left - // and from the right by the matrix of orthonormal frames. - // 2) Add a correction involving the Weingarten map. - // - // This works, and is easy to implement using the ADOL-C "hessian" driver. - // However, here we implement a small shortcut. Computing the embedded Hessian and - // multiplying one side by the orthonormal frame is the same as evaluating the Hessian - // (seen as an operator from R^n to R^n) in the directions of the vectors of the - // orthonormal frame. By luck, ADOL-C can compute the evaluations of the Hessian in - // a given direction directly (in fact, this is also how the "hessian" driver works). - // Since there are less frame vectors than the dimension of the embedding space, - // this reinterpretation allows to reduce the number of calls to ADOL-C. - // In my Cosserat shell tests this reduced assembly time by about 10%. - - std::vector<Dune::FieldMatrix<RT,blocksize0,embeddedBlocksize0> > orthonormalFrame0(nDofs0); - - for (size_t i=0; i<nDofs0; i++) - orthonormalFrame0[i] = localConfiguration0[i].orthonormalFrame(); - - std::vector<Dune::FieldMatrix<RT,blocksize1,embeddedBlocksize1> > orthonormalFrame1(nDofs1); - - for (size_t i=0; i<nDofs1; i++) - orthonormalFrame1[i] = localConfiguration1[i].orthonormalFrame(); - - Dune::Matrix<Dune::FieldMatrix<double,blocksize0, embeddedBlocksize0> > embeddedHessian00(nDofs0,nDofs0); - Dune::Matrix<Dune::FieldMatrix<double,blocksize0, embeddedBlocksize1> > embeddedHessian01(nDofs0,nDofs1); - Dune::Matrix<Dune::FieldMatrix<double,blocksize1, embeddedBlocksize0> > embeddedHessian10(nDofs1,nDofs0); - Dune::Matrix<Dune::FieldMatrix<double,blocksize1, embeddedBlocksize1> > embeddedHessian11(nDofs1,nDofs1); - - if(adolcScalarMode_) { - std::vector<double> v(nDoubles); - std::vector<double> w(nDoubles); - - std::fill(v.begin(), v.end(), 0.0); - - size_t nDoubles0 = nDofs0*embeddedBlocksize0; // nDoubles = nDoubles0 + nDoubles1 - size_t nDoubles1 = nDofs1*embeddedBlocksize1; - - std::fill(v.begin(), v.end(), 0.0); - - for (size_t i=0; i<nDofs0 + nDofs1; i++) { - - // Evaluate Hessian in the direction of each vector of the orthonormal frame - if (i < nDofs0) { //Upper half - auto i0 = i; - for (int ii0=0; ii0<blocksize0; ii0++) { - for (size_t k0=0; k0<embeddedBlocksize0; k0++) { - v[i0*embeddedBlocksize0 + k0] = orthonormalFrame0[i0][ii0][k0]; - } - int rc= 3; - MINDEC(rc, hess_vec(rank, nDoubles, xp.data(), v.data(), w.data())); - if (rc < 0) - DUNE_THROW(Dune::Exception, "ADOL-C has returned with error code " << rc << "!"); - - for (size_t j0=0; j0<nDoubles0; j0++) //Upper left - embeddedHessian00[i0][j0/embeddedBlocksize0][ii0][j0%embeddedBlocksize0] = w[j0]; - - for (size_t j1=0; j1<nDoubles1; j1++) //Upper right - embeddedHessian01[i0][j1/embeddedBlocksize1][ii0][j1%embeddedBlocksize1] = w[nDoubles0 + j1]; - - // Make v the null vector again - std::fill(&v[i0*embeddedBlocksize0], &v[(i0+1)*embeddedBlocksize0], 0.0); - } - } else { // i = nDofs0 ... nDofs0 + nDofs1 //lower half - auto i1 = i - nDofs0; - for (int ii1=0; ii1<blocksize1; ii1++) { - for (size_t k1=0; k1<embeddedBlocksize1; k1++) { - v[nDoubles0 + i1*embeddedBlocksize1 + k1] = orthonormalFrame1[i1][ii1][k1]; - } - int rc= 3; - MINDEC(rc, hess_vec(rank, nDoubles, xp.data(), v.data(), w.data())); - if (rc < 0) - DUNE_THROW(Dune::Exception, "ADOL-C has returned with error code " << rc << "!"); - - for (size_t j0=0; j0<nDoubles0; j0++) //Uppper left - embeddedHessian10[i1][j0/embeddedBlocksize0][ii1][j0%embeddedBlocksize0] = w[j0]; - - for (size_t j1=0; j1<nDoubles1; j1++) //Upper right - embeddedHessian11[i1][j1/embeddedBlocksize1][ii1][j1%embeddedBlocksize1] = w[nDoubles0 + j1]; - - // Make v the null vector again - std::fill(&v[nDoubles0 + i1*embeddedBlocksize1], &v[nDoubles0 + (i1+1)*embeddedBlocksize1], 0.0); - } - } + // Express gradient in local coordinate system + localConfiguration1[i].orthonormalFrame().mv(localEmbeddedGradient1[i],localGradient1[i]); + } + + ///////////////////////////////////////////////////////////////// + // Compute Hessian + ///////////////////////////////////////////////////////////////// + + // We compute the Hessian of the energy functional using the ADOL-C system. + // Since ADOL-C does not know about nonlinear spaces, what we get is actually + // the Hessian of a prolongation of the energy functional into the surrounding + // Euclidean space. To obtain the Riemannian Hessian from this we apply the + // formula described in Absil, Mahoney, Trumpf, "An extrinsic look at the Riemannian Hessian". + // This formula consists of two steps: + // 1) Remove all entries of the Hessian pertaining to the normal space of the + // manifold. In the aforementioned paper this is done by projection onto the + // tangent space. Since we want a matrix that is really smaller (but full rank again), + // we can achieve the same effect by multiplying the embedded Hessian from the left + // and from the right by the matrix of orthonormal frames. + // 2) Add a correction involving the Weingarten map. + // + // This works, and is easy to implement using the ADOL-C "hessian" driver. + // However, here we implement a small shortcut. Computing the embedded Hessian and + // multiplying one side by the orthonormal frame is the same as evaluating the Hessian + // (seen as an operator from R^n to R^n) in the directions of the vectors of the + // orthonormal frame. By luck, ADOL-C can compute the evaluations of the Hessian in + // a given direction directly (in fact, this is also how the "hessian" driver works). + // Since there are less frame vectors than the dimension of the embedding space, + // this reinterpretation allows to reduce the number of calls to ADOL-C. + // In my Cosserat shell tests this reduced assembly time by about 10%. + + std::vector<Dune::FieldMatrix<RT,blocksize0,embeddedBlocksize0> > orthonormalFrame0(nDofs0); + + for (size_t i=0; i<nDofs0; i++) + orthonormalFrame0[i] = localConfiguration0[i].orthonormalFrame(); + + std::vector<Dune::FieldMatrix<RT,blocksize1,embeddedBlocksize1> > orthonormalFrame1(nDofs1); + + for (size_t i=0; i<nDofs1; i++) + orthonormalFrame1[i] = localConfiguration1[i].orthonormalFrame(); + + Dune::Matrix<Dune::FieldMatrix<double,blocksize0, embeddedBlocksize0> > embeddedHessian00(nDofs0,nDofs0); + Dune::Matrix<Dune::FieldMatrix<double,blocksize0, embeddedBlocksize1> > embeddedHessian01(nDofs0,nDofs1); + Dune::Matrix<Dune::FieldMatrix<double,blocksize1, embeddedBlocksize0> > embeddedHessian10(nDofs1,nDofs0); + Dune::Matrix<Dune::FieldMatrix<double,blocksize1, embeddedBlocksize1> > embeddedHessian11(nDofs1,nDofs1); + + if(adolcScalarMode_) { + std::vector<double> v(nDoubles); + std::vector<double> w(nDoubles); + + std::fill(v.begin(), v.end(), 0.0); + + size_t nDoubles0 = nDofs0*embeddedBlocksize0; // nDoubles = nDoubles0 + nDoubles1 + size_t nDoubles1 = nDofs1*embeddedBlocksize1; + + std::fill(v.begin(), v.end(), 0.0); + + for (size_t i=0; i<nDofs0 + nDofs1; i++) { + + // Evaluate Hessian in the direction of each vector of the orthonormal frame + if (i < nDofs0) { //Upper half + auto i0 = i; + for (int ii0=0; ii0<blocksize0; ii0++) { + for (size_t k0=0; k0<embeddedBlocksize0; k0++) { + v[i0*embeddedBlocksize0 + k0] = orthonormalFrame0[i0][ii0][k0]; + } + int rc= 3; + MINDEC(rc, hess_vec(rank, nDoubles, xp.data(), v.data(), w.data())); + if (rc < 0) + DUNE_THROW(Dune::Exception, "ADOL-C has returned with error code " << rc << "!"); + + for (size_t j0=0; j0<nDoubles0; j0++) //Upper left + embeddedHessian00[i0][j0/embeddedBlocksize0][ii0][j0%embeddedBlocksize0] = w[j0]; + + for (size_t j1=0; j1<nDoubles1; j1++) //Upper right + embeddedHessian01[i0][j1/embeddedBlocksize1][ii0][j1%embeddedBlocksize1] = w[nDoubles0 + j1]; + + // Make v the null vector again + std::fill(&v[i0*embeddedBlocksize0], &v[(i0+1)*embeddedBlocksize0], 0.0); } - - } else { //ADOL-C vector mode} - int n = nDoubles; - int nDirections = nDofs0 * blocksize0 + nDofs1 * blocksize1; - double* tangent[nDoubles]; - for(size_t i=0; i<nDoubles; i++) - tangent[i] = (double*)malloc(nDirections*sizeof(double)); - - double* rawHessian[nDoubles]; - for(size_t i=0; i<nDoubles; i++) - rawHessian[i] = (double*)malloc(nDirections*sizeof(double)); - - // Initialize directions field with zeros - for (int j=0; j<nDirections; j++) - for (int i=0; i<n; i++) - tangent[i][j] = 0.0; - - for (size_t j=0; j<nDofs0*blocksize0; j++) - for (int i=0; i<embeddedBlocksize0; i++) - tangent[(j/blocksize0)*embeddedBlocksize0+i][j] = orthonormalFrame0[j/blocksize0][j%blocksize0][i]; - - for (size_t j=0; j<nDofs1*blocksize1; j++) - for (int i=0; i<embeddedBlocksize1; i++) - tangent[nDofs0*embeddedBlocksize0 + (j/blocksize1)*embeddedBlocksize1+i][nDofs0*blocksize0 + j] = orthonormalFrame1[j/blocksize1][j%blocksize1][i]; - - hess_mat(rank,nDoubles,nDirections,xp.data(),tangent,rawHessian); - - // Copy Hessian into Dune data type - size_t offset0 = nDofs0*embeddedBlocksize0; - size_t offset1 = nDofs0*blocksize0; - - // upper left block - for(size_t i=0; i<nDofs0*embeddedBlocksize0; i++) - for (size_t j=0; j<nDofs0*blocksize0; j++) - embeddedHessian00[j/blocksize0][i/embeddedBlocksize0][j%blocksize0][i%embeddedBlocksize0] = rawHessian[i][j]; - - // upper right block - for(size_t i=0; i<nDofs1*embeddedBlocksize1; i++) - for (size_t j=0; j<nDofs0*blocksize0; j++) - embeddedHessian01[j/blocksize0][i/embeddedBlocksize1][j%blocksize0][i%embeddedBlocksize1] = rawHessian[offset0+i][j]; - - // lower left block - for(size_t i=0; i<nDofs0*embeddedBlocksize0; i++) - for (size_t j=0; j<nDofs1*blocksize1; j++) - embeddedHessian10[j/blocksize1][i/embeddedBlocksize0][j%blocksize1][i%embeddedBlocksize0] = rawHessian[i][offset1+j]; - - // lower right block - for(size_t i=0; i<nDofs1*embeddedBlocksize1; i++) - for (size_t j=0; j<nDofs1*blocksize1; j++) - embeddedHessian11[j/blocksize1][i/embeddedBlocksize1][j%blocksize1][i%embeddedBlocksize1] = rawHessian[offset0+i][offset1+j]; - - for(size_t i=0; i<nDoubles; i++) { - free(rawHessian[i]); - free(tangent[i]); + } else { // i = nDofs0 ... nDofs0 + nDofs1 //lower half + auto i1 = i - nDofs0; + for (int ii1=0; ii1<blocksize1; ii1++) { + for (size_t k1=0; k1<embeddedBlocksize1; k1++) { + v[nDoubles0 + i1*embeddedBlocksize1 + k1] = orthonormalFrame1[i1][ii1][k1]; + } + int rc= 3; + MINDEC(rc, hess_vec(rank, nDoubles, xp.data(), v.data(), w.data())); + if (rc < 0) + DUNE_THROW(Dune::Exception, "ADOL-C has returned with error code " << rc << "!"); + + for (size_t j0=0; j0<nDoubles0; j0++) //Uppper left + embeddedHessian10[i1][j0/embeddedBlocksize0][ii1][j0%embeddedBlocksize0] = w[j0]; + + for (size_t j1=0; j1<nDoubles1; j1++) //Upper right + embeddedHessian11[i1][j1/embeddedBlocksize1][ii1][j1%embeddedBlocksize1] = w[nDoubles0 + j1]; + + // Make v the null vector again + std::fill(&v[nDoubles0 + i1*embeddedBlocksize1], &v[nDoubles0 + (i1+1)*embeddedBlocksize1], 0.0); } + } } - // 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". + } else { //ADOL-C vector mode} + int n = nDoubles; + int nDirections = nDofs0 * blocksize0 + nDofs1 * blocksize1; + double* tangent[nDoubles]; + for(size_t i=0; i<nDoubles; i++) + tangent[i] = (double*)malloc(nDirections*sizeof(double)); + + double* rawHessian[nDoubles]; + for(size_t i=0; i<nDoubles; i++) + rawHessian[i] = (double*)malloc(nDirections*sizeof(double)); + + // Initialize directions field with zeros + for (int j=0; j<nDirections; j++) + for (int i=0; i<n; i++) + tangent[i][j] = 0.0; + + for (size_t j=0; j<nDofs0*blocksize0; j++) + for (int i=0; i<embeddedBlocksize0; i++) + tangent[(j/blocksize0)*embeddedBlocksize0+i][j] = orthonormalFrame0[j/blocksize0][j%blocksize0][i]; + + for (size_t j=0; j<nDofs1*blocksize1; j++) + for (int i=0; i<embeddedBlocksize1; i++) + tangent[nDofs0*embeddedBlocksize0 + (j/blocksize1)*embeddedBlocksize1+i][nDofs0*blocksize0 + j] = orthonormalFrame1[j/blocksize1][j%blocksize1][i]; + + hess_mat(rank,nDoubles,nDirections,xp.data(),tangent,rawHessian); + + // Copy Hessian into Dune data type + size_t offset0 = nDofs0*embeddedBlocksize0; + size_t offset1 = nDofs0*blocksize0; + + // upper left block + for(size_t i=0; i<nDofs0*embeddedBlocksize0; i++) + for (size_t j=0; j<nDofs0*blocksize0; j++) + embeddedHessian00[j/blocksize0][i/embeddedBlocksize0][j%blocksize0][i%embeddedBlocksize0] = rawHessian[i][j]; + + // upper right block + for(size_t i=0; i<nDofs1*embeddedBlocksize1; i++) + for (size_t j=0; j<nDofs0*blocksize0; j++) + embeddedHessian01[j/blocksize0][i/embeddedBlocksize1][j%blocksize0][i%embeddedBlocksize1] = rawHessian[offset0+i][j]; + + // lower left block + for(size_t i=0; i<nDofs0*embeddedBlocksize0; i++) + for (size_t j=0; j<nDofs1*blocksize1; j++) + embeddedHessian10[j/blocksize1][i/embeddedBlocksize0][j%blocksize1][i%embeddedBlocksize0] = rawHessian[i][offset1+j]; + + // lower right block + for(size_t i=0; i<nDofs1*embeddedBlocksize1; i++) + for (size_t j=0; j<nDofs1*blocksize1; j++) + embeddedHessian11[j/blocksize1][i/embeddedBlocksize1][j%blocksize1][i%embeddedBlocksize1] = rawHessian[offset0+i][offset1+j]; + + for(size_t i=0; i<nDoubles; i++) { + free(rawHessian[i]); + free(tangent[i]); + } + } - this->A00_.setSize(nDofs0,nDofs0); + // 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". - for (size_t col=0; col<nDofs0; col++) { + this->A00_.setSize(nDofs0,nDofs0); - for (size_t subCol=0; subCol<blocksize0; subCol++) { + for (size_t col=0; col<nDofs0; col++) { - typename TargetSpace0::EmbeddedTangentVector z = orthonormalFrame0[col][subCol]; + for (size_t subCol=0; subCol<blocksize0; subCol++) { - // P_x \partial^2 f z - for (size_t row=0; row<nDofs0; row++) { - typename TargetSpace0::TangentVector semiEmbeddedProduct; - embeddedHessian00[row][col].mv(z,semiEmbeddedProduct); + typename TargetSpace0::EmbeddedTangentVector z = orthonormalFrame0[col][subCol]; - for (int subRow=0; subRow<blocksize0; subRow++) - this->A00_[row][col][subRow][subCol] = semiEmbeddedProduct[subRow]; - } + // P_x \partial^2 f z + for (size_t row=0; row<nDofs0; row++) { + typename TargetSpace0::TangentVector semiEmbeddedProduct; + embeddedHessian00[row][col].mv(z,semiEmbeddedProduct); - } + for (int subRow=0; subRow<blocksize0; subRow++) + this->A00_[row][col][subRow][subCol] = semiEmbeddedProduct[subRow]; + } } - this->A01_.setSize(nDofs0,nDofs1); + } - for (size_t col=0; col<nDofs1; col++) { + this->A01_.setSize(nDofs0,nDofs1); - for (size_t subCol=0; subCol<blocksize1; subCol++) { + for (size_t col=0; col<nDofs1; col++) { - typename TargetSpace1::EmbeddedTangentVector z = orthonormalFrame1[col][subCol]; + for (size_t subCol=0; subCol<blocksize1; subCol++) { - // P_x \partial^2 f z - for (size_t row=0; row<nDofs0; row++) { - typename TargetSpace0::TangentVector semiEmbeddedProduct; - embeddedHessian01[row][col].mv(z,semiEmbeddedProduct); + typename TargetSpace1::EmbeddedTangentVector z = orthonormalFrame1[col][subCol]; - for (int subRow=0; subRow<blocksize0; subRow++) - this->A01_[row][col][subRow][subCol] = semiEmbeddedProduct[subRow]; - } + // P_x \partial^2 f z + for (size_t row=0; row<nDofs0; row++) { + typename TargetSpace0::TangentVector semiEmbeddedProduct; + embeddedHessian01[row][col].mv(z,semiEmbeddedProduct); - } + for (int subRow=0; subRow<blocksize0; subRow++) + this->A01_[row][col][subRow][subCol] = semiEmbeddedProduct[subRow]; + } } - this->A10_.setSize(nDofs1,nDofs0); + } - for (size_t col=0; col<nDofs0; col++) { + this->A10_.setSize(nDofs1,nDofs0); - for (size_t subCol=0; subCol<blocksize0; subCol++) { + for (size_t col=0; col<nDofs0; col++) { - typename TargetSpace0::EmbeddedTangentVector z = orthonormalFrame0[col][subCol]; + for (size_t subCol=0; subCol<blocksize0; subCol++) { - // P_x \partial^2 f z - for (size_t row=0; row<nDofs1; row++) { - typename TargetSpace1::TangentVector semiEmbeddedProduct; - embeddedHessian10[row][col].mv(z,semiEmbeddedProduct); + typename TargetSpace0::EmbeddedTangentVector z = orthonormalFrame0[col][subCol]; - for (int subRow=0; subRow<blocksize1; subRow++) - this->A10_[row][col][subRow][subCol] = semiEmbeddedProduct[subRow]; - } + // P_x \partial^2 f z + for (size_t row=0; row<nDofs1; row++) { + typename TargetSpace1::TangentVector semiEmbeddedProduct; + embeddedHessian10[row][col].mv(z,semiEmbeddedProduct); - } + for (int subRow=0; subRow<blocksize1; subRow++) + this->A10_[row][col][subRow][subCol] = semiEmbeddedProduct[subRow]; + } } - this->A11_.setSize(nDofs1,nDofs1); + } - for (size_t col=0; col<nDofs1; col++) { + this->A11_.setSize(nDofs1,nDofs1); - for (size_t subCol=0; subCol<blocksize1; subCol++) { + for (size_t col=0; col<nDofs1; col++) { - typename TargetSpace1::EmbeddedTangentVector z = orthonormalFrame1[col][subCol]; + for (size_t subCol=0; subCol<blocksize1; subCol++) { - // P_x \partial^2 f z - for (size_t row=0; row<nDofs1; row++) { - typename TargetSpace1::TangentVector semiEmbeddedProduct; - embeddedHessian11[row][col].mv(z,semiEmbeddedProduct); + typename TargetSpace1::EmbeddedTangentVector z = orthonormalFrame1[col][subCol]; - for (int subRow=0; subRow<blocksize1; subRow++) - this->A11_[row][col][subRow][subCol] = semiEmbeddedProduct[subRow]; - } + // P_x \partial^2 f z + for (size_t row=0; row<nDofs1; row++) { + typename TargetSpace1::TangentVector semiEmbeddedProduct; + embeddedHessian11[row][col].mv(z,semiEmbeddedProduct); - } + for (int subRow=0; subRow<blocksize1; subRow++) + this->A11_[row][col][subRow][subCol] = semiEmbeddedProduct[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 TargetSpace0::EmbeddedTangentVector> projectedGradient0(nDofs0); - for (size_t i=0; i<nDofs0; i++) - projectedGradient0[i] = localConfiguration0[i].projectOntoNormalSpace(localEmbeddedGradient0[i]); + ////////////////////////////////////////////////////////////////////////////////////// + // Further correction due to non-planar configuration space + // + \mathfrak{A}_x(z,P^\orth_x \partial f) + ////////////////////////////////////////////////////////////////////////////////////// - std::vector<typename TargetSpace1::EmbeddedTangentVector> projectedGradient1(nDofs1); - for (size_t i=0; i<nDofs1; i++) - projectedGradient1[i] = localConfiguration1[i].projectOntoNormalSpace(localEmbeddedGradient1[i]); + // Project embedded gradient onto normal space + std::vector<typename TargetSpace0::EmbeddedTangentVector> projectedGradient0(nDofs0); + for (size_t i=0; i<nDofs0; i++) + projectedGradient0[i] = localConfiguration0[i].projectOntoNormalSpace(localEmbeddedGradient0[i]); - // The Weingarten map has only diagonal entries - for (size_t row=0; row<nDofs0; row++) { + std::vector<typename TargetSpace1::EmbeddedTangentVector> projectedGradient1(nDofs1); + for (size_t i=0; i<nDofs1; i++) + projectedGradient1[i] = localConfiguration1[i].projectOntoNormalSpace(localEmbeddedGradient1[i]); - for (size_t subRow=0; subRow<blocksize0; subRow++) { + // The Weingarten map has only diagonal entries + for (size_t row=0; row<nDofs0; row++) { - typename TargetSpace0::EmbeddedTangentVector z = orthonormalFrame0[row][subRow]; - typename TargetSpace0::EmbeddedTangentVector tmp1 = localConfiguration0[row].weingarten(z,projectedGradient0[row]); + for (size_t subRow=0; subRow<blocksize0; subRow++) { - typename TargetSpace0::TangentVector tmp2; - orthonormalFrame0[row].mv(tmp1,tmp2); + typename TargetSpace0::EmbeddedTangentVector z = orthonormalFrame0[row][subRow]; + typename TargetSpace0::EmbeddedTangentVector tmp1 = localConfiguration0[row].weingarten(z,projectedGradient0[row]); - this->A00_[row][row][subRow] += tmp2; - } + typename TargetSpace0::TangentVector tmp2; + orthonormalFrame0[row].mv(tmp1,tmp2); + this->A00_[row][row][subRow] += tmp2; } - for (size_t row=0; row<nDofs1; row++) { + } - for (size_t subRow=0; subRow<blocksize1; subRow++) { + for (size_t row=0; row<nDofs1; row++) { - typename TargetSpace1::EmbeddedTangentVector z = orthonormalFrame1[row][subRow]; - typename TargetSpace1::EmbeddedTangentVector tmp1 = localConfiguration1[row].weingarten(z,projectedGradient1[row]); + for (size_t subRow=0; subRow<blocksize1; subRow++) { - typename TargetSpace1::TangentVector tmp2; - orthonormalFrame1[row].mv(tmp1,tmp2); + typename TargetSpace1::EmbeddedTangentVector z = orthonormalFrame1[row][subRow]; + typename TargetSpace1::EmbeddedTangentVector tmp1 = localConfiguration1[row].weingarten(z,projectedGradient1[row]); - this->A11_[row][row][subRow] += tmp2; - } + typename TargetSpace1::TangentVector tmp2; + orthonormalFrame1[row].mv(tmp1,tmp2); + this->A11_[row][row][subRow] += tmp2; } + + } } #endif - diff --git a/dune/gfe/assemblers/nonplanarcosseratshellenergy.hh b/dune/gfe/assemblers/nonplanarcosseratshellenergy.hh index bd5385d3..fd1a9eb0 100644 --- a/dune/gfe/assemblers/nonplanarcosseratshellenergy.hh +++ b/dune/gfe/assemblers/nonplanarcosseratshellenergy.hh @@ -40,8 +40,8 @@ template<class Basis, int dim, class field_type, class StressFreeStateGridFuncti class NonplanarCosseratShellEnergy : public Dune::GFE::LocalEnergy<Basis,RigidBodyMotion<field_type,dim> >, public MixedLocalGeodesicFEStiffness<Basis, - RealTuple<field_type,dim>, - Rotation<field_type,dim> > + RealTuple<field_type,dim>, + Rotation<field_type,dim> > { // grid types typedef typename Basis::GridView GridView; @@ -65,7 +65,7 @@ public: const BoundaryPatch<GridView>* neumannBoundary, const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> neumannFunction, const std::function<Dune::FieldVector<double,3>(Dune::FieldVector<double,dimworld>)> volumeLoad) - : stressFreeStateGridFunction_(stressFreeStateGridFunction), + : stressFreeStateGridFunction_(stressFreeStateGridFunction), neumannBoundary_(neumannBoundary), neumannFunction_(neumannFunction), volumeLoad_(volumeLoad) @@ -87,7 +87,7 @@ public: b1_ = parameters.template get<double>("b1"); b2_ = parameters.template get<double>("b2"); b3_ = parameters.template get<double>("b3"); - + // Indicator to use the alternative energy W_Coss from Birsan 2021: useAlternativeEnergyWCoss_ = parameters.template get<bool>("useAlternativeEnergyWCoss", false); } @@ -96,7 +96,7 @@ public: RT energy (const typename Basis::LocalView& localView, const std::vector<TargetSpace>& localSolution) const; -/** \brief Assemble the energy for a single element */ + /** \brief Assemble the energy for a single element */ RT energy (const typename Basis::LocalView& localView, const std::vector<RealTuple<field_type,dim> >& localDisplacementConfiguration, const std::vector<Rotation<field_type,dim> >& localOrientationConfiguration) const override; @@ -104,7 +104,7 @@ public: /* Sources: Birsan 2019: Derivation of a refined six-parameter shell model, equation (111) Birsan 2021: Alternative derivation of the higher-order constitudtive model for six-parameter elastic shells, equations (119) and (126) - */ + */ RT W_Coss(const Dune::FieldMatrix<field_type,3,3>& S, const Dune::FieldMatrix<double,3,3>& a, const Dune::FieldVector<double,3>& n0) const { return W_Coss_mixt(S,S,a,n0); @@ -129,8 +129,8 @@ public: RT W_mixt(const Dune::FieldMatrix<field_type,3,3>& S, const Dune::FieldMatrix<field_type,3,3>& T) const { return mu_ * Dune::GFE::frobeniusProduct(Dune::GFE::sym(S), Dune::GFE::sym(T)) - + mu_c_ * Dune::GFE::frobeniusProduct(Dune::GFE::skew(S), Dune::GFE::skew(T)) - + lambda_ * mu_ / (lambda_ + 2*mu_) * Dune::GFE::trace(S) * Dune::GFE::trace(T); + + mu_c_ * Dune::GFE::frobeniusProduct(Dune::GFE::skew(S), Dune::GFE::skew(T)) + + lambda_ * mu_ / (lambda_ + 2*mu_) * Dune::GFE::trace(S) * Dune::GFE::trace(T); } RT W_mp(const Dune::FieldMatrix<field_type,3,3>& S) const @@ -142,7 +142,7 @@ public: RT W_curv(const Dune::FieldMatrix<field_type,3,3>& S) const { return mu_ * L_c_ * L_c_ * (b1_ * Dune::GFE::dev(Dune::GFE::sym(S)).frobenius_norm2() - + b2_ * Dune::GFE::skew(S).frobenius_norm2() + b3_ * Dune::GFE::traceSquared(S)); + + b2_ * Dune::GFE::skew(S).frobenius_norm2() + b3_ * Dune::GFE::traceSquared(S)); } #if HAVE_DUNE_GMSH4 @@ -208,11 +208,11 @@ energy(const typename Basis::LocalView& localView, // Construct a curved geometry of this element of the Cosserat shell in its stress-free state // The variable local holds the local coordinates in the reference element // and localGeometry.global maps them to the world coordinates - Dune::CurvedGeometry<DT, gridDim, dimworld, Dune::CurvedGeometryTraits<DT, Dune::LagrangeLFECache<DT,DT,gridDim>>> geometry(referenceElement(element), - [this,element](const auto& local) { - auto localGridFunction = localFunction(*stressFreeStateGridFunction_); - localGridFunction.bind(element); - return localGridFunction(local); + Dune::CurvedGeometry<DT, gridDim, dimworld, Dune::CurvedGeometryTraits<DT, Dune::LagrangeLFECache<DT,DT,gridDim> > > geometry(referenceElement(element), + [this,element](const auto& local) { + auto localGridFunction = localFunction(*stressFreeStateGridFunction_); + localGridFunction.bind(element); + return localGridFunction(local); }, getOrder(stressFreeStateGridFunction_)); #else // When using element.geometry(), the geometry of the element is flat @@ -313,7 +313,7 @@ energy(const typename Basis::LocalView& localView, for (int j = 0; j < dimworld; j++) b[i][j] = grad_s_n[i][j]; #endif - + // Mean curvatue auto H = 0.5 * Dune::GFE::trace(b); @@ -359,20 +359,20 @@ energy(const typename Basis::LocalView& localView, field_type energyDensity = 0; if (useAlternativeEnergyWCoss_) { energyDensity += (thickness_ - K*Dune::power(thickness_,3) / 12.0) * W_Coss(Ee, a, aContravariant[2]) - + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_Coss(Ee*b + c*Ke, a, aContravariant[2]) - + Dune::power(thickness_,3) / 6.0 * W_Coss_mixt(Ee, c*Ke*b - 2*H*c*Ke, a, aContravariant[2]) - + Dune::power(thickness_,5) / 80.0 * W_Coss( (Ee*b + c*Ke)*b, a, aContravariant[2]); + + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_Coss(Ee*b + c*Ke, a, aContravariant[2]) + + Dune::power(thickness_,3) / 6.0 * W_Coss_mixt(Ee, c*Ke*b - 2*H*c*Ke, a, aContravariant[2]) + + Dune::power(thickness_,5) / 80.0 * W_Coss( (Ee*b + c*Ke)*b, a, aContravariant[2]); } else { energyDensity += (thickness_ - K*Dune::power(thickness_,3) / 12.0) * W_m(Ee) - + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_m(Ee*b + c*Ke) - + Dune::power(thickness_,3) / 6.0 * W_mixt(Ee, c*Ke*b - 2*H*c*Ke) - + Dune::power(thickness_,5) / 80.0 * W_mp( (Ee*b + c*Ke)*b); + + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_m(Ee*b + c*Ke) + + Dune::power(thickness_,3) / 6.0 * W_mixt(Ee, c*Ke*b - 2*H*c*Ke) + + Dune::power(thickness_,5) / 80.0 * W_mp( (Ee*b + c*Ke)*b); } // Add the bending energy density energyDensity += (thickness_ - K*Dune::power(thickness_,3) / 12.0) * W_curv(Ke) - + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_curv(Ke*b) - + Dune::power(thickness_,5) / 80.0 * W_curv(Ke*b*b); + + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_curv(Ke*b) + + Dune::power(thickness_,5) / 80.0 * W_curv(Ke*b*b); // Add energy density energy += quad[pt].weight() * integrationElement * energyDensity; @@ -449,11 +449,11 @@ energy(const typename Basis::LocalView& localView, // Construct a curved geometry of this element of the Cosserat shell in its stress-free state // The variable local holds the local coordinates in the reference element // and localGeometry.global maps them to the world coordinates - Dune::CurvedGeometry<DT, gridDim, dimworld, Dune::CurvedGeometryTraits<DT, Dune::LagrangeLFECache<DT,DT,gridDim>>> geometry(referenceElement(element), - [this,element](const auto& local) { - auto localGridFunction = localFunction(*stressFreeStateGridFunction_); - localGridFunction.bind(element); - return localGridFunction(local); + Dune::CurvedGeometry<DT, gridDim, dimworld, Dune::CurvedGeometryTraits<DT, Dune::LagrangeLFECache<DT,DT,gridDim> > > geometry(referenceElement(element), + [this,element](const auto& local) { + auto localGridFunction = localFunction(*stressFreeStateGridFunction_); + localGridFunction.bind(element); + return localGridFunction(local); }, getOrder(stressFreeStateGridFunction_)); #else // When using element.geometry(), the geometry of the element is flat @@ -558,7 +558,7 @@ energy(const typename Basis::LocalView& localView, b[i][j] = grad_s_n[i][j]; #endif b *= (-1); - + // Mean curvatue auto H = 0.5 * Dune::GFE::trace(b); @@ -604,19 +604,19 @@ energy(const typename Basis::LocalView& localView, field_type energyDensity = 0; if (useAlternativeEnergyWCoss_) { energyDensity += (thickness_ - K*Dune::power(thickness_,3) / 12.0) * W_Coss(Ee, a, aContravariant[2]) - + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_Coss(Ee*b + c*Ke, a, aContravariant[2]) - + Dune::power(thickness_,3) / 6.0 * W_Coss_mixt(Ee, c*Ke*b - 2*H*c*Ke, a, aContravariant[2]) - + Dune::power(thickness_,5) / 80.0 * W_Coss( (Ee*b + c*Ke)*b, a, aContravariant[2]); + + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_Coss(Ee*b + c*Ke, a, aContravariant[2]) + + Dune::power(thickness_,3) / 6.0 * W_Coss_mixt(Ee, c*Ke*b - 2*H*c*Ke, a, aContravariant[2]) + + Dune::power(thickness_,5) / 80.0 * W_Coss( (Ee*b + c*Ke)*b, a, aContravariant[2]); } else { energyDensity += (thickness_ - K*Dune::power(thickness_,3) / 12.0) * W_m(Ee) - + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_m(Ee*b + c*Ke) - + Dune::power(thickness_,3) / 6.0 * W_mixt(Ee, c*Ke*b - 2*H*c*Ke) - + Dune::power(thickness_,5) / 80.0 * W_mp( (Ee*b + c*Ke)*b); + + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_m(Ee*b + c*Ke) + + Dune::power(thickness_,3) / 6.0 * W_mixt(Ee, c*Ke*b - 2*H*c*Ke) + + Dune::power(thickness_,5) / 80.0 * W_mp( (Ee*b + c*Ke)*b); } energyDensity += (thickness_ - K*Dune::power(thickness_,3) / 12.0) * W_curv(Ke) - + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_curv(Ke*b) - + Dune::power(thickness_,5) / 80.0 * W_curv(Ke*b*b); + + (Dune::power(thickness_,3) / 12.0 - K * Dune::power(thickness_,5) / 80.0)*W_curv(Ke*b) + + Dune::power(thickness_,5) / 80.0 * W_curv(Ke*b*b); // Add energy density energy += quad[pt].weight() * integrationElement * energyDensity; @@ -674,4 +674,3 @@ energy(const typename Basis::LocalView& localView, } #endif //#ifndef DUNE_GFE_NONPLANARCOSSERATSHELLENERGY_HH - diff --git a/dune/gfe/assemblers/simofoxenergy.hh b/dune/gfe/assemblers/simofoxenergy.hh index 80fbcf21..55081aff 100644 --- a/dune/gfe/assemblers/simofoxenergy.hh +++ b/dune/gfe/assemblers/simofoxenergy.hh @@ -25,7 +25,7 @@ namespace Dune::GFE { class LocalFiniteElementFactory { public: static auto get(const typename Basis::LocalView &localView, std::integral_constant<std::size_t, i> iType) - -> decltype(localView.tree().child(iType, 0).finiteElement()) { + -> decltype(localView.tree().child(iType, 0).finiteElement()) { return localView.tree().child(iType, 0).finiteElement(); } }; @@ -52,10 +52,10 @@ namespace Dune::GFE { */ template <class Basis, template <int, typename, typename, typename> typename LocalFEFunction, typename field_type = double> class SimoFoxEnergyLocalStiffness - : public Dune::GFE::LocalEnergy<Basis, RealTuple<field_type, 3>, - UnitVector<field_type, 3>>, // inheritance to allow usage with LocalGeodesicFEADOLCStiffness - public MixedLocalGeodesicFEStiffness<Basis, RealTuple<field_type, 3>, - UnitVector<field_type, 3>> // inheritance to allow usage with MixedGFEAssembler + : public Dune::GFE::LocalEnergy<Basis, RealTuple<field_type, 3>, + UnitVector<field_type, 3> >, // inheritance to allow usage with LocalGeodesicFEADOLCStiffness + public MixedLocalGeodesicFEStiffness<Basis, RealTuple<field_type, 3>, + UnitVector<field_type, 3> > // inheritance to allow usage with MixedGFEAssembler { // grid types typedef typename Basis::GridView GridView; @@ -69,19 +69,19 @@ namespace Dune::GFE { // The local finite element type used for midsurface position and displacement interpolation using MidSurfaceElement = - typename std::result_of<decltype (&LocalFiniteElementFactory<Basis, 0>::get)(typename Basis::LocalView, decltype(Dune::Indices::_0))>::type; + typename std::result_of<decltype (&LocalFiniteElementFactory<Basis, 0>::get)(typename Basis::LocalView, decltype(Dune::Indices::_0))>::type; // The local finite element type used for the director interpolation using DirectorElement = - typename std::result_of<decltype (&LocalFiniteElementFactory<Basis, 1>::get)(typename Basis::LocalView, decltype(Dune::Indices::_1))>::type; + typename std::result_of<decltype (&LocalFiniteElementFactory<Basis, 1>::get)(typename Basis::LocalView, decltype(Dune::Indices::_1))>::type; // The local finite element function type to evaluate the midsurface position - using LocalMidSurfaceFunctionType = LocalFEFunction<gridDim, DT, MidSurfaceElement, RealTuple<field_type, 3>>; + using LocalMidSurfaceFunctionType = LocalFEFunction<gridDim, DT, MidSurfaceElement, RealTuple<field_type, 3> >; // The local finite element function type to evaluate the director - using LocalDirectorFunctionType = LocalFEFunction<gridDim, DT, DirectorElement, UnitVector<field_type, 3>>; + using LocalDirectorFunctionType = LocalFEFunction<gridDim, DT, DirectorElement, UnitVector<field_type, 3> >; // Extra function type for reference quantities since they are unconditionally doubles, i.e. no ADOL-C types - using LocalMidSurfaceReferenceFunctionType = LocalFEFunction<gridDim, DT, MidSurfaceElement, RealTuple<double, 3>>; - using LocalDirectorReferenceFunctionType = LocalFEFunction<gridDim, DT, DirectorElement, UnitVector<double, 3>>; + using LocalMidSurfaceReferenceFunctionType = LocalFEFunction<gridDim, DT, MidSurfaceElement, RealTuple<double, 3> >; + using LocalDirectorReferenceFunctionType = LocalFEFunction<gridDim, DT, DirectorElement, UnitVector<double, 3> >; public: /** \brief Constructor with a set of material parameters @@ -91,15 +91,15 @@ namespace Dune::GFE { SimoFoxEnergyLocalStiffness(const Dune::ParameterTree ¶meters, const BoundaryPatch<GridView> *neumannBoundary, const std::function<Dune::FieldVector<double, 3>(Dune::FieldVector<double, 2>)> neumannFunction, const std::function<Dune::FieldVector<double, 3>(Dune::FieldVector<double, 2>)> volumeLoad, - const Dune::TupleVector<std::vector<RealTuple<double, 3>>, std::vector<UnitVector<double, 3>>> &x0) - : neumannBoundary_(neumannBoundary), - neumannFunction_(neumannFunction), - thickness_{parameters.template get<double>("thickness")}, // The sheeqll thickness - mu_{parameters.template get<double>("mu")}, // Lame constant 1 - lambda_{parameters.template get<double>("lambda")}, // Lame constant 2 - kappa_{parameters.template get<double>("kappa")}, // Shear correction factor - midSurfaceRefConfig{x0[Dune::Indices::_0]}, - directorRefConfig{x0[Dune::Indices::_1]} + const Dune::TupleVector<std::vector<RealTuple<double, 3> >, std::vector<UnitVector<double, 3> > > &x0) + : neumannBoundary_(neumannBoundary), + neumannFunction_(neumannFunction), + thickness_{parameters.template get<double>("thickness")}, // The sheeqll thickness + mu_{parameters.template get<double>("mu")}, // Lame constant 1 + lambda_{parameters.template get<double>("lambda")}, // Lame constant 2 + kappa_{parameters.template get<double>("kappa")}, // Shear correction factor + midSurfaceRefConfig{x0[Dune::Indices::_0]}, + directorRefConfig{x0[Dune::Indices::_1]} { // Calculate the over the thickness preintegrated St.Venant Kirchhoff material matrix, Paper equation 10.1 */ const double Emodul = mu_ * (3 * lambda_ + 2 * mu_) / (lambda_ + mu_); // Young's modulus @@ -123,8 +123,8 @@ namespace Dune::GFE { } /** \brief Assemble the energy for a single element */ - RT energy(const typename Basis::LocalView &localView, const std::vector<RealTuple<field_type, 3>> &localMidSurfaceConfiguration, - const std::vector<UnitVector<field_type, 3>> &localDirectorConfiguration) const override; + RT energy(const typename Basis::LocalView &localView, const std::vector<RealTuple<field_type, 3> > &localMidSurfaceConfiguration, + const std::vector<UnitVector<field_type, 3> > &localDirectorConfiguration) const override; private: /** \brief A structure that contains all quantities to calculate the Lagrangian strains */ @@ -145,7 +145,7 @@ namespace Dune::GFE { /** \brief Calculates all kinematic quantities */ template <typename Element, typename LocalDirectorFunction, typename LocalMidSurfaceFunction, typename LocalDirectorReferenceFunction, - typename LocalMidSurfaceReferenceFunction, typename IntegrationPointPosition> + typename LocalMidSurfaceReferenceFunction, typename IntegrationPointPosition> static auto kinematicVariablesFactory(const Element &element, const LocalDirectorFunction &directorFunction, const LocalDirectorReferenceFunction &directorReferenceFunction, const LocalMidSurfaceFunction &midSurfaceFunction, @@ -156,7 +156,7 @@ namespace Dune::GFE { static Dune::FieldVector<RT, 8> calculateGreenLagrangianStrains(const KinematicVariables &kin); /** \brief Save all tangent base matrices for all nodes in one place*/ - Dune::BlockVector<Dune::FieldMatrix<field_type, 2, 3>> directorTangentSpaces; + Dune::BlockVector<Dune::FieldMatrix<field_type, 2, 3> > directorTangentSpaces; /** \brief The Neumann boundary */ const BoundaryPatch<GridView> *neumannBoundary_; @@ -180,8 +180,8 @@ namespace Dune::GFE { const std::function<Dune::FieldVector<double, 3>(Dune::FieldVector<double, dimworld>)> volumeLoad_; /** \brief Stores the reference configuration of the midsurface and the director field */ - const std::vector<RealTuple<double, 3>> &midSurfaceRefConfig; - const std::vector<UnitVector<double, 3>> &directorRefConfig; + const std::vector<RealTuple<double, 3> > &midSurfaceRefConfig; + const std::vector<UnitVector<double, 3> > &directorRefConfig; }; /** \brief Calculate the Green-Lagrange strain components for the thickness-integrated setting @@ -193,7 +193,7 @@ namespace Dune::GFE { * Paper Equation 4.10 */ template <class Basis, template <int, typename, typename, typename> typename LocalFEFunction, typename field_type> Dune::FieldVector<field_type, 8> SimoFoxEnergyLocalStiffness<Basis, LocalFEFunction, field_type>::calculateGreenLagrangianStrains( - const KinematicVariables &kin) + const KinematicVariables &kin) { Dune::FieldVector<RT, 8> egl; // membrane @@ -220,11 +220,11 @@ namespace Dune::GFE { */ template <class Basis, template <int, typename, typename, typename> typename LocalFEFunction, typename field_type> template <typename Element, typename LocalDirectorFunction, typename LocalMidSurfaceFunction, typename LocalDirectorReferenceFunction, - typename LocalMidSurfaceReferenceFunction, typename IntegrationPointPosition> + typename LocalMidSurfaceReferenceFunction, typename IntegrationPointPosition> auto SimoFoxEnergyLocalStiffness<Basis, LocalFEFunction, field_type>::kinematicVariablesFactory( - const Element &element, const LocalDirectorFunction &directorFunction, const LocalDirectorReferenceFunction &directorReferenceFunction, - const LocalMidSurfaceFunction &midSurfaceFunction, const LocalMidSurfaceReferenceFunction &midSurfaceReferenceFunction, - const LocalMidSurfaceFunction &midSurfaceDisplacementFunction, const IntegrationPointPosition &quadPos) + const Element &element, const LocalDirectorFunction &directorFunction, const LocalDirectorReferenceFunction &directorReferenceFunction, + const LocalMidSurfaceFunction &midSurfaceFunction, const LocalMidSurfaceReferenceFunction &midSurfaceReferenceFunction, + const LocalMidSurfaceFunction &midSurfaceDisplacementFunction, const IntegrationPointPosition &quadPos) { KinematicVariables kin{}; @@ -243,13 +243,13 @@ namespace Dune::GFE { template <class Basis, template <int, typename, typename, typename> typename LocalFEFunction, typename field_type> auto SimoFoxEnergyLocalStiffness<Basis, LocalFEFunction, field_type>::getReferenceLocalConfigurations( - const typename Basis::LocalView &localView) const { + const typename Basis::LocalView &localView) const { using namespace Dune::TypeTree::Indices; const int nDofs0 = localView.tree().child(_0, 0).finiteElement().size(); const int nDofs1 = localView.tree().child(_1, 0).finiteElement().size(); - std::vector<RealTuple<double, 3>> localConfiguration0(nDofs0); - std::vector<UnitVector<double, 3>> localConfiguration1(nDofs1); + std::vector<RealTuple<double, 3> > localConfiguration0(nDofs0); + std::vector<UnitVector<double, 3> > localConfiguration1(nDofs1); for (int i = 0; i < nDofs0 + nDofs1; i++) { int localIndexI = 0; @@ -282,8 +282,8 @@ namespace Dune::GFE { template <class Basis, template <int, typename, typename, typename> typename LocalFEFunction, typename field_type> typename SimoFoxEnergyLocalStiffness<Basis, LocalFEFunction, field_type>::RT SimoFoxEnergyLocalStiffness<Basis, LocalFEFunction, field_type>::energy( - const typename Basis::LocalView &localView, const std::vector<RealTuple<field_type, 3>> &localMidSurfaceConfiguration, - const std::vector<UnitVector<field_type, 3>> &localDirectorConfiguration) const + const typename Basis::LocalView &localView, const std::vector<RealTuple<field_type, 3> > &localMidSurfaceConfiguration, + const std::vector<UnitVector<field_type, 3> > &localDirectorConfiguration) const { auto element = localView.element(); @@ -293,7 +293,7 @@ namespace Dune::GFE { const auto [localRefMidSurfaceConfiguration, localRefDirectorConfiguration] = getReferenceLocalConfigurations(localView); - std::vector<RealTuple<field_type, 3>> displacements; + std::vector<RealTuple<field_type, 3> > displacements; displacements.reserve(localMidSurfaceConfiguration.size()); for (size_t i = 0; i < localMidSurfaceConfiguration.size(); ++i) displacements.emplace_back(localMidSurfaceConfiguration[i].globalCoordinates() - localRefMidSurfaceConfiguration[i].globalCoordinates()); @@ -314,8 +314,8 @@ namespace Dune::GFE { const Dune::FieldVector<DT, gridDim> &quadPos = curQuad.position(); const KinematicVariables kin - = kinematicVariablesFactory(element, localDirectorFunction, localDirectorReferenceFunction, localMidSurfaceFunction, - localMidSurfaceReferenceFunction, localMidSurfaceDisplacementFunction, quadPos); + = kinematicVariablesFactory(element, localDirectorFunction, localDirectorReferenceFunction, localMidSurfaceFunction, + localMidSurfaceReferenceFunction, localMidSurfaceDisplacementFunction, quadPos); const DT integrationElement = element.geometry().integrationElement(quadPos); const FieldVector<field_type, 8> Egl = calculateGreenLagrangianStrains(kin); diff --git a/dune/gfe/assemblers/sumenergy.hh b/dune/gfe/assemblers/sumenergy.hh index a7765747..dd450369 100644 --- a/dune/gfe/assemblers/sumenergy.hh +++ b/dune/gfe/assemblers/sumenergy.hh @@ -9,51 +9,51 @@ namespace Dune::GFE { /** - \brief Assembles the a sum of energies for a single element by summing up the energies of each GFE::LocalEnergy. + \brief Assembles the a sum of energies for a single element by summing up the energies of each GFE::LocalEnergy. This class works similarly to the class Dune::Elasticity::SumEnergy, where Dune::Elasticity::SumEnergy extends Dune::Elasticity::LocalEnergy and Dune::GFE::SumEnergy extends Dune::GFE::LocalEnergy. - */ + */ -template<class Basis, class... TargetSpaces> -class SumEnergy - : public Dune::GFE::LocalEnergy<Basis, TargetSpaces...>, - public MixedLocalGeodesicFEStiffness<Basis, TargetSpaces...> - //Inheriting from MixedLocalGeodesicFEStiffness is hack, and will be replaced eventually; once MixedLocalGFEADOLCStiffness - //will be removed and its functionality will be included in LocalGeodesicFEADOLCStiffness this is not needed anymore! + template<class Basis, class ... TargetSpaces> + class SumEnergy + : public Dune::GFE::LocalEnergy<Basis, TargetSpaces...>, + public MixedLocalGeodesicFEStiffness<Basis, TargetSpaces...> + //Inheriting from MixedLocalGeodesicFEStiffness is hack, and will be replaced eventually; once MixedLocalGFEADOLCStiffness + //will be removed and its functionality will be included in LocalGeodesicFEADOLCStiffness this is not needed anymore! -{ + { - using LocalView = typename Basis::LocalView; - using GridView = typename LocalView::GridView; - using DT = typename GridView::Grid::ctype; - using RT = typename Dune::GFE::LocalEnergy<Basis,TargetSpaces...>::RT; + using LocalView = typename Basis::LocalView; + using GridView = typename LocalView::GridView; + using DT = typename GridView::Grid::ctype; + using RT = typename Dune::GFE::LocalEnergy<Basis,TargetSpaces...>::RT; -public: - - /** \brief Default Constructor*/ - SumEnergy() - {} + public: - void addLocalEnergy(std::shared_ptr<GFE::LocalEnergy<Basis,TargetSpaces...>> localEnergy) - { - localEnergies_.push_back(localEnergy); - } + /** \brief Default Constructor*/ + SumEnergy() + {} - RT energy(const typename Basis::LocalView& localView, - const std::vector<TargetSpaces>&... localSolution) const - { - RT sum = 0.; - for ( const auto& localEnergy : localEnergies_ ) - sum += localEnergy->energy( localView, localSolution... ); + void addLocalEnergy(std::shared_ptr<GFE::LocalEnergy<Basis,TargetSpaces...> > localEnergy) + { + localEnergies_.push_back(localEnergy); + } + + RT energy(const typename Basis::LocalView& localView, + const std::vector<TargetSpaces>& ... localSolution) const + { + RT sum = 0.; + for ( const auto& localEnergy : localEnergies_ ) + sum += localEnergy->energy( localView, localSolution ... ); - return sum; - } + return sum; + } -private: - // vector containing pointers to the local energies - std::vector<std::shared_ptr<GFE::LocalEnergy<Basis, TargetSpaces...>>> localEnergies_; -}; + private: + // vector containing pointers to the local energies + std::vector<std::shared_ptr<GFE::LocalEnergy<Basis, TargetSpaces...> > > localEnergies_; + }; } // namespace Dune::GFE diff --git a/dune/gfe/assemblers/surfacecosseratenergy.hh b/dune/gfe/assemblers/surfacecosseratenergy.hh index df21fd02..b5d6c16b 100644 --- a/dune/gfe/assemblers/surfacecosseratenergy.hh +++ b/dune/gfe/assemblers/surfacecosseratenergy.hh @@ -19,384 +19,384 @@ #include <dune/localfunctions/lagrange/lfecache.hh> namespace Dune::GFE { -/** \brief Assembles the cosserat energy on the given boundary for a single element. - * - * \tparam CurvedGeometryGridFunction Type of the grid function that gives the geometry of the deformed surface - * \tparam Basis Type of the Basis used for assembling - * \tparam TargetSpaces The List of TargetSpaces - SurfaceCosseratEnergy needs exactly two TargetSpaces! - */ -template<class CurvedGeometryGridFunction, class Basis, class... TargetSpaces> -class SurfaceCosseratEnergy -: public Dune::GFE::LocalEnergy<Basis, TargetSpaces...> -{ - using GridView = typename Basis::GridView; - using DT = typename GridView::ctype ; - using RT = typename Dune::GFE::LocalEnergy<Basis, TargetSpaces...>::RT ; - using Entity = typename GridView::template Codim<0>::Entity ; - using RBM0 = RealTuple<RT,GridView::dimensionworld> ; - using RBM1 = Rotation<RT,GridView::dimensionworld> ; - using RBM = RigidBodyMotion<RT,GridView::dimensionworld> ; - - constexpr static int dimWorld = GridView::dimensionworld; - constexpr static int gridDim = GridView::dimension; - static constexpr int boundaryDim = gridDim - 1; - - /** \brief Compute the derivative of the rotation (with respect to x), but wrt matrix coordinates - \param value Value of the gfe function at a certain point - \param derivative First derivative of the gfe function wrt x at that point, in quaternion coordinates - \param DR First derivative of the gfe function wrt x at that point, in matrix coordinates + /** \brief Assembles the cosserat energy on the given boundary for a single element. + * + * \tparam CurvedGeometryGridFunction Type of the grid function that gives the geometry of the deformed surface + * \tparam Basis Type of the Basis used for assembling + * \tparam TargetSpaces The List of TargetSpaces - SurfaceCosseratEnergy needs exactly two TargetSpaces! */ - static void computeDR(const RBM& value, - const Dune::FieldMatrix<RT,RBM::embeddedDim,boundaryDim>& derivative, - Tensor3<RT,dimWorld,dimWorld,boundaryDim>& derivative_rotation) + template<class CurvedGeometryGridFunction, class Basis, class ... TargetSpaces> + class SurfaceCosseratEnergy + : public Dune::GFE::LocalEnergy<Basis, TargetSpaces...> { - // The LocalGFEFunction class gives us the derivatives of the orientation variable, - // but as a map into quaternion space. To obtain matrix coordinates we use the - // chain rule, which means that we have to multiply the given derivative with - // the derivative of the embedding of the unit quaternion into the space of 3x3 matrices. - // This second derivative is almost given by the method getFirstDerivativesOfDirectors. - // However, since the directors of a given unit quaternion are the _columns_ of the - // corresponding orthogonal matrix, we need to invert the i and j indices - // - // So, DR[i][j][k] contains \partial R_ij / \partial k - Tensor3<RT, dimWorld, dimWorld, 4> dd_dq; - value.q.getFirstDerivativesOfDirectors(dd_dq); - - derivative_rotation = RT(0); - for (int i=0; i<dimWorld; i++) - for (int j=0; j<dimWorld; j++) - for (int k=0; k<boundaryDim; k++) - for (int l=0; l<4; l++) - derivative_rotation[i][j][k] += dd_dq[j][i][l] * derivative[l+3][k]; - } - - - -public: - - /** \brief Constructor with a set of material parameters - * \param parameters The material parameters - * \param shellBoundary The shellBoundary contains the faces where the cosserat energy is assembled - * \param curvedGeometryGridFunction The curvedGeometryGridFunction gives the geometry of the shell in stress-free state. - When assembling, we deform the intersections using the curvedGeometryGridFunction and then use the deformed geometries. - * \param thicknessF The shell thickness parameter, given as a function and evaluated at each quadrature point - * \param lameF The Lame parameters, given as a function and evaluated at each quadrature point - */ - SurfaceCosseratEnergy(const Dune::ParameterTree& parameters, - const BoundaryPatch<GridView>* shellBoundary, - const CurvedGeometryGridFunction& curvedGeometryGridFunction, - const std::function<double(Dune::FieldVector<double,dimWorld>)> thicknessF, - const std::function<Dune::FieldVector<double,2>(Dune::FieldVector<double,dimWorld>)> lameF) - : shellBoundary_(shellBoundary), - curvedGeometryGridFunction_(curvedGeometryGridFunction), - thicknessF_(thicknessF), - lameF_(lameF) - { - // Cosserat couple modulus - mu_c_ = parameters.template get<double>("mu_c"); - - // Length scale parameter - L_c_ = parameters.template get<double>("L_c"); - - // Curvature parameters - b1_ = parameters.template get<double>("b1"); - b2_ = parameters.template get<double>("b2"); - b3_ = parameters.template get<double>("b3"); - } - -RT energy(const typename Basis::LocalView& localView, - const std::vector<TargetSpaces>&... localSolutions) const -{ - static_assert(sizeof...(TargetSpaces) == 2, "SurfaceCosseratEnergy needs exactly two TargetSpaces!"); - - using namespace Dune::Indices; - using TargetSpace0 = typename std::tuple_element<0, std::tuple<TargetSpaces...> >::type; - const std::vector<TargetSpace0>& localSolution0 = std::get<0>(std::forward_as_tuple(localSolutions...)); - using TargetSpace1 = typename std::tuple_element<1, std::tuple<TargetSpaces...> >::type; - const std::vector<TargetSpace1>& localSolution1 = std::get<1>(std::forward_as_tuple(localSolutions...)); - - // The element geometry - auto element = localView.element(); - auto gridView = localView.globalBasis().gridView(); - - //////////////////////////////////////////////////////////////////////////////////// - // Set up the local nonlinear finite element function - //////////////////////////////////////////////////////////////////////////////////// - using namespace Dune::TypeTree::Indices; - - // The set of shape functions on this element - const auto& deformationLocalFiniteElement = localView.tree().child(_0,0).finiteElement(); - const auto& orientationLocalFiniteElement = localView.tree().child(_1,0).finiteElement(); - - // to construct a local GFE function, in case they are the shape functions are the same, we can use use one GFE-Function -#if MIXED_SPACE - std::vector<RBM0> localSolutionRBM0(localSolution0.size()); - std::vector<RBM1> localSolutionRBM1(localSolution1.size()); - for (int i = 0; i < localSolution0.size(); i++) - localSolutionRBM0[i] = localSolution0[i]; - for (int i = 0; i< localSolution1.size(); i++) - localSolutionRBM1[i] = localSolution1[i]; - typedef LocalGeodesicFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RBM0> LocalGFEFunctionType0; - typedef LocalGeodesicFEFunction<gridDim, DT, decltype(orientationLocalFiniteElement), RBM1> LocalGFEFunctionType1; - LocalGFEFunctionType0 localGeodesicFEFunction0(deformationLocalFiniteElement,localSolutionRBM0); - LocalGFEFunctionType1 localGeodesicFEFunction1(orientationLocalFiniteElement,localSolutionRBM1); -#else - std::vector<RBM> localSolutionRBM(localSolution0.size()); - for (int i = 0; i < localSolution0.size(); i++) { - for (int j = 0; j < dimWorld; j++) - localSolutionRBM[i].r[j] = localSolution0[i][j]; - localSolutionRBM[i].q = localSolution1[i]; + using GridView = typename Basis::GridView; + using DT = typename GridView::ctype ; + using RT = typename Dune::GFE::LocalEnergy<Basis, TargetSpaces...>::RT ; + using Entity = typename GridView::template Codim<0>::Entity ; + using RBM0 = RealTuple<RT,GridView::dimensionworld> ; + using RBM1 = Rotation<RT,GridView::dimensionworld> ; + using RBM = RigidBodyMotion<RT,GridView::dimensionworld> ; + + constexpr static int dimWorld = GridView::dimensionworld; + constexpr static int gridDim = GridView::dimension; + static constexpr int boundaryDim = gridDim - 1; + + /** \brief Compute the derivative of the rotation (with respect to x), but wrt matrix coordinates + \param value Value of the gfe function at a certain point + \param derivative First derivative of the gfe function wrt x at that point, in quaternion coordinates + \param DR First derivative of the gfe function wrt x at that point, in matrix coordinates + */ + static void computeDR(const RBM& value, + const Dune::FieldMatrix<RT,RBM::embeddedDim,boundaryDim>& derivative, + Tensor3<RT,dimWorld,dimWorld,boundaryDim>& derivative_rotation) + { + // The LocalGFEFunction class gives us the derivatives of the orientation variable, + // but as a map into quaternion space. To obtain matrix coordinates we use the + // chain rule, which means that we have to multiply the given derivative with + // the derivative of the embedding of the unit quaternion into the space of 3x3 matrices. + // This second derivative is almost given by the method getFirstDerivativesOfDirectors. + // However, since the directors of a given unit quaternion are the _columns_ of the + // corresponding orthogonal matrix, we need to invert the i and j indices + // + // So, DR[i][j][k] contains \partial R_ij / \partial k + Tensor3<RT, dimWorld, dimWorld, 4> dd_dq; + value.q.getFirstDerivativesOfDirectors(dd_dq); + + derivative_rotation = RT(0); + for (int i=0; i<dimWorld; i++) + for (int j=0; j<dimWorld; j++) + for (int k=0; k<boundaryDim; k++) + for (int l=0; l<4; l++) + derivative_rotation[i][j][k] += dd_dq[j][i][l] * derivative[l+3][k]; } - typedef LocalGeodesicFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RBM> LocalGFEFunctionType; - LocalGFEFunctionType localGeodesicFEFunction(deformationLocalFiniteElement,localSolutionRBM); -#endif - RT energy = 0; - for (auto&& it : intersections(shellBoundary_->gridView(), element)) { - if (not shellBoundary_->contains(it)) - continue; - auto localGridFunction = localFunction(curvedGeometryGridFunction_); - auto curvedGeometryGridFunctionOrder = deformationLocalFiniteElement.localBasis().order();//curvedGeometryGridFunction_.basis().localView().tree().child(0).finiteElement().localBasis().order(); - localGridFunction.bind(element); - auto referenceElement = Dune::referenceElement<DT,boundaryDim>(it.type()); - - // Construct the geometry on the boundary using the map lGF(localGeometry.global(local)): - // The variable local holds the local coordinates in the 2D reference element, localGeometry.global maps them to the 3D reference element. - // The function lGF is the gridfunction bound to the current element, so lGF(localGeometry.global(local)) is the value of curvedGeometryGridFunction_ at - // the point on the intersection face. - using BoundaryGeometry = Dune::CurvedGeometry<DT, boundaryDim, dimWorld, Dune::CurvedGeometryTraits<DT, Dune::LagrangeLFECache<DT,DT,boundaryDim>>>; - BoundaryGeometry boundaryGeometry(referenceElement, - [localGridFunction, localGeometry=it.geometryInInside()](const auto& local) { - return localGridFunction(localGeometry.global(local)); - }, curvedGeometryGridFunctionOrder); + public: + + /** \brief Constructor with a set of material parameters + * \param parameters The material parameters + * \param shellBoundary The shellBoundary contains the faces where the cosserat energy is assembled + * \param curvedGeometryGridFunction The curvedGeometryGridFunction gives the geometry of the shell in stress-free state. + When assembling, we deform the intersections using the curvedGeometryGridFunction and then use the deformed geometries. + * \param thicknessF The shell thickness parameter, given as a function and evaluated at each quadrature point + * \param lameF The Lame parameters, given as a function and evaluated at each quadrature point + */ + SurfaceCosseratEnergy(const Dune::ParameterTree& parameters, + const BoundaryPatch<GridView>* shellBoundary, + const CurvedGeometryGridFunction& curvedGeometryGridFunction, + const std::function<double(Dune::FieldVector<double,dimWorld>)> thicknessF, + const std::function<Dune::FieldVector<double,2>(Dune::FieldVector<double,dimWorld>)> lameF) + : shellBoundary_(shellBoundary), + curvedGeometryGridFunction_(curvedGeometryGridFunction), + thicknessF_(thicknessF), + lameF_(lameF) + { + // Cosserat couple modulus + mu_c_ = parameters.template get<double>("mu_c"); + + // Length scale parameter + L_c_ = parameters.template get<double>("L_c"); + + // Curvature parameters + b1_ = parameters.template get<double>("b1"); + b2_ = parameters.template get<double>("b2"); + b3_ = parameters.template get<double>("b3"); + } - auto quadOrder = (it.type().isSimplex()) ? deformationLocalFiniteElement.localBasis().order() - : deformationLocalFiniteElement.localBasis().order() * boundaryDim; + RT energy(const typename Basis::LocalView& localView, + const std::vector<TargetSpaces>& ... localSolutions) const + { + static_assert(sizeof...(TargetSpaces) == 2, "SurfaceCosseratEnergy needs exactly two TargetSpaces!"); - const auto& quad = Dune::QuadratureRules<DT, boundaryDim>::rule(it.type(), quadOrder); - for (size_t pt=0; pt<quad.size(); pt++) { - - // Local position of the quadrature point - const Dune::FieldVector<DT,gridDim>& quadPos = it.geometryInInside().global(quad[pt].position());; + using namespace Dune::Indices; + using TargetSpace0 = typename std::tuple_element<0, std::tuple<TargetSpaces...> >::type; + const std::vector<TargetSpace0>& localSolution0 = std::get<0>(std::forward_as_tuple(localSolutions ...)); + using TargetSpace1 = typename std::tuple_element<1, std::tuple<TargetSpaces...> >::type; + const std::vector<TargetSpace1>& localSolution1 = std::get<1>(std::forward_as_tuple(localSolutions ...)); - // Global position of the quadrature point - auto quadPosGlobal = it.geometry().global(quad[pt].position()); - double thickness = thicknessF_(quadPosGlobal); - auto lameConstants = lameF_(quadPosGlobal); - double mu = lameConstants[0]; - double lambda = lameConstants[1]; + // The element geometry + auto element = localView.element(); + auto gridView = localView.globalBasis().gridView(); - const DT integrationElement = boundaryGeometry.integrationElement(quad[pt].position()); + //////////////////////////////////////////////////////////////////////////////////// + // Set up the local nonlinear finite element function + //////////////////////////////////////////////////////////////////////////////////// + using namespace Dune::TypeTree::Indices; - RBM value; - Dune::FieldMatrix<RT, RBM::embeddedDim, dimWorld> derivative; - Dune::FieldMatrix<RT, RBM::embeddedDim, boundaryDim> derivative2D; + // The set of shape functions on this element + const auto& deformationLocalFiniteElement = localView.tree().child(_0,0).finiteElement(); + const auto& orientationLocalFiniteElement = localView.tree().child(_1,0).finiteElement(); + // to construct a local GFE function, in case they are the shape functions are the same, we can use use one GFE-Function #if MIXED_SPACE - // The value of the local function - RBM0 value0 = localGeodesicFEFunction0.evaluate(quadPos); - RBM1 value1 = localGeodesicFEFunction1.evaluate(quadPos); - // The derivatives of the local functions - auto derivative0 = localGeodesicFEFunction0.evaluateDerivative(quadPos,value0); - auto derivative1 = localGeodesicFEFunction1.evaluateDerivative(quadPos,value1); - - // Put the value and the derivatives together from the separated values - for (int i = 0; i < RBM0::dim; i++) - value.r[i] = value0[i]; - value.q = value1; - for (int j = 0; j < dimWorld; j++) { - for (int i = 0; i < RBM0::embeddedDim; i++) { - derivative[i][j] = derivative0[i][j]; - if (j < boundaryDim) - derivative2D[i][j] = derivative0[i][j]; - } - for (int i = 0; i < RBM1::embeddedDim; i++) { - derivative[RBM0::embeddedDim + i][j] = derivative1[i][j]; - if (j < boundaryDim) - derivative2D[RBM0::embeddedDim + i][j] = derivative1[i][j]; - } - } + std::vector<RBM0> localSolutionRBM0(localSolution0.size()); + std::vector<RBM1> localSolutionRBM1(localSolution1.size()); + for (int i = 0; i < localSolution0.size(); i++) + localSolutionRBM0[i] = localSolution0[i]; + for (int i = 0; i< localSolution1.size(); i++) + localSolutionRBM1[i] = localSolution1[i]; + typedef LocalGeodesicFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RBM0> LocalGFEFunctionType0; + typedef LocalGeodesicFEFunction<gridDim, DT, decltype(orientationLocalFiniteElement), RBM1> LocalGFEFunctionType1; + LocalGFEFunctionType0 localGeodesicFEFunction0(deformationLocalFiniteElement,localSolutionRBM0); + LocalGFEFunctionType1 localGeodesicFEFunction1(orientationLocalFiniteElement,localSolutionRBM1); #else - value = localGeodesicFEFunction.evaluate(quadPos); - derivative = localGeodesicFEFunction.evaluateDerivative(quadPos,value); - for (int i = 0; i < RBM::embeddedDim; i++) - for (int j = 0; j < boundaryDim; j++) - derivative2D[i][j] = derivative[i][j]; + std::vector<RBM> localSolutionRBM(localSolution0.size()); + for (int i = 0; i < localSolution0.size(); i++) { + for (int j = 0; j < dimWorld; j++) + localSolutionRBM[i].r[j] = localSolution0[i][j]; + localSolutionRBM[i].q = localSolution1[i]; + } + typedef LocalGeodesicFEFunction<gridDim, DT, decltype(deformationLocalFiniteElement), RBM> LocalGFEFunctionType; + LocalGFEFunctionType localGeodesicFEFunction(deformationLocalFiniteElement,localSolutionRBM); #endif - ////////////////////////////////////////////////////////// - // The rotation and its derivative - // Note: we need it in matrix coordinates - ////////////////////////////////////////////////////////// + RT energy = 0; - Dune::FieldMatrix<RT,dimWorld,dimWorld> R; - value.q.matrix(R); - auto rt = Dune::GFE::transpose(R); + for (auto&& it : intersections(shellBoundary_->gridView(), element)) { + if (not shellBoundary_->contains(it)) + continue; - Tensor3<RT,dimWorld,dimWorld,boundaryDim> derivative_rotation; - computeDR(value, derivative2D, derivative_rotation); + auto localGridFunction = localFunction(curvedGeometryGridFunction_); + auto curvedGeometryGridFunctionOrder = deformationLocalFiniteElement.localBasis().order();//curvedGeometryGridFunction_.basis().localView().tree().child(0).finiteElement().localBasis().order(); + localGridFunction.bind(element); + auto referenceElement = Dune::referenceElement<DT,boundaryDim>(it.type()); - ////////////////////////////////////////////////////////// - // Fundamental forms and curvature - ////////////////////////////////////////////////////////// + // Construct the geometry on the boundary using the map lGF(localGeometry.global(local)): + // The variable local holds the local coordinates in the 2D reference element, localGeometry.global maps them to the 3D reference element. + // The function lGF is the gridfunction bound to the current element, so lGF(localGeometry.global(local)) is the value of curvedGeometryGridFunction_ at + // the point on the intersection face. + using BoundaryGeometry = Dune::CurvedGeometry<DT, boundaryDim, dimWorld, Dune::CurvedGeometryTraits<DT, Dune::LagrangeLFECache<DT,DT,boundaryDim> > >; + BoundaryGeometry boundaryGeometry(referenceElement, + [localGridFunction, localGeometry=it.geometryInInside()](const auto& local) { + return localGridFunction(localGeometry.global(local)); + }, curvedGeometryGridFunctionOrder); - // First fundamental form - Dune::FieldMatrix<double,dimWorld,dimWorld> aCovariant; + auto quadOrder = (it.type().isSimplex()) ? deformationLocalFiniteElement.localBasis().order() + : deformationLocalFiniteElement.localBasis().order() * boundaryDim; - // If dimWorld==3, then the first two lines of aCovariant are simply the jacobianTransposed - // of the element. If dimWorld<3 (i.e., ==2), we have to explicitly enters 0.0 in the last column. - const auto jacobianTransposed = boundaryGeometry.jacobianTransposed(quad[pt].position()); + const auto& quad = Dune::QuadratureRules<DT, boundaryDim>::rule(it.type(), quadOrder); + for (size_t pt=0; pt<quad.size(); pt++) { - for (int i=0; i<2; i++) - { - for (int j=0; j<dimWorld; j++) - aCovariant[i][j] = jacobianTransposed[i][j]; - for (int j=dimWorld; j<3; j++) - aCovariant[i][j] = 0.0; - } + // Local position of the quadrature point + const Dune::FieldVector<DT,gridDim>& quadPos = it.geometryInInside().global(quad[pt].position());; - aCovariant[2] = Dune::FMatrixHelp::Impl::crossProduct(aCovariant[0], aCovariant[1]); - aCovariant[2] /= aCovariant[2].two_norm(); - - auto aContravariant = aCovariant; - aContravariant.invert(); - // The contravariant base vectors are the *columns* of the inverse of the covariant matrix - // To get an easier access to the columns, we use the transpose of the contravariant matrix - aContravariant = Dune::GFE::transpose(aContravariant); - - Dune::FieldMatrix<double,3,3> a(0); - for (int alpha=0; alpha<boundaryDim; alpha++) - a += Dune::GFE::dyadicProduct(aCovariant[alpha], aContravariant[alpha]); - - auto a00 = aCovariant[0] * aCovariant[0]; - auto a01 = aCovariant[0] * aCovariant[1]; - auto a10 = aCovariant[1] * aCovariant[0]; - auto a11 = aCovariant[1] * aCovariant[1]; - auto aScalar = std::sqrt(a00*a11 - a10*a01); - - // Alternator tensor - Dune::FieldMatrix<int,2,2> eps = {{0,1},{-1,0}}; - Dune::FieldMatrix<double,3,3> c(0); - - for (int alpha=0; alpha<2; alpha++) - for (int beta=0; beta<2; beta++) - c += aScalar * eps[alpha][beta] * Dune::GFE::dyadicProduct(aContravariant[alpha], aContravariant[beta]); - - // Second fundamental form: The derivative of the normal field - auto b = boundaryGeometry.normalGradient(quad[pt].position()); - b *= (-1); - - // Mean curvatue - auto H = 0.5 * Dune::GFE::trace(b); - - // Gauss curvature, calculated with the normalGradient in the eucidean coordinate system - // see e.g. formula (3.5) from "Refined dimensional reduction for isotropic elastic Cosserat shells with initial curvature" - auto bSquared = b*b; - auto K = 2*H*H - 0.5*Dune::GFE::trace(bSquared); - - ////////////////////////////////////////////////////////// - // Strain tensors - ////////////////////////////////////////////////////////// - - // Elastic shell strain - Dune::FieldMatrix<RT,3,3> Ee(0); - Dune::FieldMatrix<RT,3,3> grad_s_m(0); - for (int alpha=0; alpha<boundaryDim; alpha++) - { - Dune::FieldVector<RT,3> vec; - for (int i=0; i<3; i++) - vec[i] = derivative[i][alpha]; - grad_s_m += Dune::GFE::dyadicProduct(vec, aContravariant[alpha]); - } + // Global position of the quadrature point + auto quadPosGlobal = it.geometry().global(quad[pt].position()); + double thickness = thicknessF_(quadPosGlobal); + auto lameConstants = lameF_(quadPosGlobal); + double mu = lameConstants[0]; + double lambda = lameConstants[1]; - Ee = rt * grad_s_m - a; - - // Elastic shell bending-curvature strain - Dune::FieldMatrix<RT,3,3> Ke(0); - for (int alpha=0; alpha<boundaryDim; alpha++) - { - Dune::FieldMatrix<RT,3,3> tmp; - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) - tmp[i][j] = derivative_rotation[i][j][alpha]; - auto tmp2 = rt * tmp; - Ke += Dune::GFE::dyadicProduct(SkewMatrix<RT,3>(tmp2).axial(), aContravariant[alpha]); - } + const DT integrationElement = boundaryGeometry.integrationElement(quad[pt].position()); - ////////////////////////////////////////////////////////// - // Add the local energy density - ////////////////////////////////////////////////////////// + RBM value; + Dune::FieldMatrix<RT, RBM::embeddedDim, dimWorld> derivative; + Dune::FieldMatrix<RT, RBM::embeddedDim, boundaryDim> derivative2D; - // Add the membrane energy density - auto energyDensity = (thickness - K*Dune::power(thickness,3) / 12.0) * W_m(Ee, mu, lambda); - energyDensity += (Dune::power(thickness,3) / 12.0 - K * Dune::power(thickness,5) / 80.0)*W_m(Ee*b + c*Ke, mu, lambda); - energyDensity += Dune::power(thickness,3) / 6.0 * W_mixt(Ee, c*Ke*b - 2*H*c*Ke, mu, lambda); - energyDensity += Dune::power(thickness,5) / 80.0 * W_mp( (Ee*b + c*Ke)*b, mu, lambda); +#if MIXED_SPACE + // The value of the local function + RBM0 value0 = localGeodesicFEFunction0.evaluate(quadPos); + RBM1 value1 = localGeodesicFEFunction1.evaluate(quadPos); + // The derivatives of the local functions + auto derivative0 = localGeodesicFEFunction0.evaluateDerivative(quadPos,value0); + auto derivative1 = localGeodesicFEFunction1.evaluateDerivative(quadPos,value1); + + // Put the value and the derivatives together from the separated values + for (int i = 0; i < RBM0::dim; i++) + value.r[i] = value0[i]; + value.q = value1; + for (int j = 0; j < dimWorld; j++) { + for (int i = 0; i < RBM0::embeddedDim; i++) { + derivative[i][j] = derivative0[i][j]; + if (j < boundaryDim) + derivative2D[i][j] = derivative0[i][j]; + } + for (int i = 0; i < RBM1::embeddedDim; i++) { + derivative[RBM0::embeddedDim + i][j] = derivative1[i][j]; + if (j < boundaryDim) + derivative2D[RBM0::embeddedDim + i][j] = derivative1[i][j]; + } + } +#else + value = localGeodesicFEFunction.evaluate(quadPos); + derivative = localGeodesicFEFunction.evaluateDerivative(quadPos,value); + for (int i = 0; i < RBM::embeddedDim; i++) + for (int j = 0; j < boundaryDim; j++) + derivative2D[i][j] = derivative[i][j]; +#endif - // Add the bending energy density - energyDensity += (thickness - K*Dune::power(thickness,3) / 12.0) * W_curv(Ke, mu) - + (Dune::power(thickness,3) / 12.0 - K * Dune::power(thickness,5) / 80.0)*W_curv(Ke*b, mu) - + Dune::power(thickness,5) / 80.0 * W_curv(Ke*b*b, mu); + ////////////////////////////////////////////////////////// + // The rotation and its derivative + // Note: we need it in matrix coordinates + ////////////////////////////////////////////////////////// + + Dune::FieldMatrix<RT,dimWorld,dimWorld> R; + value.q.matrix(R); + auto rt = Dune::GFE::transpose(R); + + Tensor3<RT,dimWorld,dimWorld,boundaryDim> derivative_rotation; + computeDR(value, derivative2D, derivative_rotation); + + ////////////////////////////////////////////////////////// + // Fundamental forms and curvature + ////////////////////////////////////////////////////////// + + // First fundamental form + Dune::FieldMatrix<double,dimWorld,dimWorld> aCovariant; + + // If dimWorld==3, then the first two lines of aCovariant are simply the jacobianTransposed + // of the element. If dimWorld<3 (i.e., ==2), we have to explicitly enters 0.0 in the last column. + const auto jacobianTransposed = boundaryGeometry.jacobianTransposed(quad[pt].position()); + + for (int i=0; i<2; i++) + { + for (int j=0; j<dimWorld; j++) + aCovariant[i][j] = jacobianTransposed[i][j]; + for (int j=dimWorld; j<3; j++) + aCovariant[i][j] = 0.0; + } + + aCovariant[2] = Dune::FMatrixHelp::Impl::crossProduct(aCovariant[0], aCovariant[1]); + aCovariant[2] /= aCovariant[2].two_norm(); + + auto aContravariant = aCovariant; + aContravariant.invert(); + // The contravariant base vectors are the *columns* of the inverse of the covariant matrix + // To get an easier access to the columns, we use the transpose of the contravariant matrix + aContravariant = Dune::GFE::transpose(aContravariant); + + Dune::FieldMatrix<double,3,3> a(0); + for (int alpha=0; alpha<boundaryDim; alpha++) + a += Dune::GFE::dyadicProduct(aCovariant[alpha], aContravariant[alpha]); + + auto a00 = aCovariant[0] * aCovariant[0]; + auto a01 = aCovariant[0] * aCovariant[1]; + auto a10 = aCovariant[1] * aCovariant[0]; + auto a11 = aCovariant[1] * aCovariant[1]; + auto aScalar = std::sqrt(a00*a11 - a10*a01); + + // Alternator tensor + Dune::FieldMatrix<int,2,2> eps = {{0,1},{-1,0}}; + Dune::FieldMatrix<double,3,3> c(0); + + for (int alpha=0; alpha<2; alpha++) + for (int beta=0; beta<2; beta++) + c += aScalar * eps[alpha][beta] * Dune::GFE::dyadicProduct(aContravariant[alpha], aContravariant[beta]); + + // Second fundamental form: The derivative of the normal field + auto b = boundaryGeometry.normalGradient(quad[pt].position()); + b *= (-1); + + // Mean curvatue + auto H = 0.5 * Dune::GFE::trace(b); + + // Gauss curvature, calculated with the normalGradient in the eucidean coordinate system + // see e.g. formula (3.5) from "Refined dimensional reduction for isotropic elastic Cosserat shells with initial curvature" + auto bSquared = b*b; + auto K = 2*H*H - 0.5*Dune::GFE::trace(bSquared); + + ////////////////////////////////////////////////////////// + // Strain tensors + ////////////////////////////////////////////////////////// + + // Elastic shell strain + Dune::FieldMatrix<RT,3,3> Ee(0); + Dune::FieldMatrix<RT,3,3> grad_s_m(0); + for (int alpha=0; alpha<boundaryDim; alpha++) + { + Dune::FieldVector<RT,3> vec; + for (int i=0; i<3; i++) + vec[i] = derivative[i][alpha]; + grad_s_m += Dune::GFE::dyadicProduct(vec, aContravariant[alpha]); + } + + Ee = rt * grad_s_m - a; + + // Elastic shell bending-curvature strain + Dune::FieldMatrix<RT,3,3> Ke(0); + for (int alpha=0; alpha<boundaryDim; alpha++) + { + Dune::FieldMatrix<RT,3,3> tmp; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + tmp[i][j] = derivative_rotation[i][j][alpha]; + auto tmp2 = rt * tmp; + Ke += Dune::GFE::dyadicProduct(SkewMatrix<RT,3>(tmp2).axial(), aContravariant[alpha]); + } + + ////////////////////////////////////////////////////////// + // Add the local energy density + ////////////////////////////////////////////////////////// + + // Add the membrane energy density + auto energyDensity = (thickness - K*Dune::power(thickness,3) / 12.0) * W_m(Ee, mu, lambda); + energyDensity += (Dune::power(thickness,3) / 12.0 - K * Dune::power(thickness,5) / 80.0)*W_m(Ee*b + c*Ke, mu, lambda); + energyDensity += Dune::power(thickness,3) / 6.0 * W_mixt(Ee, c*Ke*b - 2*H*c*Ke, mu, lambda); + energyDensity += Dune::power(thickness,5) / 80.0 * W_mp( (Ee*b + c*Ke)*b, mu, lambda); + + // Add the bending energy density + energyDensity += (thickness - K*Dune::power(thickness,3) / 12.0) * W_curv(Ke, mu) + + (Dune::power(thickness,3) / 12.0 - K * Dune::power(thickness,5) / 80.0)*W_curv(Ke*b, mu) + + Dune::power(thickness,5) / 80.0 * W_curv(Ke*b*b, mu); + + // Add energy density + energy += quad[pt].weight() * integrationElement * energyDensity; + } + } - // Add energy density - energy += quad[pt].weight() * integrationElement * energyDensity; + return energy; } - } - - return energy; -} - RT W_m(const Dune::FieldMatrix<RT,3,3>& S, double mu, double lambda) const - { - return W_mixt(S,S, mu, lambda); - } + RT W_m(const Dune::FieldMatrix<RT,3,3>& S, double mu, double lambda) const + { + return W_mixt(S,S, mu, lambda); + } - RT W_mixt(const Dune::FieldMatrix<RT,3,3>& S, const Dune::FieldMatrix<RT,3,3>& T, double mu, double lambda) const - { - return mu * Dune::GFE::frobeniusProduct(Dune::GFE::sym(S), Dune::GFE::sym(T)) - + mu_c_ * Dune::GFE::frobeniusProduct(Dune::GFE::skew(S), Dune::GFE::skew(T)) - + lambda * mu / (lambda + 2*mu) * Dune::GFE::trace(S) * Dune::GFE::trace(T); - } + RT W_mixt(const Dune::FieldMatrix<RT,3,3>& S, const Dune::FieldMatrix<RT,3,3>& T, double mu, double lambda) const + { + return mu * Dune::GFE::frobeniusProduct(Dune::GFE::sym(S), Dune::GFE::sym(T)) + + mu_c_ * Dune::GFE::frobeniusProduct(Dune::GFE::skew(S), Dune::GFE::skew(T)) + + lambda * mu / (lambda + 2*mu) * Dune::GFE::trace(S) * Dune::GFE::trace(T); + } - RT W_mp(const Dune::FieldMatrix<RT,3,3>& S, double mu, double lambda) const - { - return mu * Dune::GFE::sym(S).frobenius_norm2() + mu_c_ * Dune::GFE::skew(S).frobenius_norm2() + lambda * 0.5 * Dune::GFE::traceSquared(S); - } + RT W_mp(const Dune::FieldMatrix<RT,3,3>& S, double mu, double lambda) const + { + return mu * Dune::GFE::sym(S).frobenius_norm2() + mu_c_ * Dune::GFE::skew(S).frobenius_norm2() + lambda * 0.5 * Dune::GFE::traceSquared(S); + } - RT W_curv(const Dune::FieldMatrix<RT,3,3>& S, double mu) const - { - return mu * L_c_ * L_c_ * (b1_ * Dune::GFE::dev(Dune::GFE::sym(S)).frobenius_norm2() - + b2_ * Dune::GFE::skew(S).frobenius_norm2() + b3_ * Dune::GFE::traceSquared(S)); - } + RT W_curv(const Dune::FieldMatrix<RT,3,3>& S, double mu) const + { + return mu * L_c_ * L_c_ * (b1_ * Dune::GFE::dev(Dune::GFE::sym(S)).frobenius_norm2() + + b2_ * Dune::GFE::skew(S).frobenius_norm2() + b3_ * Dune::GFE::traceSquared(S)); + } -private: - /** \brief The shell boundary */ - const BoundaryPatch<GridView>* shellBoundary_; + private: + /** \brief The shell boundary */ + const BoundaryPatch<GridView>* shellBoundary_; - /** \brief The function used to create the Geometries used for assembling */ - const CurvedGeometryGridFunction curvedGeometryGridFunction_; + /** \brief The function used to create the Geometries used for assembling */ + const CurvedGeometryGridFunction curvedGeometryGridFunction_; - /** \brief The shell thickness as a function*/ - std::function<double(Dune::FieldVector<double,dimWorld>)> thicknessF_; + /** \brief The shell thickness as a function*/ + std::function<double(Dune::FieldVector<double,dimWorld>)> thicknessF_; - /** \brief The Lamé-parameters as a function*/ - std::function<Dune::FieldVector<double,2>(Dune::FieldVector<double,dimWorld>)> lameF_; + /** \brief The Lamé-parameters as a function*/ + std::function<Dune::FieldVector<double,2>(Dune::FieldVector<double,dimWorld>)> lameF_; - /** \brief Lame constants */ - double mu_, lambda_; + /** \brief Lame constants */ + double mu_, lambda_; - /** \brief Cosserat couple modulus, preferably 0 */ - double mu_c_; + /** \brief Cosserat couple modulus, preferably 0 */ + double mu_c_; - /** \brief Length scale parameter */ - double L_c_; + /** \brief Length scale parameter */ + double L_c_; - /** \brief Curvature parameters */ - double b1_, b2_, b3_; + /** \brief Curvature parameters */ + double b1_, b2_, b3_; -}; + }; } // namespace Dune::GFE #endif //#ifndef DUNE_GFE_SURFACECOSSERATENERGY_HH diff --git a/dune/gfe/assemblers/surfacecosseratstressassembler.hh b/dune/gfe/assemblers/surfacecosseratstressassembler.hh index 5c4f6da3..74db17fc 100644 --- a/dune/gfe/assemblers/surfacecosseratstressassembler.hh +++ b/dune/gfe/assemblers/surfacecosseratstressassembler.hh @@ -12,294 +12,294 @@ namespace Dune::GFE { /** \brief An assembler that can calculate the norms of specific stress tensors for each element for an output by film-on-substrate - \tparam BasisOrderD Basis used for the displacement - \tparam BasisOrderR Basis used for the rotation - \tparam TargetSpaceD Target space for the Displacement - \tparam TargetSpaceR Target space for the Rotation - */ + \tparam BasisOrderD Basis used for the displacement + \tparam BasisOrderR Basis used for the rotation + \tparam TargetSpaceD Target space for the Displacement + \tparam TargetSpaceR Target space for the Rotation + */ template <class BasisOrderD, class BasisOrderR, class TargetSpaceD, class TargetSpaceR> class SurfaceCosseratStressAssembler { - public: - const static int dim = TargetSpaceD::dimension; - using GridView = typename BasisOrderD::GridView; - using VectorD = std::vector<TargetSpaceD>; - using VectorR = std::vector<TargetSpaceR>; + public: + const static int dim = TargetSpaceD::dimension; + using GridView = typename BasisOrderD::GridView; + using VectorD = std::vector<TargetSpaceD>; + using VectorR = std::vector<TargetSpaceR>; - BasisOrderD basisOrderD_; - BasisOrderR basisOrderR_; + BasisOrderD basisOrderD_; + BasisOrderR basisOrderR_; - SurfaceCosseratStressAssembler(const BasisOrderD basisOrderD, - const BasisOrderR basisOrderR) + SurfaceCosseratStressAssembler(const BasisOrderD basisOrderD, + const BasisOrderR basisOrderR) : basisOrderD_(basisOrderD), - basisOrderR_(basisOrderR) - {} - - /** \brief Calculate the norm of the 1st-Piola-Kirchhoff-Stress-Tensor and the Cauchy-Stress-Tensor for each element - - The 1st-Piola-Kirchhoff-Stress-Tensor is the derivative of the energy density with respect to the deformation gradient - - Calculate the deformation gradient for each element using the basis functions and - their gradients; then add them up using the localConfiguration - - Evaluate the deformation gradient at each quadrature point using the respective quadrature rule with the given order - - Evaluate the density function and tape the evaluation - then use ADOLC to evaluate the derivative (∂/∂F) W(F) - - The derivative is then a dim x dim matrix - - Then calculate the final stressTensor of the element by averagin over the quadrature points using the quadrature - weights and the reference element volume - \param x Coefficient vector for the displacement - \param elasticDensity Energy density function - \param int Order of the quadrature rule - \param stressSubstrate1stPiolaKirchhoffTensor Vector containing the the 1st-Piola-Kirchhoff-Stress-Tensor for each element - \param stressSubstrateCauchyTensor Vector containing the Cauchy-Stress-Tensor for each element + basisOrderR_(basisOrderR) + {} + + /** \brief Calculate the norm of the 1st-Piola-Kirchhoff-Stress-Tensor and the Cauchy-Stress-Tensor for each element + + The 1st-Piola-Kirchhoff-Stress-Tensor is the derivative of the energy density with respect to the deformation gradient + - Calculate the deformation gradient for each element using the basis functions and + their gradients; then add them up using the localConfiguration + - Evaluate the deformation gradient at each quadrature point using the respective quadrature rule with the given order + - Evaluate the density function and tape the evaluation - then use ADOLC to evaluate the derivative (∂/∂F) W(F) + - The derivative is then a dim x dim matrix + - Then calculate the final stressTensor of the element by averagin over the quadrature points using the quadrature + weights and the reference element volume + \param x Coefficient vector for the displacement + \param elasticDensity Energy density function + \param int Order of the quadrature rule + \param stressSubstrate1stPiolaKirchhoffTensor Vector containing the the 1st-Piola-Kirchhoff-Stress-Tensor for each element + \param stressSubstrateCauchyTensor Vector containing the Cauchy-Stress-Tensor for each element */ - template <class Density> - void assembleSubstrateStress( - const VectorD x, - const Density* elasticDensity, - const int quadOrder, - std::vector<FieldMatrix<double,dim,dim>>& stressSubstrate1stPiolaKirchhoffTensor, - std::vector<FieldMatrix<double,dim,dim>>& stressSubstrateCauchyTensor) + template <class Density> + void assembleSubstrateStress( + const VectorD x, + const Density* elasticDensity, + const int quadOrder, + std::vector<FieldMatrix<double,dim,dim> >& stressSubstrate1stPiolaKirchhoffTensor, + std::vector<FieldMatrix<double,dim,dim> >& stressSubstrateCauchyTensor) + { + + std::cout << "Calculating the Frobenius norm of the 1st-Piola-Kirchhoff-Stress-Tensor ( (∂/∂F) W(F) )" << std::endl + << "and the Frobenius norm of the Cauchy-Stress-Tensor (1/det(F) * (∂/∂F) W(F) * F^T) of the substrate..." << std::endl; + + auto xFlat = Functions::istlVectorBackend(x); + static constexpr auto partitionType = Partitions::interiorBorder; + MultipleCodimMultipleGeomTypeMapper<GridView> elementMapper(basisOrderD_.gridView(),mcmgElementLayout()); + stressSubstrate1stPiolaKirchhoffTensor.resize(elementMapper.size()); + stressSubstrateCauchyTensor.resize(elementMapper.size()); + + for (const auto& element : elements(basisOrderD_.gridView(), partitionType)) { + auto localViewOrderD = basisOrderD_.localView(); + localViewOrderD.bind(element); + size_t nDofsOrderD = localViewOrderD.tree().size(); + + // Extract values at this element + std::vector<double> localConfiguration(nDofsOrderD); + for (size_t i=0; i<nDofsOrderD; i++) + localConfiguration[i] = xFlat[localViewOrderD.index(i)]; //localViewOrderD.index(i) is a multi-index + + //Store the reference gradient and the gradients for this element + const auto& lFEOrderD = localViewOrderD.tree().child(0).finiteElement(); + std::vector<FieldMatrix<double,1,dim> > referenceGradients(lFEOrderD.size()); + std::vector<FieldMatrix<double,1,dim> > gradients(lFEOrderD.size()); + + auto evaluateAtPoint = [&](FieldVector<double,3> pointGlobal, FieldVector<double,3> pointLocal) -> std::vector<FieldMatrix<double,dim,dim> > { + std::vector<FieldMatrix<double,dim,dim> > stressTensors(2); + + const auto jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(pointLocal); + + // Get gradients of shape functions + lFEOrderD.localBasis().evaluateJacobian(pointLocal, referenceGradients); + + // Compute gradients of Base functions + for (size_t i=0; i<gradients.size(); ++i) + gradients[i] = referenceGradients[i] * transpose(jacobianInverseTransposed); + + // Deformation gradient in vector form + size_t nDoubles = dim*dim; + std::vector<double> deformationGradientFlat(nDoubles); + for (size_t i=0; i<nDoubles; i++) + deformationGradientFlat[i] = 0; + for (size_t i=0; i<gradients.size(); i++) + for (size_t j=0; j<dim; j++) + for (size_t k=0; k<dim; k++) + deformationGradientFlat[dim*j + k] += localConfiguration[ localViewOrderD.tree().child(j).localIndex(i)]*gradients[i][0][k]; + + double pureDensity = 0; + + trace_on(0); + + FieldMatrix<adouble,dim,dim> deformationGradient(0); + for (size_t j=0; j<dim; j++) + for (size_t k=0; k<dim; k++) + deformationGradient[j][k] <<= deformationGradientFlat[dim*j + k]; + + // Tape the actual calculation + adouble density = 0; + try { + density = (*elasticDensity)(pointGlobal, deformationGradient); + } catch (Exception &e) { + trace_off(0); + throw e; + } + density >>= pureDensity; + + trace_off(0); + + // Compute the actual gradient + std::vector<double> localStressFlat(nDoubles); + gradient(0,nDoubles,deformationGradientFlat.data(),localStressFlat.data()); + + FieldMatrix<double,dim,dim> localStress(0); + for (size_t j=0; j<dim; j++) + for (size_t k=0; k<dim; k++) + localStress[j][k] = localStressFlat[dim*j + k]; + + stressTensors[0] = localStress; // 1st-Piola-Kirchhoff-Stress-Tensor + + FieldMatrix<double,dim,dim> deformationGradientTransposed(0); + for (size_t j=0; j<dim; j++) + for (size_t k=0; k<dim; k++) + deformationGradient[j][k] >>= deformationGradientTransposed[k][j]; + + localStress /= deformationGradientTransposed.determinant(); + localStress = localStress * deformationGradientTransposed; + stressTensors[1] = localStress; // Cauchy-Stress-Tensor + + return stressTensors; + }; + + //Call evaluateAtPoint for all points in the quadrature rule + const auto& quad = Dune::QuadratureRules<double, dim>::rule(element.type(), quadOrder); + stressSubstrate1stPiolaKirchhoffTensor[elementMapper.index(element)] = 0; + stressSubstrateCauchyTensor[elementMapper.index(element)] = 0; + + for (size_t pt=0; pt<quad.size(); pt++) { + auto pointLocal = quad[pt].position(); + auto pointGlobal = element.geometry().global(pointLocal); + auto stressTensors = evaluateAtPoint(pointGlobal, pointLocal); + stressSubstrate1stPiolaKirchhoffTensor[elementMapper.index(element)] += stressTensors[0] * quad[pt].weight()/referenceElement(element).volume(); + stressSubstrateCauchyTensor[elementMapper.index(element)] += stressTensors[1] * quad[pt].weight()/referenceElement(element).volume(); + } + } + } + /** \brief Calculate the norm of the Biot-Type-Stress-Tensor of the shell + + The formula for Biot-Type-Stress-Tensor of the Cosserat shell given by (4.11) in + Ghiba, Bîrsan, Lewintan, Neff, March 2020: "The isotropic Cosserat shell model including terms up to $O(h^5)$. Part I: Derivation in matrix notation" + \param rot Coefficient vector for the rotation + \param x Coefficient vector for the displacement + \param xInitial Coefficient vector for the stress-free configuration of the shell, used to calculate nablaTheta + \param lameF Function assigning the Lamé parameters to a given point + \param mu_c Cosserat couple modulus + \param shellBoundary BoundaryPatch containing the elements that actually belong to the shell + \param order Order of the quadrature rule + \param stressShellBiotTensor Vector containing the Biot-Stress-Tensor for each element + */ + void assembleShellStress( + const VectorR rot, + const VectorD x, + const VectorD xInitial, + const std::function<Dune::FieldVector<double,2>(Dune::FieldVector<double,dim>)> lameF, + const double mu_c, + const BoundaryPatch<GridView> shellBoundary, + const int quadOrder, + std::vector<FieldMatrix<double,dim,dim> >& stressShellBiotTensor) + { + std::cout << "Calculating the Frobenius norm of the Biot-Type-Stress-Tensor of the shell..." << std::endl; + auto xFlat = Functions::istlVectorBackend(x); + auto xInitialFlat = Functions::istlVectorBackend(xInitial); + + MultipleCodimMultipleGeomTypeMapper<GridView> elementMapper(basisOrderD_.gridView(),mcmgElementLayout()); + stressShellBiotTensor.resize(elementMapper.size()); + + static constexpr auto partitionType = Partitions::interiorBorder; + for (const auto& element : elements(basisOrderD_.gridView(), partitionType)) + { + stressShellBiotTensor[elementMapper.index(element)] = 0; - std::cout << "Calculating the Frobenius norm of the 1st-Piola-Kirchhoff-Stress-Tensor ( (∂/∂F) W(F) )" << std::endl - << "and the Frobenius norm of the Cauchy-Stress-Tensor (1/det(F) * (∂/∂F) W(F) * F^T) of the substrate..." << std::endl; + int intersectionCounter = 0; + for (auto&& it : intersections(shellBoundary.gridView(), element)) { + FieldMatrix<double,dim,dim> stressTensorThisIntersection(0); - auto xFlat = Functions::istlVectorBackend(x); - static constexpr auto partitionType = Partitions::interiorBorder; - MultipleCodimMultipleGeomTypeMapper<GridView> elementMapper(basisOrderD_.gridView(),mcmgElementLayout()); - stressSubstrate1stPiolaKirchhoffTensor.resize(elementMapper.size()); - stressSubstrateCauchyTensor.resize(elementMapper.size()); + //Continue if the element does not intersect with the shell boundary + if (not shellBoundary.contains(it)) + continue; - for (const auto& element : elements(basisOrderD_.gridView(), partitionType)) - { + //LocalView for the basisOrderD_ auto localViewOrderD = basisOrderD_.localView(); localViewOrderD.bind(element); size_t nDofsOrderD = localViewOrderD.tree().size(); - // Extract values at this element + // Extract local configuration at this element std::vector<double> localConfiguration(nDofsOrderD); - for (size_t i=0; i<nDofsOrderD; i++) - localConfiguration[i] = xFlat[localViewOrderD.index(i)]; //localViewOrderD.index(i) is a multi-index + std::vector<double> localConfigurationInitial(nDofsOrderD); + for (size_t i=0; i<nDofsOrderD; i++) { + localConfiguration[i] = xFlat[localViewOrderD.index(i)]; + localConfigurationInitial[i] = xInitialFlat[localViewOrderD.index(i)]; + } - //Store the reference gradient and the gradients for this element const auto& lFEOrderD = localViewOrderD.tree().child(0).finiteElement(); + //Store the reference gradient and the gradients for *this element* std::vector<FieldMatrix<double,1,dim> > referenceGradients(lFEOrderD.size()); std::vector<FieldMatrix<double,1,dim> > gradients(lFEOrderD.size()); - auto evaluateAtPoint = [&](FieldVector<double,3> pointGlobal, FieldVector<double,3> pointLocal) -> std::vector<FieldMatrix<double,dim,dim>>{ - std::vector<FieldMatrix<double,dim,dim>> stressTensors(2); - - const auto jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(pointLocal); - - // Get gradients of shape functions - lFEOrderD.localBasis().evaluateJacobian(pointLocal, referenceGradients); + //LocalView for the basisOrderR_ + auto localViewOrderR = basisOrderR_.localView(); + localViewOrderR.bind(element); + const auto& lFEOrderR = localViewOrderR.tree().child(0).finiteElement(); + VectorR localConfigurationRot(lFEOrderR.size()); + for (std::size_t i=0; i<localConfigurationRot.size(); i++) + localConfigurationRot[i] = rot[localViewOrderR.index(i)[0]]; //localViewOrderR.index(i) is a multiindex, its first entry is the actual index + typedef LocalGeodesicFEFunction<dim, double, decltype(lFEOrderR), TargetSpaceR> LocalGFEFunctionType; + LocalGFEFunctionType localGeodesicFEFunction(lFEOrderR,localConfigurationRot); - // Compute gradients of Base functions - for (size_t i=0; i<gradients.size(); ++i) - gradients[i] = referenceGradients[i] * transpose(jacobianInverseTransposed); + auto evaluateAtPoint = [&](FieldVector<double,3> pointGlobal, FieldVector<double,3> pointLocal3d) -> FieldMatrix<double,dim,dim> { + Dune::FieldMatrix<double,dim,dim> nablaTheta; - // Deformation gradient in vector form - size_t nDoubles = dim*dim; - std::vector<double> deformationGradientFlat(nDoubles); - for (size_t i=0; i<nDoubles; i++) - deformationGradientFlat[i] = 0; - for (size_t i=0; i<gradients.size(); i++) - for (size_t j=0; j<dim; j++) - for (size_t k=0; k<dim; k++) - deformationGradientFlat[dim*j + k] += localConfiguration[ localViewOrderD.tree().child(j).localIndex(i)]*gradients[i][0][k]; + const auto jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(pointLocal3d); - double pureDensity = 0; + // Get gradients of shape functions + lFEOrderD.localBasis().evaluateJacobian(pointLocal3d, referenceGradients); - trace_on(0); + // Compute gradients of Base functions at this element + for (size_t i=0; i<gradients.size(); i++) + gradients[i] = referenceGradients[i] * transpose(jacobianInverseTransposed); - FieldMatrix<adouble,dim,dim> deformationGradient(0); - for (size_t j=0; j<dim; j++) - for (size_t k=0; k<dim; k++) - deformationGradient[j][k] <<= deformationGradientFlat[dim*j + k]; + // Deformation gradient - call this U_es_minus_Id already + FieldMatrix<double,dim,dim> U_es_minus_Id(0); + for (size_t i=0; i<gradients.size(); i++) + for (size_t j=0; j<dim; j++) { + U_es_minus_Id[j].axpy(localConfiguration[ localViewOrderD.tree().child(j).localIndex(i)],gradients[i][0]); + nablaTheta[j].axpy(localConfigurationInitial[ localViewOrderD.tree().child(j).localIndex(i)],gradients[i][0]); + } - // Tape the actual calculation - adouble density = 0; - try { - density = (*elasticDensity)(pointGlobal, deformationGradient); - } catch (Exception &e) { - trace_off(0); - throw e; - } - density >>= pureDensity; + TargetSpaceR value = localGeodesicFEFunction.evaluate(pointLocal3d); + FieldMatrix<double,dim,dim> rotationMatrix(0); + FieldMatrix<double,dim,dim> rotationMatrixTransposed(0); + value.matrix(rotationMatrix); - trace_off(0); + MatrixVector::transpose(rotationMatrix, rotationMatrixTransposed); - // Compute the actual gradient - std::vector<double> localStressFlat(nDoubles); - gradient(0,nDoubles,deformationGradientFlat.data(),localStressFlat.data()); + U_es_minus_Id.leftmultiply(rotationMatrixTransposed); // Attention: The rotation here is already is Q_e, we don't have to multiply with Q_0!!! - FieldMatrix<double,dim,dim> localStress(0); - for (size_t j=0; j<dim; j++) - for (size_t k=0; k<dim; k++) - localStress[j][k] = localStressFlat[dim*j + k]; + nablaTheta.invert(); + U_es_minus_Id.rightmultiply(nablaTheta); - stressTensors[0] = localStress; // 1st-Piola-Kirchhoff-Stress-Tensor + for (size_t j = 0; j < dim; j++) + U_es_minus_Id[j][j] -= 1.0; - FieldMatrix<double,dim,dim> deformationGradientTransposed(0); - for (size_t j=0; j<dim; j++) - for (size_t k=0; k<dim; k++) - deformationGradient[j][k] >>= deformationGradientTransposed[k][j]; + auto lameConstants = lameF(pointGlobal); + double mu = lameConstants[0]; + double lambda = lameConstants[1]; - localStress /= deformationGradientTransposed.determinant(); - localStress = localStress * deformationGradientTransposed; - stressTensors[1] = localStress; // Cauchy-Stress-Tensor - - return stressTensors; - }; + FieldMatrix<double,dim,dim> localStressShell(0); + localStressShell = 2*mu*GFE::sym(U_es_minus_Id) + 2*mu_c*GFE::skew(U_es_minus_Id); + for (size_t j = 0; j < dim; j++) + localStressShell[j][j] += lambda*GFE::trace(GFE::sym(U_es_minus_Id)); + return localStressShell; + }; //Call evaluateAtPoint for all points in the quadrature rule - const auto& quad = Dune::QuadratureRules<double, dim>::rule(element.type(), quadOrder); - stressSubstrate1stPiolaKirchhoffTensor[elementMapper.index(element)] = 0; - stressSubstrateCauchyTensor[elementMapper.index(element)] = 0; + const auto& quad = Dune::QuadratureRules<double, dim-1>::rule(it.type(), quadOrder); //Quad rule on the boundary for (size_t pt=0; pt<quad.size(); pt++) { - auto pointLocal = quad[pt].position(); - auto pointGlobal = element.geometry().global(pointLocal); - auto stressTensors = evaluateAtPoint(pointGlobal, pointLocal); - stressSubstrate1stPiolaKirchhoffTensor[elementMapper.index(element)] += stressTensors[0] * quad[pt].weight()/referenceElement(element).volume(); - stressSubstrateCauchyTensor[elementMapper.index(element)] += stressTensors[1] * quad[pt].weight()/referenceElement(element).volume(); - } - } - } - /** \brief Calculate the norm of the Biot-Type-Stress-Tensor of the shell - - The formula for Biot-Type-Stress-Tensor of the Cosserat shell given by (4.11) in - Ghiba, Bîrsan, Lewintan, Neff, March 2020: "The isotropic Cosserat shell model including terms up to $O(h^5)$. Part I: Derivation in matrix notation" - \param rot Coefficient vector for the rotation - \param x Coefficient vector for the displacement - \param xInitial Coefficient vector for the stress-free configuration of the shell, used to calculate nablaTheta - \param lameF Function assigning the Lamé parameters to a given point - \param mu_c Cosserat couple modulus - \param shellBoundary BoundaryPatch containing the elements that actually belong to the shell - \param order Order of the quadrature rule - \param stressShellBiotTensor Vector containing the Biot-Stress-Tensor for each element - */ - void assembleShellStress( - const VectorR rot, - const VectorD x, - const VectorD xInitial, - const std::function<Dune::FieldVector<double,2>(Dune::FieldVector<double,dim>)> lameF, - const double mu_c, - const BoundaryPatch<GridView> shellBoundary, - const int quadOrder, - std::vector<FieldMatrix<double,dim,dim>>& stressShellBiotTensor) - { - std::cout << "Calculating the Frobenius norm of the Biot-Type-Stress-Tensor of the shell..." << std::endl; - auto xFlat = Functions::istlVectorBackend(x); - auto xInitialFlat = Functions::istlVectorBackend(xInitial); - - MultipleCodimMultipleGeomTypeMapper<GridView> elementMapper(basisOrderD_.gridView(),mcmgElementLayout()); - stressShellBiotTensor.resize(elementMapper.size()); - - static constexpr auto partitionType = Partitions::interiorBorder; - for (const auto& element : elements(basisOrderD_.gridView(), partitionType)) - { - stressShellBiotTensor[elementMapper.index(element)] = 0; - - int intersectionCounter = 0; - for (auto&& it : intersections(shellBoundary.gridView(), element)) { - FieldMatrix<double,dim,dim> stressTensorThisIntersection(0); - - //Continue if the element does not intersect with the shell boundary - if (not shellBoundary.contains(it)) - continue; - - //LocalView for the basisOrderD_ - auto localViewOrderD = basisOrderD_.localView(); - localViewOrderD.bind(element); - size_t nDofsOrderD = localViewOrderD.tree().size(); - - // Extract local configuration at this element - std::vector<double> localConfiguration(nDofsOrderD); - std::vector<double> localConfigurationInitial(nDofsOrderD); - for (size_t i=0; i<nDofsOrderD; i++) { - localConfiguration[i] = xFlat[localViewOrderD.index(i)]; - localConfigurationInitial[i] = xInitialFlat[localViewOrderD.index(i)]; - } - - const auto& lFEOrderD = localViewOrderD.tree().child(0).finiteElement(); - //Store the reference gradient and the gradients for *this element* - std::vector<FieldMatrix<double,1,dim> > referenceGradients(lFEOrderD.size()); - std::vector<FieldMatrix<double,1,dim> > gradients(lFEOrderD.size()); - - //LocalView for the basisOrderR_ - auto localViewOrderR = basisOrderR_.localView(); - localViewOrderR.bind(element); - const auto& lFEOrderR = localViewOrderR.tree().child(0).finiteElement(); - VectorR localConfigurationRot(lFEOrderR.size()); - for (std::size_t i=0; i<localConfigurationRot.size(); i++) - localConfigurationRot[i] = rot[localViewOrderR.index(i)[0]];//localViewOrderR.index(i) is a multiindex, its first entry is the actual index - typedef LocalGeodesicFEFunction<dim, double, decltype(lFEOrderR), TargetSpaceR> LocalGFEFunctionType; - LocalGFEFunctionType localGeodesicFEFunction(lFEOrderR,localConfigurationRot); - - auto evaluateAtPoint = [&](FieldVector<double,3> pointGlobal, FieldVector<double,3> pointLocal3d) -> FieldMatrix<double,dim,dim>{ - Dune::FieldMatrix<double,dim,dim> nablaTheta; - - const auto jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(pointLocal3d); - - // Get gradients of shape functions - lFEOrderD.localBasis().evaluateJacobian(pointLocal3d, referenceGradients); - - // Compute gradients of Base functions at this element - for (size_t i=0; i<gradients.size(); i++) - gradients[i] = referenceGradients[i] * transpose(jacobianInverseTransposed); - - // Deformation gradient - call this U_es_minus_Id already - FieldMatrix<double,dim,dim> U_es_minus_Id(0); - for (size_t i=0; i<gradients.size(); i++) - for (size_t j=0; j<dim; j++){ - U_es_minus_Id[j].axpy(localConfiguration[ localViewOrderD.tree().child(j).localIndex(i)],gradients[i][0]); - nablaTheta[j].axpy(localConfigurationInitial[ localViewOrderD.tree().child(j).localIndex(i)],gradients[i][0]); - } - - TargetSpaceR value = localGeodesicFEFunction.evaluate(pointLocal3d); - FieldMatrix<double,dim,dim> rotationMatrix(0); - FieldMatrix<double,dim,dim> rotationMatrixTransposed(0); - value.matrix(rotationMatrix); - - MatrixVector::transpose(rotationMatrix, rotationMatrixTransposed); - - U_es_minus_Id.leftmultiply(rotationMatrixTransposed); // Attention: The rotation here is already is Q_e, we don't have to multiply with Q_0!!! - - nablaTheta.invert(); - U_es_minus_Id.rightmultiply(nablaTheta); - - for (size_t j = 0; j < dim; j++) - U_es_minus_Id[j][j] -= 1.0; - - auto lameConstants = lameF(pointGlobal); - double mu = lameConstants[0]; - double lambda = lameConstants[1]; - - FieldMatrix<double,dim,dim> localStressShell(0); - localStressShell = 2*mu*GFE::sym(U_es_minus_Id) + 2*mu_c*GFE::skew(U_es_minus_Id); - for (size_t j = 0; j < dim; j++) - localStressShell[j][j] += lambda*GFE::trace(GFE::sym(U_es_minus_Id)); - return localStressShell; - }; - - //Call evaluateAtPoint for all points in the quadrature rule - const auto& quad = Dune::QuadratureRules<double, dim-1>::rule(it.type(), quadOrder); //Quad rule on the boundary - - for (size_t pt=0; pt<quad.size(); pt++) { - auto pointLocal2d = quad[pt].position(); - auto pointLocal3d = it.geometryInInside().global(pointLocal2d); - auto pointGlobal = it.geometry().global(pointLocal2d); - - auto stressTensor = evaluateAtPoint(pointGlobal, pointLocal3d); - stressTensorThisIntersection += stressTensor * quad[pt].weight()/referenceElement(it.inside()).volume(); - } - stressShellBiotTensor[elementMapper.index(element)] += stressTensorThisIntersection; - intersectionCounter++; + auto pointLocal2d = quad[pt].position(); + auto pointLocal3d = it.geometryInInside().global(pointLocal2d); + auto pointGlobal = it.geometry().global(pointLocal2d); + + auto stressTensor = evaluateAtPoint(pointGlobal, pointLocal3d); + stressTensorThisIntersection += stressTensor * quad[pt].weight()/referenceElement(it.inside()).volume(); } - if (intersectionCounter >= 1) - stressShellBiotTensor[elementMapper.index(element)] /= intersectionCounter; + stressShellBiotTensor[elementMapper.index(element)] += stressTensorThisIntersection; + intersectionCounter++; } + if (intersectionCounter >= 1) + stressShellBiotTensor[elementMapper.index(element)] /= intersectionCounter; } + } }; } #endif diff --git a/dune/gfe/assemblers/weightedsumenergy.hh b/dune/gfe/assemblers/weightedsumenergy.hh index 6fd5df3d..9f7d7179 100644 --- a/dune/gfe/assemblers/weightedsumenergy.hh +++ b/dune/gfe/assemblers/weightedsumenergy.hh @@ -29,7 +29,7 @@ public: WeightedSumEnergy(std::vector<std::shared_ptr<Dune::GFE::LocalEnergy<Basis,TargetSpace> > > addends, std::vector<double> weights) - : addends_(addends), + : addends_(addends), weights_(weights) {} @@ -51,4 +51,3 @@ public: }; #endif - diff --git a/dune/gfe/averagedistanceassembler.hh b/dune/gfe/averagedistanceassembler.hh index be41bba0..55fd1a33 100644 --- a/dune/gfe/averagedistanceassembler.hh +++ b/dune/gfe/averagedistanceassembler.hh @@ -9,92 +9,92 @@ template <class TargetSpace, class WeightType=double> class AverageDistanceAssembler { - typedef typename TargetSpace::ctype ctype; - static const int size = TargetSpace::TangentVector::dimension; - static const int embeddedSize = TargetSpace::EmbeddedTangentVector::dimension; + typedef typename TargetSpace::ctype ctype; + static const int size = TargetSpace::TangentVector::dimension; + static const int embeddedSize = TargetSpace::EmbeddedTangentVector::dimension; public: - /** \brief Constructor with given coefficients \f$ v_i \f$ and weights \f$ w_i \f$ - */ - AverageDistanceAssembler(const std::vector<TargetSpace>& coefficients, - const std::vector<WeightType>& weights) - : coefficients_(coefficients), - weights_(weights) - {} - - /** \brief Constructor with given coefficients \f$ v_i \f$ and weights \f$ w_i \f$ - * - * The weights are given as a vector of length-1 FieldVectors instead of as a - * vector of doubles. The reason is that these weights are actually the values - * of Lagrange shape functions, and the dune-localfunction interface returns - * shape function values this way. - */ - AverageDistanceAssembler(const std::vector<TargetSpace>& coefficients, - const std::vector<Dune::FieldVector<WeightType,1> >& weights) - : coefficients_(coefficients), - weights_(weights.size()) - { - for (size_t i=0; i<weights.size(); i++) - weights_[i] = weights[i][0]; + /** \brief Constructor with given coefficients \f$ v_i \f$ and weights \f$ w_i \f$ + */ + AverageDistanceAssembler(const std::vector<TargetSpace>& coefficients, + const std::vector<WeightType>& weights) + : coefficients_(coefficients), + weights_(weights) + {} + + /** \brief Constructor with given coefficients \f$ v_i \f$ and weights \f$ w_i \f$ + * + * The weights are given as a vector of length-1 FieldVectors instead of as a + * vector of doubles. The reason is that these weights are actually the values + * of Lagrange shape functions, and the dune-localfunction interface returns + * shape function values this way. + */ + AverageDistanceAssembler(const std::vector<TargetSpace>& coefficients, + const std::vector<Dune::FieldVector<WeightType,1> >& weights) + : coefficients_(coefficients), + weights_(weights.size()) + { + for (size_t i=0; i<weights.size(); i++) + weights_[i] = weights[i][0]; + } + + ctype value(const TargetSpace& x) const { + + ctype result = 0; + for (size_t i=0; i<coefficients_.size(); i++) { + ctype dist = TargetSpace::distance(coefficients_[i], x); + result += weights_[i]*dist*dist; } - ctype value(const TargetSpace& x) const { - - ctype result = 0; - for (size_t i=0; i<coefficients_.size(); i++) { - ctype dist = TargetSpace::distance(coefficients_[i], x); - result += weights_[i]*dist*dist; - } - - return result; - } - - void assembleEmbeddedGradient(const TargetSpace& x, - typename TargetSpace::EmbeddedTangentVector& gradient) const - { - gradient = 0; - for (size_t i=0; i<coefficients_.size(); i++) - gradient.axpy(weights_[i], - TargetSpace::derivativeOfDistanceSquaredWRTSecondArgument(coefficients_[i], x)); - } - - void assembleGradient(const TargetSpace& x, - typename TargetSpace::TangentVector& gradient) const - { - typename TargetSpace::EmbeddedTangentVector embeddedGradient; - assembleEmbeddedGradient(x,embeddedGradient); - - Dune::FieldMatrix<ctype,size,embeddedSize> orthonormalFrame = x.orthonormalFrame(); - orthonormalFrame.mv(embeddedGradient,gradient); - } - - void assembleEmbeddedHessian(const TargetSpace& x, - Dune::SymmetricMatrix<ctype,embeddedSize>& matrix) const - { - matrix = 0; - for (size_t i=0; i<coefficients_.size(); i++) - matrix.axpy(weights_[i], TargetSpace::secondDerivativeOfDistanceSquaredWRTSecondArgument(coefficients_[i], x)); - } - - // TODO Use a Symmetric matrix for the result - void assembleHessian(const TargetSpace& x, - Dune::FieldMatrix<ctype,size,size>& matrix) const - { - Dune::SymmetricMatrix<ctype,embeddedSize> embeddedHessian; - assembleEmbeddedHessian(x,embeddedHessian); - - Dune::FieldMatrix<ctype,size,embeddedSize> frame = x.orthonormalFrame(); - - // this is frame * embeddedHessian * frame^T - for (int i=0; i<size; i++) - for (int j=0; j<size; j++) - matrix[i][j] = embeddedHessian.energyScalarProduct(frame[i], frame[j]); - } - - const std::vector<TargetSpace> coefficients_; - - std::vector<WeightType> weights_; + return result; + } + + void assembleEmbeddedGradient(const TargetSpace& x, + typename TargetSpace::EmbeddedTangentVector& gradient) const + { + gradient = 0; + for (size_t i=0; i<coefficients_.size(); i++) + gradient.axpy(weights_[i], + TargetSpace::derivativeOfDistanceSquaredWRTSecondArgument(coefficients_[i], x)); + } + + void assembleGradient(const TargetSpace& x, + typename TargetSpace::TangentVector& gradient) const + { + typename TargetSpace::EmbeddedTangentVector embeddedGradient; + assembleEmbeddedGradient(x,embeddedGradient); + + Dune::FieldMatrix<ctype,size,embeddedSize> orthonormalFrame = x.orthonormalFrame(); + orthonormalFrame.mv(embeddedGradient,gradient); + } + + void assembleEmbeddedHessian(const TargetSpace& x, + Dune::SymmetricMatrix<ctype,embeddedSize>& matrix) const + { + matrix = 0; + for (size_t i=0; i<coefficients_.size(); i++) + matrix.axpy(weights_[i], TargetSpace::secondDerivativeOfDistanceSquaredWRTSecondArgument(coefficients_[i], x)); + } + + // TODO Use a Symmetric matrix for the result + void assembleHessian(const TargetSpace& x, + Dune::FieldMatrix<ctype,size,size>& matrix) const + { + Dune::SymmetricMatrix<ctype,embeddedSize> embeddedHessian; + assembleEmbeddedHessian(x,embeddedHessian); + + Dune::FieldMatrix<ctype,size,embeddedSize> frame = x.orthonormalFrame(); + + // this is frame * embeddedHessian * frame^T + for (int i=0; i<size; i++) + for (int j=0; j<size; j++) + matrix[i][j] = embeddedHessian.energyScalarProduct(frame[i], frame[j]); + } + + const std::vector<TargetSpace> coefficients_; + + std::vector<WeightType> weights_; }; diff --git a/dune/gfe/averageinterface.hh b/dune/gfe/averageinterface.hh index 6022f8cd..f004c54b 100644 --- a/dune/gfe/averageinterface.hh +++ b/dune/gfe/averageinterface.hh @@ -32,33 +32,33 @@ template <class GridView> class PressureAverager : public Ipopt::TNLP { - typedef double field_type; + typedef double field_type; - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> > MatrixType; - typedef typename MatrixType::row_type RowType; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> > MatrixType; + typedef typename MatrixType::row_type RowType; - constexpr static int dim = GridView::dimension; + constexpr static int dim = GridView::dimension; public: - /** \brief Constructor */ - PressureAverager(const BoundaryPatch<GridView>* patch, - Dune::BlockVector<Dune::FieldVector<double,dim> >* result, - const Dune::FieldVector<double,dim>& resultantForce, - const Dune::FieldVector<double,dim>& resultantTorque, - const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* massMatrix, - const Dune::BlockVector<Dune::FieldVector<double,1> >* nodalWeights, - const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* constraintJacobian) - : jacobianCutoff_(1e-12), patch_(patch), - massMatrix_(massMatrix), nodalWeights_(nodalWeights), - constraintJacobian_(constraintJacobian), x_(result), - resultantForce_(resultantForce), resultantTorque_(resultantTorque) - { - patchArea_ = patch->area(); - } - - /** default destructor */ - virtual ~PressureAverager() {}; + /** \brief Constructor */ + PressureAverager(const BoundaryPatch<GridView>* patch, + Dune::BlockVector<Dune::FieldVector<double,dim> >* result, + const Dune::FieldVector<double,dim>& resultantForce, + const Dune::FieldVector<double,dim>& resultantTorque, + const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* massMatrix, + const Dune::BlockVector<Dune::FieldVector<double,1> >* nodalWeights, + const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* constraintJacobian) + : jacobianCutoff_(1e-12), patch_(patch), + massMatrix_(massMatrix), nodalWeights_(nodalWeights), + constraintJacobian_(constraintJacobian), x_(result), + resultantForce_(resultantForce), resultantTorque_(resultantTorque) + { + patchArea_ = patch->area(); + } + + /** default destructor */ + virtual ~PressureAverager() {}; /**@name Overloaded from TNLP */ //@{ @@ -107,37 +107,37 @@ public: /** @name Solution Methods */ //@{ /** This method is called when the algorithm is complete so the TNLP can store/write the solution */ - virtual void finalize_solution(Ipopt::SolverReturn status, + virtual void finalize_solution(Ipopt::SolverReturn status, Ipopt::Index n, const Ipopt::Number* x, const Ipopt::Number* z_L, const Ipopt::Number* z_U, Ipopt::Index m, const Ipopt::Number* g, const Ipopt::Number* lambda, Ipopt::Number obj_value, - const Ipopt::IpoptData* ip_data, - Ipopt::IpoptCalculatedQuantities* ip_cq); + const Ipopt::IpoptData* ip_data, + Ipopt::IpoptCalculatedQuantities* ip_cq); //@} - // ///////////////////////////////// - // Data - // ///////////////////////////////// + // ///////////////////////////////// + // Data + // ///////////////////////////////// - /** \brief All entries in the constraint Jacobian smaller than the value - here are removed. Apparently this is unnecessary though. - */ - const double jacobianCutoff_; + /** \brief All entries in the constraint Jacobian smaller than the value + here are removed. Apparently this is unnecessary though. + */ + const double jacobianCutoff_; - const BoundaryPatch<GridView>* patch_; + const BoundaryPatch<GridView>* patch_; - double patchArea_; + double patchArea_; - const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* massMatrix_; + const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* massMatrix_; - const Dune::BlockVector<Dune::FieldVector<double,1> >* nodalWeights_; + const Dune::BlockVector<Dune::FieldVector<double,1> >* nodalWeights_; - const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* constraintJacobian_; + const Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >* constraintJacobian_; - Dune::BlockVector<Dune::FieldVector<double,dim> >* x_; + Dune::BlockVector<Dune::FieldVector<double,dim> >* x_; - Dune::FieldVector<double,dim> resultantForce_; - Dune::FieldVector<double,dim> resultantTorque_; + Dune::FieldVector<double,dim> resultantForce_; + Dune::FieldVector<double,dim> resultantTorque_; private: /**@name Methods to block default compiler methods. @@ -155,35 +155,35 @@ bool PressureAverager<GridView>:: get_nlp_info(Ipopt::Index& n, Ipopt::Index& m, Ipopt::Index& nnz_jac_g, Ipopt::Index& nnz_h_lag, IndexStyleEnum& index_style) { - // One variable for each vertex on the coupling boundary, and three for the closest constant field - n = patch_->numVertices()*dim + dim; + // One variable for each vertex on the coupling boundary, and three for the closest constant field + n = patch_->numVertices()*dim + dim; - // prescribed total forces and moments - m = 2*dim; + // prescribed total forces and moments + m = 2*dim; - // number of nonzeroes in the constraint Jacobian - // leave out the very small ones, as they create instabilities - nnz_jac_g = 0; - for (int i=0; i<m; i++) { + // number of nonzeroes in the constraint Jacobian + // leave out the very small ones, as they create instabilities + nnz_jac_g = 0; + for (int i=0; i<m; i++) { - const RowType& jacobianRow = (*constraintJacobian_)[i]; + const RowType& jacobianRow = (*constraintJacobian_)[i]; - for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) - if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) - nnz_jac_g++; + for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) + if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) + nnz_jac_g++; - } + } - // We only need the lower left corner of the Hessian (since it is symmetric) - if (!massMatrix_) - DUNE_THROW(SolverError, "No mass matrix has been supplied!"); + // We only need the lower left corner of the Hessian (since it is symmetric) + if (!massMatrix_) + DUNE_THROW(SolverError, "No mass matrix has been supplied!"); - nnz_h_lag = 0; + nnz_h_lag = 0; - // use the C style indexing (0-based) - index_style = Ipopt::TNLP::C_STYLE; + // use the C style indexing (0-based) + index_style = Ipopt::TNLP::C_STYLE; - return true; + return true; } @@ -193,21 +193,21 @@ bool PressureAverager<GridView>:: get_bounds_info(Ipopt::Index n, Ipopt::Number* x_l, Ipopt::Number* x_u, Ipopt::Index m, Ipopt::Number* g_l, Ipopt::Number* g_u) { - // here, the n and m we gave IPOPT in get_nlp_info are passed back to us. - // If desired, we could assert to make sure they are what we think they are. - //assert(n == x_->dim()); - //assert(m == 0); - - // Be on the safe side: unset all variable bounds - for (int i=0; i<n; i++) { - x_l[i] = -std::numeric_limits<double>::max(); - x_u[i] = std::numeric_limits<double>::max(); - } - - for (int i=0; i<dim; i++) { - g_l[i] = g_u[i] = resultantForce_[i]; - g_l[i+dim] = g_u[i+dim] = resultantTorque_[i]; - } + // here, the n and m we gave IPOPT in get_nlp_info are passed back to us. + // If desired, we could assert to make sure they are what we think they are. + //assert(n == x_->dim()); + //assert(m == 0); + + // Be on the safe side: unset all variable bounds + for (int i=0; i<n; i++) { + x_l[i] = -std::numeric_limits<double>::max(); + x_u[i] = std::numeric_limits<double>::max(); + } + + for (int i=0; i<dim; i++) { + g_l[i] = g_u[i] = resultantForce_[i]; + g_l[i+dim] = g_u[i+dim] = resultantTorque_[i]; + } return true; } @@ -219,19 +219,19 @@ get_starting_point(Ipopt::Index n, bool init_x, Ipopt::Number* x, bool init_z, Ipopt::Number* z_L, Ipopt::Number* z_U, Ipopt::Index m, bool init_lambda, Ipopt::Number* lambda) { - // Here, we assume we only have starting values for x, if you code - // your own NLP, you can provide starting values for the dual variables - // if you wish - assert(init_x == true); - assert(init_z == false); - assert(init_lambda == false); - - // initialize to the given starting point - for (int i=0; i<n/dim; i++) - for (int j=0; j<dim; j++) - x[i*dim+j] = resultantForce_[j]/patchArea_; + // Here, we assume we only have starting values for x, if you code + // your own NLP, you can provide starting values for the dual variables + // if you wish + assert(init_x == true); + assert(init_z == false); + assert(init_lambda == false); + + // initialize to the given starting point + for (int i=0; i<n/dim; i++) + for (int j=0; j<dim; j++) + x[i*dim+j] = resultantForce_[j]/patchArea_; - return true; + return true; } // returns the value of the objective function @@ -239,34 +239,34 @@ template <class GridView> bool PressureAverager<GridView>:: eval_f(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Number& obj_value) { - // Init return value - obj_value = 0; + // Init return value + obj_value = 0; - //////////////////////////////////// - // Compute x^T*A*x - //////////////////////////////////// + //////////////////////////////////// + // Compute x^T*A*x + //////////////////////////////////// - for (int rowIdx=0; rowIdx<massMatrix_->N(); rowIdx++) { + for (int rowIdx=0; rowIdx<massMatrix_->N(); rowIdx++) { - const typename MatrixType::row_type& row = (*massMatrix_)[rowIdx]; + const typename MatrixType::row_type& row = (*massMatrix_)[rowIdx]; - typename MatrixType::row_type::ConstIterator cIt = row.begin(); - typename MatrixType::row_type::ConstIterator cEndIt = row.end(); + typename MatrixType::row_type::ConstIterator cIt = row.begin(); + typename MatrixType::row_type::ConstIterator cEndIt = row.end(); - for (; cIt!=cEndIt; ++cIt) - for (int i=0; i<dim; i++) - obj_value += x[dim*rowIdx+i] * x[dim*cIt.index()+i] * (*cIt)[0][0]; + for (; cIt!=cEndIt; ++cIt) + for (int i=0; i<dim; i++) + obj_value += x[dim*rowIdx+i] * x[dim*cIt.index()+i] * (*cIt)[0][0]; - } + } - // - for (int i=0; i<nodalWeights_->size(); i++) - for (int j=0; j<dim; j++) - obj_value -= 2 * x[n-dim + j] * x[i*dim+j] * (*nodalWeights_)[i]; + // + for (int i=0; i<nodalWeights_->size(); i++) + for (int j=0; j<dim; j++) + obj_value -= 2 * x[n-dim + j] * x[i*dim+j] * (*nodalWeights_)[i]; - // += c^2 * \int 1 ds - for (int i=0; i<dim; i++) - obj_value += patchArea_ * (x[n-dim + i] * x[n-dim + i]); + // += c^2 * \int 1 ds + for (int i=0; i<dim; i++) + obj_value += patchArea_ * (x[n-dim + i] * x[n-dim + i]); return true; } @@ -276,41 +276,41 @@ template <class GridView> bool PressureAverager<GridView>:: eval_grad_f(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Number* grad_f) { - //std::cout << "### eval_grad_f ###" << std::endl; + //std::cout << "### eval_grad_f ###" << std::endl; - // \nabla J = A(x,.) - b(x) - for (int i=0; i<n; i++) - grad_f[i] = 0; + // \nabla J = A(x,.) - b(x) + for (int i=0; i<n; i++) + grad_f[i] = 0; - for (int i=0; i<massMatrix_->N(); i++) { + for (int i=0; i<massMatrix_->N(); i++) { - const typename MatrixType::row_type& row = (*massMatrix_)[i]; + const typename MatrixType::row_type& row = (*massMatrix_)[i]; - typename MatrixType::row_type::ConstIterator cIt = row.begin(); - typename MatrixType::row_type::ConstIterator cEndIt = row.end(); + typename MatrixType::row_type::ConstIterator cIt = row.begin(); + typename MatrixType::row_type::ConstIterator cEndIt = row.end(); - for (; cIt!=cEndIt; ++cIt) - for (int j=0; j<dim; j++) - grad_f[i*dim+j] += 2 * (*cIt)[0][0] * x[cIt.index()*dim+j]; + for (; cIt!=cEndIt; ++cIt) + for (int j=0; j<dim; j++) + grad_f[i*dim+j] += 2 * (*cIt)[0][0] * x[cIt.index()*dim+j]; - for (int j=0; j<dim; j++) - grad_f[i*dim+j] -= 2 * x[n-dim+j] * (*nodalWeights_)[i]; + for (int j=0; j<dim; j++) + grad_f[i*dim+j] -= 2 * x[n-dim+j] * (*nodalWeights_)[i]; - } + } - for (int i=0; i<dim; i++) { + for (int i=0; i<dim; i++) { - for (int j=0; j<nodalWeights_->size(); j++) - grad_f[n-dim+i] -= 2* (*nodalWeights_)[j]*x[j*dim+i]; + for (int j=0; j<nodalWeights_->size(); j++) + grad_f[n-dim+i] -= 2* (*nodalWeights_)[j]*x[j*dim+i]; - grad_f[n-dim+i] += 2*x[n-dim+i]*patchArea_; + grad_f[n-dim+i] += 2*x[n-dim+i]*patchArea_; - } + } -// for (int i=0; i<n; i++) { -// std::cout << "x = " << x[i] << std::endl; -// std::cout << "grad = " << grad_f[i] << std::endl; -// } + // for (int i=0; i<n; i++) { + // std::cout << "x = " << x[i] << std::endl; + // std::cout << "grad = " << grad_f[i] << std::endl; + // } return true; } @@ -320,23 +320,23 @@ template <class GridView> bool PressureAverager<GridView>:: eval_g(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Index m, Ipopt::Number* g) { - for (int i=0; i<m; i++) { + for (int i=0; i<m; i++) { - // init - g[i] = 0; + // init + g[i] = 0; - const RowType& jacobianRow = (*constraintJacobian_)[i]; + const RowType& jacobianRow = (*constraintJacobian_)[i]; - for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) - if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) { - //printf("adding %g times %g\n", (*cIt)[0][0], x[cIt.index()]); - g[i] += (*cIt)[0][0] * x[cIt.index()]; - } + for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) + if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) { + //printf("adding %g times %g\n", (*cIt)[0][0], x[cIt.index()]); + g[i] += (*cIt)[0][0] * x[cIt.index()]; + } - } + } -// for (int i=0; i<m; i++) -// std::cout << "g[" << i << "]: " << g[i] << std::endl; + // for (int i=0; i<m; i++) + // std::cout << "g[" << i << "]: " << g[i] << std::endl; return true; } @@ -348,41 +348,41 @@ eval_jac_g(Ipopt::Index n, const Ipopt::Number* x, bool new_x, Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index* iRow, Ipopt::Index *jCol, Ipopt::Number* values) { - int idx = 0; + int idx = 0; - if (values==NULL) { + if (values==NULL) { - for (int i=0; i<m; i++) { - - const RowType& jacobianRow = (*constraintJacobian_)[i]; + for (int i=0; i<m; i++) { - for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) { - if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) { - iRow[idx] = i; - jCol[idx] = cIt.index(); - idx++; - } - } + const RowType& jacobianRow = (*constraintJacobian_)[i]; + for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) { + if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) { + iRow[idx] = i; + jCol[idx] = cIt.index(); + idx++; } + } - } else { + } - for (int i=0; i<m; i++) { + } else { - const RowType& jacobianRow = (*constraintJacobian_)[i]; + for (int i=0; i<m; i++) { - for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) - if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) - values[idx++] = (*cIt)[0][0]; + const RowType& jacobianRow = (*constraintJacobian_)[i]; - } + for (typename RowType::ConstIterator cIt = jacobianRow.begin(); cIt!=jacobianRow.end(); ++cIt) + if ( std::abs((*cIt)[0][0]) > jacobianCutoff_ ) + values[idx++] = (*cIt)[0][0]; + + } - } + } - return true; + return true; } //return the structure or values of the hessian @@ -393,8 +393,8 @@ eval_h(Ipopt::Index n, const Ipopt::Number* x, bool new_x, bool new_lambda, Ipopt::Index nele_hess, Ipopt::Index* iRow, Ipopt::Index* jCol, Ipopt::Number* values) { - // We are using a quasi-Hessian approximation - return false; + // We are using a quasi-Hessian approximation + return false; } template <class GridView> @@ -406,50 +406,50 @@ finalize_solution(Ipopt::SolverReturn status, const Ipopt::IpoptData* ip_data, Ipopt::IpoptCalculatedQuantities* ip_cq) { - x_->resize(patch_->numVertices()); + x_->resize(patch_->numVertices()); - for (int i=0; i<x_->size(); i++) - for (int j=0; j<dim; j++) - (*x_)[i][j] = x[i*dim+j]; + for (int i=0; i<x_->size(); i++) + for (int j=0; j<dim; j++) + (*x_)[i][j] = x[i*dim+j]; } template <class GridView> void weakToStrongBoundaryStress(const BoundaryPatch<GridView>& boundary, - const Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> >& weakBoundaryStress, - Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> >& strongBoundaryStress) + const Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> >& weakBoundaryStress, + Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> >& strongBoundaryStress) { - static const int dim = GridView::dimension; - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,dim,dim> > MatrixType; - typedef Dune::BlockVector<Dune::FieldVector<double,dim> > VectorType; - - // turn residual (== weak form of the Neumann forces) to the strong form - MatrixType surfaceMassMatrix; - assembleSurfaceMassMatrix(boundary, surfaceMassMatrix); - - std::vector<int> globalToLocal; - boundary.makeGlobalToLocal(globalToLocal); - VectorType localWeakBoundaryStress(surfaceMassMatrix.N()); - assert(globalToLocal.size()==weakBoundaryStress.size()); - for (int i=0; i<globalToLocal.size(); i++) - if (globalToLocal[i] >= 0) - localWeakBoundaryStress[globalToLocal[i]] = weakBoundaryStress[i]; - - VectorType localStrongBoundaryStress(surfaceMassMatrix.N()); - localStrongBoundaryStress = 0; - - // solve with a cg solver - Dune::MatrixAdapter<MatrixType,VectorType,VectorType> op(surfaceMassMatrix); - Dune::SeqILU0<MatrixType,VectorType,VectorType> ilu0(surfaceMassMatrix,1.0); - Dune::CGSolver<VectorType> cg(op,ilu0,1E-4,100,0); - Dune::InverseOperatorResult statistics; - cg.apply(localStrongBoundaryStress, localWeakBoundaryStress, statistics); - - VectorType neumannForces(weakBoundaryStress.size()); - neumannForces = 0; - for (int i=0; i<globalToLocal.size(); i++) - if (globalToLocal[i] >= 0) - strongBoundaryStress[i] = localStrongBoundaryStress[globalToLocal[i]]; + static const int dim = GridView::dimension; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,dim,dim> > MatrixType; + typedef Dune::BlockVector<Dune::FieldVector<double,dim> > VectorType; + + // turn residual (== weak form of the Neumann forces) to the strong form + MatrixType surfaceMassMatrix; + assembleSurfaceMassMatrix(boundary, surfaceMassMatrix); + + std::vector<int> globalToLocal; + boundary.makeGlobalToLocal(globalToLocal); + VectorType localWeakBoundaryStress(surfaceMassMatrix.N()); + assert(globalToLocal.size()==weakBoundaryStress.size()); + for (int i=0; i<globalToLocal.size(); i++) + if (globalToLocal[i] >= 0) + localWeakBoundaryStress[globalToLocal[i]] = weakBoundaryStress[i]; + + VectorType localStrongBoundaryStress(surfaceMassMatrix.N()); + localStrongBoundaryStress = 0; + + // solve with a cg solver + Dune::MatrixAdapter<MatrixType,VectorType,VectorType> op(surfaceMassMatrix); + Dune::SeqILU0<MatrixType,VectorType,VectorType> ilu0(surfaceMassMatrix,1.0); + Dune::CGSolver<VectorType> cg(op,ilu0,1E-4,100,0); + Dune::InverseOperatorResult statistics; + cg.apply(localStrongBoundaryStress, localWeakBoundaryStress, statistics); + + VectorType neumannForces(weakBoundaryStress.size()); + neumannForces = 0; + for (int i=0; i<globalToLocal.size(); i++) + if (globalToLocal[i] >= 0) + strongBoundaryStress[i] = localStrongBoundaryStress[globalToLocal[i]]; } @@ -461,54 +461,54 @@ void computeTotalForceAndTorque(const BoundaryPatch<GridView>& interface, const Dune::FieldVector<double,3>& center, Dune::FieldVector<double,3>& totalForce, Dune::FieldVector<double,3>& totalTorque) { - const int dim = GridView::dimension; - - // /////////////////////////////////////////// - // Initialize output configuration - // /////////////////////////////////////////// - totalForce = 0; - totalTorque = 0; + const int dim = GridView::dimension; - //////////////////////////////////////////////////////////////// - // Interpret the Neumann value coefficients as a P1 function - //////////////////////////////////////////////////////////////// - typedef P1NodalBasis<GridView,double> P1Basis; - P1Basis p1Basis(interface.gridView()); - BasisGridFunction<P1Basis, Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> > > neumannFunction(p1Basis, boundaryStress); + // /////////////////////////////////////////// + // Initialize output configuration + // /////////////////////////////////////////// + totalForce = 0; + totalTorque = 0; - // /////////////////////////////////////////// - // Loop and integrate over the interface - // /////////////////////////////////////////// + //////////////////////////////////////////////////////////////// + // Interpret the Neumann value coefficients as a P1 function + //////////////////////////////////////////////////////////////// + typedef P1NodalBasis<GridView,double> P1Basis; + P1Basis p1Basis(interface.gridView()); + BasisGridFunction<P1Basis, Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> > > neumannFunction(p1Basis, boundaryStress); - typename BoundaryPatch<GridView>::iterator it = interface.begin(); - typename BoundaryPatch<GridView>::iterator endIt = interface.end(); + // /////////////////////////////////////////// + // Loop and integrate over the interface + // /////////////////////////////////////////// - for (; it!=endIt; ++it) { + typename BoundaryPatch<GridView>::iterator it = interface.begin(); + typename BoundaryPatch<GridView>::iterator endIt = interface.end(); - const typename BoundaryPatch<GridView>::iterator::Intersection::Geometry& segmentGeometry = it->geometry(); + for (; it!=endIt; ++it) { - // Get quadrature rule - const Dune::QuadratureRule<double, dim-1>& quad = Dune::QuadratureRules<double, dim-1>::rule(segmentGeometry.type(), dim-1); + const typename BoundaryPatch<GridView>::iterator::Intersection::Geometry& segmentGeometry = it->geometry(); - /* Loop over all integration points */ - for (size_t ip=0; ip<quad.size(); ip++) { + // Get quadrature rule + const Dune::QuadratureRule<double, dim-1>& quad = Dune::QuadratureRules<double, dim-1>::rule(segmentGeometry.type(), dim-1); - // Local position of the quadrature point - const Dune::FieldVector<double,dim> quadPos = it->geometryInInside().global(quad[ip].position()); - const Dune::FieldVector<double,dim> worldPos = it->geometry().global(quad[ip].position()); + /* Loop over all integration points */ + for (size_t ip=0; ip<quad.size(); ip++) { - const double integrationElement = segmentGeometry.integrationElement(quad[ip].position()); + // Local position of the quadrature point + const Dune::FieldVector<double,dim> quadPos = it->geometryInInside().global(quad[ip].position()); + const Dune::FieldVector<double,dim> worldPos = it->geometry().global(quad[ip].position()); - Dune::FieldVector<double,dim> value; - neumannFunction.evaluateLocal(*it->inside(), quadPos, value); + const double integrationElement = segmentGeometry.integrationElement(quad[ip].position()); - totalForce.axpy(quad[ip].weight() * integrationElement, value); - totalTorque.axpy(quad[ip].weight() * integrationElement, MatrixVector::crossProduct(worldPos-center,value)); + Dune::FieldVector<double,dim> value; + neumannFunction.evaluateLocal(*it->inside(), quadPos, value); - } + totalForce.axpy(quad[ip].weight() * integrationElement, value); + totalTorque.axpy(quad[ip].weight() * integrationElement, MatrixVector::crossProduct(worldPos-center,value)); } + } + } @@ -520,196 +520,196 @@ void computeAveragePressure(const typename RigidBodyMotion<double,GridView::dime const Dune::FieldVector<double,GridView::dimension>& centerOfTorque, Dune::BlockVector<Dune::FieldVector<double, GridView::dimension> >& pressure) { - const GridView& gridView = interface.gridView(); - const typename GridView::IndexSet& indexSet = gridView.indexSet(); - const int dim = GridView::dimension; - typedef double field_type; - - Dune::LagrangeLocalFiniteElementCache<double,double, dim, 1> finiteElementCache; - - // Create the matrix of constraints - Dune::BCRSMatrix<Dune::FieldMatrix<field_type,1,1> > constraints(2*dim, dim*interface.numVertices(), - Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >::random); - - for (int i=0; i<dim; i++) { - constraints.setrowsize(i, interface.numVertices()); - constraints.setrowsize(i+dim, dim*interface.numVertices()); - } - - constraints.endrowsizes(); - - for (int i=0; i<dim; i++) - for (int j=0; j<interface.numVertices(); j++) - constraints.addindex(i, dim*j+i); - - for (int i=0; i<dim; i++) - for (int j=0; j<dim*interface.numVertices(); j++) - constraints.addindex(i+dim, j); + const GridView& gridView = interface.gridView(); + const typename GridView::IndexSet& indexSet = gridView.indexSet(); + const int dim = GridView::dimension; + typedef double field_type; - constraints.endindices(); + Dune::LagrangeLocalFiniteElementCache<double,double, dim, 1> finiteElementCache; - constraints = 0; + // Create the matrix of constraints + Dune::BCRSMatrix<Dune::FieldMatrix<field_type,1,1> > constraints(2*dim, dim*interface.numVertices(), + Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> >::random); - // Create the surface mass matrix - Dune::BCRSMatrix<Dune::FieldMatrix<field_type,1,1> > massMatrix; - assembleSurfaceMassMatrix<GridView, 1>(interface, massMatrix); + for (int i=0; i<dim; i++) { + constraints.setrowsize(i, interface.numVertices()); + constraints.setrowsize(i+dim, dim*interface.numVertices()); + } - // Make global-to-local array - std::vector<int> globalToLocal; - interface.makeGlobalToLocal(globalToLocal); + constraints.endrowsizes(); - // Make array of nodal weights - Dune::BlockVector<Dune::FieldVector<double,1> > nodalWeights(interface.numVertices()); - nodalWeights = 0; + for (int i=0; i<dim; i++) + for (int j=0; j<interface.numVertices(); j++) + constraints.addindex(i, dim*j+i); - typename BoundaryPatch<GridView>::iterator it = interface.begin(); - typename BoundaryPatch<GridView>::iterator endIt = interface.end(); + for (int i=0; i<dim; i++) + for (int j=0; j<dim*interface.numVertices(); j++) + constraints.addindex(i+dim, j); - for (; it!=endIt; ++it) { + constraints.endindices(); - // Get shape functions - const typename Dune::LagrangeLocalFiniteElementCache<double,double, dim, 1>::FiniteElementType& - localFiniteElement = finiteElementCache.get(it->inside()->type()); + constraints = 0; - const Dune::ReferenceElement<double,dim>& refElement = Dune::ReferenceElements<double, dim>::general(it->inside()->type()); + // Create the surface mass matrix + Dune::BCRSMatrix<Dune::FieldMatrix<field_type,1,1> > massMatrix; + assembleSurfaceMassMatrix<GridView, 1>(interface, massMatrix); - // four rows because a face may have no more than four vertices - Dune::FieldVector<double,4> mu(0); - Dune::FieldVector<double,3> mu_tilde[4][3]; + // Make global-to-local array + std::vector<int> globalToLocal; + interface.makeGlobalToLocal(globalToLocal); - for (int i=0; i<4; i++) - for (int j=0; j<3; j++) - mu_tilde[i][j] = 0; + // Make array of nodal weights + Dune::BlockVector<Dune::FieldVector<double,1> > nodalWeights(interface.numVertices()); + nodalWeights = 0; - for (int i=0; i<it->geometry().corners(); i++) { + typename BoundaryPatch<GridView>::iterator it = interface.begin(); + typename BoundaryPatch<GridView>::iterator endIt = interface.end(); - const Dune::QuadratureRule<double, dim-1>& quad - = Dune::QuadratureRules<double, dim-1>::rule(it->type(), dim-1); + for (; it!=endIt; ++it) { - for (size_t qp=0; qp<quad.size(); qp++) { + // Get shape functions + const typename Dune::LagrangeLocalFiniteElementCache<double,double, dim, 1>::FiniteElementType& + localFiniteElement = finiteElementCache.get(it->inside()->type()); - // Local position of the quadrature point - const Dune::FieldVector<double,dim>& quadPos = it->geometryInInside().global(quad[qp].position()); + const Dune::ReferenceElement<double,dim>& refElement = Dune::ReferenceElements<double, dim>::general(it->inside()->type()); - const double integrationElement = it->geometry().integrationElement(quad[qp].position()); + // four rows because a face may have no more than four vertices + Dune::FieldVector<double,4> mu(0); + Dune::FieldVector<double,3> mu_tilde[4][3]; - std::vector<Dune::FieldVector<double,1> > shapeFunctionValues; - localFiniteElement.localBasis().evaluateFunction(quadPos, shapeFunctionValues); + for (int i=0; i<4; i++) + for (int j=0; j<3; j++) + mu_tilde[i][j] = 0; - // \mu_i = \int_t \varphi_i \ds - int indexInFace = refElement.subEntity(it->indexInInside(), 1, i, dim); - mu[i] += quad[qp].weight() * integrationElement * shapeFunctionValues[indexInFace]; + for (int i=0; i<it->geometry().corners(); i++) { - // \tilde{\mu}_i^j = \int_t (x - x_0) \times \varphi_i \ds - Dune::FieldVector<double,dim> worldPos = it->geometry().global(quad[qp].position()); + const Dune::QuadratureRule<double, dim-1>& quad + = Dune::QuadratureRules<double, dim-1>::rule(it->type(), dim-1); - for (int j=0; j<dim; j++) { + for (size_t qp=0; qp<quad.size(); qp++) { - // Vector-valued basis function - Dune::FieldVector<double,dim> phi_i(0); - phi_i[j] = shapeFunctionValues[indexInFace]; + // Local position of the quadrature point + const Dune::FieldVector<double,dim>& quadPos = it->geometryInInside().global(quad[qp].position()); - mu_tilde[i][j].axpy(quad[qp].weight() * integrationElement, - Arithmetic::crossProduct(Dune::FieldVector<double,dim>(worldPos-centerOfTorque), phi_i)); + const double integrationElement = it->geometry().integrationElement(quad[qp].position()); - } + std::vector<Dune::FieldVector<double,1> > shapeFunctionValues; + localFiniteElement.localBasis().evaluateFunction(quadPos, shapeFunctionValues); - } + // \mu_i = \int_t \varphi_i \ds + int indexInFace = refElement.subEntity(it->indexInInside(), 1, i, dim); + mu[i] += quad[qp].weight() * integrationElement * shapeFunctionValues[indexInFace]; - } + // \tilde{\mu}_i^j = \int_t (x - x_0) \times \varphi_i \ds + Dune::FieldVector<double,dim> worldPos = it->geometry().global(quad[qp].position()); - // Set up matrix - for (int i=0; i<refElement.size(it->indexInInside(),1,dim); i++) { + for (int j=0; j<dim; j++) { - int faceIdxi = refElement.subEntity(it->indexInInside(), 1, i, dim); - int subIndex = globalToLocal[indexSet.subIndex(*it->inside(), faceIdxi, dim)]; + // Vector-valued basis function + Dune::FieldVector<double,dim> phi_i(0); + phi_i[j] = shapeFunctionValues[indexInFace]; - nodalWeights[subIndex] += mu[i]; - for (int j=0; j<dim; j++) - constraints[j][subIndex*dim+j] += mu[i]; + mu_tilde[i][j].axpy(quad[qp].weight() * integrationElement, + Arithmetic::crossProduct(Dune::FieldVector<double,dim>(worldPos-centerOfTorque), phi_i)); - for (int j=0; j<3; j++) - for (int k=0; k<3; k++) - constraints[dim+k][dim*subIndex+j] += mu_tilde[i][j][k]; - - } - - } - - //printmatrix(std::cout, constraints, "jacobian", "--"); - //printmatrix(std::cout, massMatrix, "mass", "--"); - - // ///////////////////////////////////////////////////////////////////////////////////// - // Set up and start the interior-point solver - // ///////////////////////////////////////////////////////////////////////////////////// + } - Dune::FieldVector<double,dim> resultantForce, resultantTorque; - for (int i=0; i<dim; i++) { - resultantForce[i] = resultantForceTorque[i]; - resultantTorque[i] = resultantForceTorque[dim+i]; - } + } - // Create a new instance of IpoptApplication - Ipopt::SmartPtr<Ipopt::IpoptApplication> app = new Ipopt::IpoptApplication(); - - // Change some options - app->Options()->SetNumericValue("tol", 1e-8); - app->Options()->SetIntegerValue("max_iter", 1000); - app->Options()->SetStringValue("mu_strategy", "adaptive"); - app->Options()->SetStringValue("output_file", "ipopt.out"); - app->Options()->SetStringValue("hessian_approximation", "limited-memory"); - app->Options()->SetStringValue("jac_c_constant", "yes"); - app->Options()->SetIntegerValue("print_level", 0); - - // Initialize the IpoptApplication and process the options - Ipopt::ApplicationReturnStatus status; - status = app->Initialize(); - if (status != Ipopt::Solve_Succeeded) - DUNE_THROW(SolverError, "Error during IPOpt initialization!"); - - // Ask Ipopt to solve the problem - Dune::BlockVector<Dune::FieldVector<double,dim> > localPressure; - Ipopt::SmartPtr<Ipopt::TNLP> defectSolverSmart = new PressureAverager<GridView>(&interface, - &localPressure, - resultantForce, - resultantTorque, - &massMatrix, - &nodalWeights, - &constraints); - status = app->OptimizeTNLP(defectSolverSmart); - - if (status != Ipopt::Solve_Succeeded - && status != Ipopt::Solved_To_Acceptable_Level) { - //DUNE_THROW(SolverError, "Solving the defect problem failed!"); - std::cout << "IPOpt returned error code " << status << "!" << std::endl; } - // ////////////////////////////////////////////////////////////////////////////// - // Get result - // ////////////////////////////////////////////////////////////////////////////// + // Set up matrix + for (int i=0; i<refElement.size(it->indexInInside(),1,dim); i++) { - // set up output array - pressure.resize(indexSet.size(dim)); - pressure = 0; + int faceIdxi = refElement.subEntity(it->indexInInside(), 1, i, dim); + int subIndex = globalToLocal[indexSet.subIndex(*it->inside(), faceIdxi, dim)]; - for (size_t i=0; i<globalToLocal.size(); i++) - if (globalToLocal[i]>=0) - pressure[i] = localPressure[globalToLocal[i]]; + nodalWeights[subIndex] += mu[i]; + for (int j=0; j<dim; j++) + constraints[j][subIndex*dim+j] += mu[i]; - // ///////////////////////////////////////////////////////////////////////////////////// - // Compute the overall force and torque to see whether the preceding code is correct - // ///////////////////////////////////////////////////////////////////////////////////// + for (int j=0; j<3; j++) + for (int k=0; k<3; k++) + constraints[dim+k][dim*subIndex+j] += mu_tilde[i][j][k]; - Dune::FieldVector<double,3> outputForce(0), outputTorque(0); - - computeTotalForceAndTorque(interface, pressure, centerOfTorque, outputForce, outputTorque); + } - outputForce -= resultantForce; - outputTorque -= resultantTorque; - assert( outputForce.infinity_norm() < 1e-6 ); - assert( outputTorque.infinity_norm() < 1e-6 ); -// std::cout << "Output force: " << outputForce << std::endl; -// std::cout << "Output torque: " << outputTorque << " " << resultantTorque[0]/outputTorque[0] << std::endl; + } + + //printmatrix(std::cout, constraints, "jacobian", "--"); + //printmatrix(std::cout, massMatrix, "mass", "--"); + + // ///////////////////////////////////////////////////////////////////////////////////// + // Set up and start the interior-point solver + // ///////////////////////////////////////////////////////////////////////////////////// + + Dune::FieldVector<double,dim> resultantForce, resultantTorque; + for (int i=0; i<dim; i++) { + resultantForce[i] = resultantForceTorque[i]; + resultantTorque[i] = resultantForceTorque[dim+i]; + } + + // Create a new instance of IpoptApplication + Ipopt::SmartPtr<Ipopt::IpoptApplication> app = new Ipopt::IpoptApplication(); + + // Change some options + app->Options()->SetNumericValue("tol", 1e-8); + app->Options()->SetIntegerValue("max_iter", 1000); + app->Options()->SetStringValue("mu_strategy", "adaptive"); + app->Options()->SetStringValue("output_file", "ipopt.out"); + app->Options()->SetStringValue("hessian_approximation", "limited-memory"); + app->Options()->SetStringValue("jac_c_constant", "yes"); + app->Options()->SetIntegerValue("print_level", 0); + + // Initialize the IpoptApplication and process the options + Ipopt::ApplicationReturnStatus status; + status = app->Initialize(); + if (status != Ipopt::Solve_Succeeded) + DUNE_THROW(SolverError, "Error during IPOpt initialization!"); + + // Ask Ipopt to solve the problem + Dune::BlockVector<Dune::FieldVector<double,dim> > localPressure; + Ipopt::SmartPtr<Ipopt::TNLP> defectSolverSmart = new PressureAverager<GridView>(&interface, + &localPressure, + resultantForce, + resultantTorque, + &massMatrix, + &nodalWeights, + &constraints); + status = app->OptimizeTNLP(defectSolverSmart); + + if (status != Ipopt::Solve_Succeeded + && status != Ipopt::Solved_To_Acceptable_Level) { + //DUNE_THROW(SolverError, "Solving the defect problem failed!"); + std::cout << "IPOpt returned error code " << status << "!" << std::endl; + } + + // ////////////////////////////////////////////////////////////////////////////// + // Get result + // ////////////////////////////////////////////////////////////////////////////// + + // set up output array + pressure.resize(indexSet.size(dim)); + pressure = 0; + + for (size_t i=0; i<globalToLocal.size(); i++) + if (globalToLocal[i]>=0) + pressure[i] = localPressure[globalToLocal[i]]; + + // ///////////////////////////////////////////////////////////////////////////////////// + // Compute the overall force and torque to see whether the preceding code is correct + // ///////////////////////////////////////////////////////////////////////////////////// + + Dune::FieldVector<double,3> outputForce(0), outputTorque(0); + + computeTotalForceAndTorque(interface, pressure, centerOfTorque, outputForce, outputTorque); + + outputForce -= resultantForce; + outputTorque -= resultantTorque; + assert( outputForce.infinity_norm() < 1e-6 ); + assert( outputTorque.infinity_norm() < 1e-6 ); + // std::cout << "Output force: " << outputForce << std::endl; + // std::cout << "Output torque: " << outputTorque << " " << resultantTorque[0]/outputTorque[0] << std::endl; } @@ -718,145 +718,145 @@ void computeAverageInterface(const BoundaryPatch<GridView>& interface, const Dune::BlockVector<Dune::FieldVector<double,GridView::dimension> > deformation, RigidBodyMotion<double,3>& average) { - using namespace Dune; - - typedef typename GridView::template Codim<0>::Entity EntityType; - typedef typename GridView::IntersectionIterator NeighborIterator; + using namespace Dune; - const GridView& gridView = interface.gridView(); - const typename GridView::IndexSet& indexSet = gridView.indexSet(); - const int dim = GridView::dimension; + typedef typename GridView::template Codim<0>::Entity EntityType; + typedef typename GridView::IntersectionIterator NeighborIterator; - Dune::LagrangeLocalFiniteElementCache<double,double, dim, 1> finiteElementCache; + const GridView& gridView = interface.gridView(); + const typename GridView::IndexSet& indexSet = gridView.indexSet(); + const int dim = GridView::dimension; - // /////////////////////////////////////////// - // Initialize output configuration - // /////////////////////////////////////////// - average.r = 0; + Dune::LagrangeLocalFiniteElementCache<double,double, dim, 1> finiteElementCache; - double interfaceArea = 0; - FieldMatrix<double,dim,dim> deformationGradient(0); + // /////////////////////////////////////////// + // Initialize output configuration + // /////////////////////////////////////////// + average.r = 0; - // /////////////////////////////////////////// - // Loop and integrate over the interface - // /////////////////////////////////////////// + double interfaceArea = 0; + FieldMatrix<double,dim,dim> deformationGradient(0); - typename BoundaryPatch<GridView>::iterator it = interface.begin(); - typename BoundaryPatch<GridView>::iterator endIt = interface.end(); + // /////////////////////////////////////////// + // Loop and integrate over the interface + // /////////////////////////////////////////// - for (; it!=endIt; ++it) { + typename BoundaryPatch<GridView>::iterator it = interface.begin(); + typename BoundaryPatch<GridView>::iterator endIt = interface.end(); - const typename NeighborIterator::Intersection::Geometry& segmentGeometry = it->geometry(); + for (; it!=endIt; ++it) { - // Get quadrature rule - const QuadratureRule<double, dim-1>& quad = QuadratureRules<double, dim-1>::rule(segmentGeometry.type(), dim-1); + const typename NeighborIterator::Intersection::Geometry& segmentGeometry = it->geometry(); - // Get set of shape functions on this segment - const typename Dune::LagrangeLocalFiniteElementCache<double,double, dim, 1>::FiniteElementType& - localFiniteElement = finiteElementCache.get(it->inside()->type()); + // Get quadrature rule + const QuadratureRule<double, dim-1>& quad = QuadratureRules<double, dim-1>::rule(segmentGeometry.type(), dim-1); - /* Loop over all integration points */ - for (int ip=0; ip<quad.size(); ip++) { + // Get set of shape functions on this segment + const typename Dune::LagrangeLocalFiniteElementCache<double,double, dim, 1>::FiniteElementType& + localFiniteElement = finiteElementCache.get(it->inside()->type()); - // Local position of the quadrature point - const FieldVector<double,dim> quadPos = it->geometryInInside().global(quad[ip].position()); + /* Loop over all integration points */ + for (int ip=0; ip<quad.size(); ip++) { - const double integrationElement = segmentGeometry.integrationElement(quad[ip].position()); + // Local position of the quadrature point + const FieldVector<double,dim> quadPos = it->geometryInInside().global(quad[ip].position()); - std::vector<Dune::FieldVector<double,1> > values; - localFiniteElement.localBasis().evaluateFunction(quadPos,values); + const double integrationElement = segmentGeometry.integrationElement(quad[ip].position()); - // Evaluate base functions - FieldVector<double,dim> posAtQuadPoint(0); + std::vector<Dune::FieldVector<double,1> > values; + localFiniteElement.localBasis().evaluateFunction(quadPos,values); - for(int i=0; i<it->inside()->geometry().corners(); i++) { + // Evaluate base functions + FieldVector<double,dim> posAtQuadPoint(0); - int idx = indexSet.subIndex(*it->inside(), i, dim); + for(int i=0; i<it->inside()->geometry().corners(); i++) { - // Deformation at the quadrature point - posAtQuadPoint.axpy(values[i], deformation[idx]); - } + int idx = indexSet.subIndex(*it->inside(), i, dim); - const FieldMatrix<double,dim,dim>& inv = it->inside()->geometry().jacobianInverseTransposed(quadPos); + // Deformation at the quadrature point + posAtQuadPoint.axpy(values[i], deformation[idx]); + } - /**********************************************/ - /* compute gradients of the shape functions */ - /**********************************************/ - std::vector<FieldMatrix<double, 1, dim> > shapeGrads; - localFiniteElement.localBasis().evaluateJacobian(quadPos,shapeGrads); + const FieldMatrix<double,dim,dim>& inv = it->inside()->geometry().jacobianInverseTransposed(quadPos); - for (int dof=0; dof<it->inside()->geometry().corners(); dof++) { + /**********************************************/ + /* compute gradients of the shape functions */ + /**********************************************/ + std::vector<FieldMatrix<double, 1, dim> > shapeGrads; + localFiniteElement.localBasis().evaluateJacobian(quadPos,shapeGrads); - FieldVector<double,dim> tmp = shapeGrads[dof][0]; + for (int dof=0; dof<it->inside()->geometry().corners(); dof++) { - // multiply with jacobian inverse - shapeGrads[dof] = 0; - inv.umv(tmp, shapeGrads[dof][0]); - } + FieldVector<double,dim> tmp = shapeGrads[dof][0]; - /****************************************************/ - // The deformation gradient of the deformation - // in formulas: F(i,j) = $\partial \phi_i / \partial x_j$ - // or F(i,j) = Id + $\partial u_i / \partial x_j$ - /****************************************************/ - FieldMatrix<double, dim, dim> F; - for (int i=0; i<dim; i++) { + // multiply with jacobian inverse + shapeGrads[dof] = 0; + inv.umv(tmp, shapeGrads[dof][0]); + } - for (int j=0; j<dim; j++) { + /****************************************************/ + // The deformation gradient of the deformation + // in formulas: F(i,j) = $\partial \phi_i / \partial x_j$ + // or F(i,j) = Id + $\partial u_i / \partial x_j$ + /****************************************************/ + FieldMatrix<double, dim, dim> F; + for (int i=0; i<dim; i++) { - F[i][j] = (i==j) ? 1 : 0; + for (int j=0; j<dim; j++) { - for (int k=0; k<it->inside()->geometry().corners(); k++) - F[i][j] += deformation[indexSet.subIndex(*it->inside(), k, dim)][i]*shapeGrads[k][0][j]; + F[i][j] = (i==j) ? 1 : 0; - } + for (int k=0; k<it->inside()->geometry().corners(); k++) + F[i][j] += deformation[indexSet.subIndex(*it->inside(), k, dim)][i]*shapeGrads[k][0][j]; - } + } - // Sum up interface area - interfaceArea += quad[ip].weight() * integrationElement; + } - // Sum up average displacement - average.r.axpy(quad[ip].weight() * integrationElement, posAtQuadPoint); + // Sum up interface area + interfaceArea += quad[ip].weight() * integrationElement; - // Sum up average deformation gradient - for (int i=0; i<dim; i++) - for (int j=0; j<dim; j++) - deformationGradient[i][j] += F[i][j] * quad[ip].weight() * integrationElement; + // Sum up average displacement + average.r.axpy(quad[ip].weight() * integrationElement, posAtQuadPoint); - } + // Sum up average deformation gradient + for (int i=0; i<dim; i++) + for (int j=0; j<dim; j++) + deformationGradient[i][j] += F[i][j] * quad[ip].weight() * integrationElement; } + } + - // average deformation of the interface is the integral over its - // deformation divided by its area - average.r /= interfaceArea; + // average deformation of the interface is the integral over its + // deformation divided by its area + average.r /= interfaceArea; - // average deformation is the integral over the deformation gradient - // divided by its area - deformationGradient /= interfaceArea; - //std::cout << "deformationGradient: " << std::endl << deformationGradient << std::endl; + // average deformation is the integral over the deformation gradient + // divided by its area + deformationGradient /= interfaceArea; + //std::cout << "deformationGradient: " << std::endl << deformationGradient << std::endl; - // Get the rotational part of the deformation gradient by performing a - // polar composition. - FieldVector<double,dim> W; - FieldMatrix<double,dim,dim> V, VT, U; + // Get the rotational part of the deformation gradient by performing a + // polar composition. + FieldVector<double,dim> W; + FieldMatrix<double,dim,dim> V, VT, U; - // returns a decomposition U W VT, where U is returned in the first argument - svdcmp<double,dim,dim>(deformationGradient, W, V); + // returns a decomposition U W VT, where U is returned in the first argument + svdcmp<double,dim,dim>(deformationGradient, W, V); - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) - VT[i][j] = V[j][i]; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + VT[i][j] = V[j][i]; - deformationGradient.rightmultiply(VT); - //std::cout << deformationGradient << std::endl; + deformationGradient.rightmultiply(VT); + //std::cout << deformationGradient << std::endl; - // deformationGradient now contains the orthogonal part of the polar decomposition - assert( std::abs(1-deformationGradient.determinant()) < 1e-3); + // deformationGradient now contains the orthogonal part of the polar decomposition + assert( std::abs(1-deformationGradient.determinant()) < 1e-3); - average.q.set(deformationGradient); + average.q.set(deformationGradient); } /** \brief Set a Dirichlet value that corresponds to a given rigid body motion @@ -867,52 +867,52 @@ void setRotation(const BoundaryPatch<GridView>& dirichletBoundary, const RigidBodyMotion<double,3>& referenceInterface, const RigidBodyMotion<double,3>& lambda) { - const typename GridView::IndexSet& indexSet = dirichletBoundary.gridView().indexSet(); - const int dim = GridView::dimension; - const int dimworld = GridView::dimensionworld; + const typename GridView::IndexSet& indexSet = dirichletBoundary.gridView().indexSet(); + const int dim = GridView::dimension; + const int dimworld = GridView::dimensionworld; - // Get the relative rotation, first as a quaternion... - Rotation<double,3> relativeRotation; - relativeRotation = Rotation<double,3>(referenceInterface.q.inverse()); - relativeRotation = lambda.q.mult(relativeRotation); + // Get the relative rotation, first as a quaternion... + Rotation<double,3> relativeRotation; + relativeRotation = Rotation<double,3>(referenceInterface.q.inverse()); + relativeRotation = lambda.q.mult(relativeRotation); - // ... then as a matrix - Dune::FieldMatrix<double,3,3> rotation; - relativeRotation.matrix(rotation); + // ... then as a matrix + Dune::FieldMatrix<double,3,3> rotation; + relativeRotation.matrix(rotation); - // /////////////////////////////////////////// - // Loop over all vertices - // /////////////////////////////////////////// + // /////////////////////////////////////////// + // Loop over all vertices + // /////////////////////////////////////////// - typename BoundaryPatch<GridView>::iterator it = dirichletBoundary.begin(); - typename BoundaryPatch<GridView>::iterator endIt = dirichletBoundary.end(); + typename BoundaryPatch<GridView>::iterator it = dirichletBoundary.begin(); + typename BoundaryPatch<GridView>::iterator endIt = dirichletBoundary.end(); - for (; it!=endIt; ++it) { + for (; it!=endIt; ++it) { - int indexInInside = it->indexInInside(); - int nCorners = Dune::ReferenceElements<double,dim>::general(it->inside()->type()).size(indexInInside, 1, dim); + int indexInInside = it->indexInInside(); + int nCorners = Dune::ReferenceElements<double,dim>::general(it->inside()->type()).size(indexInInside, 1, dim); - for (int i=0; i<nCorners; i++) { - int cornerIdx = Dune::ReferenceElements<double,dim>::general(it->inside()->type()).subEntity(it->indexInInside(), 1, i, dim); - int globalIdx = indexSet.subIndex(*it->inside(), cornerIdx, dim); + for (int i=0; i<nCorners; i++) { + int cornerIdx = Dune::ReferenceElements<double,dim>::general(it->inside()->type()).subEntity(it->indexInInside(), 1, i, dim); + int globalIdx = indexSet.subIndex(*it->inside(), cornerIdx, dim); - // Get vertex position - const Dune::FieldVector<double,dimworld> pos = it->inside()->geometry().corner(cornerIdx); + // Get vertex position + const Dune::FieldVector<double,dimworld> pos = it->inside()->geometry().corner(cornerIdx); - // Action of the rigid body motion - Dune::FieldVector<double,dimworld> rpos; - rotation.mv(pos-referenceInterface.r, rpos); - rpos += lambda.r; + // Action of the rigid body motion + Dune::FieldVector<double,dimworld> rpos; + rotation.mv(pos-referenceInterface.r, rpos); + rpos += lambda.r; - // We compute _displacements_, not positions - rpos -= pos; + // We compute _displacements_, not positions + rpos -= pos; - deformation[globalIdx] = rpos; - - } + deformation[globalIdx] = rpos; } + } + } #endif diff --git a/dune/gfe/cosseratstrain.hh b/dune/gfe/cosseratstrain.hh index f69141f1..84c99372 100644 --- a/dune/gfe/cosseratstrain.hh +++ b/dune/gfe/cosseratstrain.hh @@ -33,24 +33,24 @@ namespace Dune { In the continuum case (domain dimension == world dimension) this is \f$ \hat{F} = \nabla m \f$ In the case of a shell it is - \f$ \hat{F} = (\nabla m | \overline{R}_3) \f$ - */ + \f$ \hat{F} = (\nabla m | \overline{R}_3) \f$ + */ FieldMatrix<T,dimworld,dimworld> F; for (int i=0; i<dimworld; i++) - for (int j=0; j<dim; j++) - F[i][j] = deformationGradient[i][j]; + for (int j=0; j<dim; j++) + F[i][j] = deformationGradient[i][j]; for (int i=0; i<dimworld; i++) - for (int j=dim; j<dimworld; j++) - F[i][j] = R[i][j]; + for (int j=dim; j<dimworld; j++) + F[i][j] = R[i][j]; // U = R^T F for (int i=0; i<dimworld; i++) - for (int j=0; j<dimworld; j++) { - data_[i][j] = 0; - for (int k=0; k<dimworld; k++) - data_[i][j] += R[k][i] * F[k][j]; - } + for (int j=0; j<dimworld; j++) { + data_[i][j] = 0; + for (int k=0; k<dimworld; k++) + data_[i][j] += R[k][i] * F[k][j]; + } } @@ -65,24 +65,24 @@ namespace Dune { In the continuum case (domain dimension == world dimension) this is \f$ \hat{F} = \nabla m \f$ In the case of a shell it is - \f$ \hat{F} = (\nabla m | \overline{R}_3) \f$ - */ + \f$ \hat{F} = (\nabla m | \overline{R}_3) \f$ + */ FieldMatrix<T,dimworld,dimworld> F; for (int i=0; i<dimworld; i++) - for (int j=0; j<dim; j++) - F[i][j] = deformationGradient[i][j]; + for (int j=0; j<dim; j++) + F[i][j] = deformationGradient[i][j]; for (int i=0; i<dimworld; i++) - for (int j=dim; j<dimworld; j++) - F[i][j] = R[i][j]; + for (int j=dim; j<dimworld; j++) + F[i][j] = R[i][j]; // U = R^T F for (int i=0; i<dimworld; i++) - for (int j=0; j<dimworld; j++) { - data_[i][j] = 0; - for (int k=0; k<dimworld; k++) - data_[i][j] += R[k][i] * F[k][j]; - } + for (int j=0; j<dimworld; j++) { + data_[i][j] = 0; + for (int k=0; k<dimworld; k++) + data_[i][j] += R[k][i] * F[k][j]; + } } @@ -105,4 +105,4 @@ namespace Dune { } // namespace Dune -#endif // DUNE_GFE_COSSERATSTRAIN_HH \ No newline at end of file +#endif // DUNE_GFE_COSSERATSTRAIN_HH diff --git a/dune/gfe/cosseratvtkwriter.hh b/dune/gfe/cosseratvtkwriter.hh index 84799c74..feb41c41 100644 --- a/dune/gfe/cosseratvtkwriter.hh +++ b/dune/gfe/cosseratvtkwriter.hh @@ -19,545 +19,545 @@ template <class GridType> class CosseratVTKWriter { - static const int dim = GridType::dimension; - - template <typename Basis1, typename Basis2> - static void downsample(const Basis1& basis1, const std::vector<RigidBodyMotion<double,3> >& v1, - const Basis2& basis2, std::vector<RigidBodyMotion<double,3> >& v2) - { - // Embed v1 into R^7 - std::vector<Dune::FieldVector<double,7> > v1Embedded(v1.size()); - for (size_t i=0; i<v1.size(); i++) - v1Embedded[i] = v1[i].globalCoordinates(); - - // Interpolate - auto function = Dune::Functions::makeDiscreteGlobalBasisFunction<Dune::FieldVector<double,7> >(basis1, v1Embedded); - std::vector<Dune::FieldVector<double,7> > v2Embedded; - Dune::Functions::interpolate(basis2, v2Embedded, function); - - // Copy back from R^7 into RigidBodyMotions - v2.resize(v2Embedded.size()); - for (size_t i=0; i<v2.size(); i++) - v2[i] = RigidBodyMotion<double,3>(v2Embedded[i]); - } - - template <typename Basis1, typename Basis2> - static void downsample(const Basis1& basis1, const std::vector<RealTuple<double,3> >& v1, - const Basis2& basis2, std::vector<RealTuple<double,3> >& v2) - { - // Copy from RealTuple to FieldVector - std::vector<Dune::FieldVector<double,3> > v1Embedded(v1.size()); - for (size_t i=0; i<v1.size(); i++) - v1Embedded[i] = v1[i].globalCoordinates(); - - // Interpolate - auto function = Dune::Functions::makeDiscreteGlobalBasisFunction<Dune::FieldVector<double,3> >(basis1, v1Embedded); - std::vector<Dune::FieldVector<double,3> > v2Embedded; - Dune::Functions::interpolate(basis2, v2Embedded, function); - - // Copy back from FieldVector to RealTuple - v2.resize(v2Embedded.size()); - for (size_t i=0; i<v2.size(); i++) - v2[i] = RealTuple<double,3>(v2Embedded[i]); + static const int dim = GridType::dimension; + + template <typename Basis1, typename Basis2> + static void downsample(const Basis1& basis1, const std::vector<RigidBodyMotion<double,3> >& v1, + const Basis2& basis2, std::vector<RigidBodyMotion<double,3> >& v2) + { + // Embed v1 into R^7 + std::vector<Dune::FieldVector<double,7> > v1Embedded(v1.size()); + for (size_t i=0; i<v1.size(); i++) + v1Embedded[i] = v1[i].globalCoordinates(); + + // Interpolate + auto function = Dune::Functions::makeDiscreteGlobalBasisFunction<Dune::FieldVector<double,7> >(basis1, v1Embedded); + std::vector<Dune::FieldVector<double,7> > v2Embedded; + Dune::Functions::interpolate(basis2, v2Embedded, function); + + // Copy back from R^7 into RigidBodyMotions + v2.resize(v2Embedded.size()); + for (size_t i=0; i<v2.size(); i++) + v2[i] = RigidBodyMotion<double,3>(v2Embedded[i]); + } + + template <typename Basis1, typename Basis2> + static void downsample(const Basis1& basis1, const std::vector<RealTuple<double,3> >& v1, + const Basis2& basis2, std::vector<RealTuple<double,3> >& v2) + { + // Copy from RealTuple to FieldVector + std::vector<Dune::FieldVector<double,3> > v1Embedded(v1.size()); + for (size_t i=0; i<v1.size(); i++) + v1Embedded[i] = v1[i].globalCoordinates(); + + // Interpolate + auto function = Dune::Functions::makeDiscreteGlobalBasisFunction<Dune::FieldVector<double,3> >(basis1, v1Embedded); + std::vector<Dune::FieldVector<double,3> > v2Embedded; + Dune::Functions::interpolate(basis2, v2Embedded, function); + + // Copy back from FieldVector to RealTuple + v2.resize(v2Embedded.size()); + for (size_t i=0; i<v2.size(); i++) + v2[i] = RealTuple<double,3>(v2Embedded[i]); + } + + /** \brief Extend filename to contain communicator rank and size + * + * Copied from dune-grid vtkwriter.hh + */ + static std::string getParallelPieceName(const std::string& name, + const std::string& path, + int commRank, int commSize) + { + std::ostringstream s; + if(path.size() > 0) { + s << path; + if(path[path.size()-1] != '/') + s << '/'; } - - /** \brief Extend filename to contain communicator rank and size - * - * Copied from dune-grid vtkwriter.hh - */ - static std::string getParallelPieceName(const std::string& name, - const std::string& path, - int commRank, int commSize) - { - std::ostringstream s; - if(path.size() > 0) { - s << path; - if(path[path.size()-1] != '/') - s << '/'; - } - s << 's' << std::setw(4) << std::setfill('0') << commSize << '-'; - s << 'p' << std::setw(4) << std::setfill('0') << commRank << '-'; - s << name; - if(GridType::dimension > 1) - s << ".vtu"; - else - s << ".vtp"; - return s.str(); - } - - /** \brief Extend filename to contain communicator rank and size - * - * Copied from dune-grid vtkwriter.hh - */ - static std::string getParallelName(const std::string& name, - const std::string& path, - int commSize) - { - std::ostringstream s; - if(path.size() > 0) { - s << path; - if(path[path.size()-1] != '/') - s << '/'; - } - s << 's' << std::setw(4) << std::setfill('0') << commSize << '-'; - s << name; - if(GridType::dimension > 1) - s << ".pvtu"; - else - s << ".pvtp"; - return s.str(); + s << 's' << std::setw(4) << std::setfill('0') << commSize << '-'; + s << 'p' << std::setw(4) << std::setfill('0') << commRank << '-'; + s << name; + if(GridType::dimension > 1) + s << ".vtu"; + else + s << ".vtp"; + return s.str(); + } + + /** \brief Extend filename to contain communicator rank and size + * + * Copied from dune-grid vtkwriter.hh + */ + static std::string getParallelName(const std::string& name, + const std::string& path, + int commSize) + { + std::ostringstream s; + if(path.size() > 0) { + s << path; + if(path[path.size()-1] != '/') + s << '/'; } + s << 's' << std::setw(4) << std::setfill('0') << commSize << '-'; + s << name; + if(GridType::dimension > 1) + s << ".pvtu"; + else + s << ".pvtp"; + return s.str(); + } public: - /** \brief Write a configuration given with respect to a scalar function space basis - */ - template <typename Basis> - static void write(const Basis& basis, - const Dune::TupleVector<std::vector<RealTuple<double,3> >, - std::vector<Rotation<double,3> > >& configuration, - const std::string& filename) - { - using namespace Dune::TypeTree::Indices; - std::vector<RigidBodyMotion<double,3>> xRBM(basis.size()); - for (std::size_t i = 0; i < basis.size(); i++) { - for (int j = 0; j < 3; j ++) // Displacement part - xRBM[i].r[j] = configuration[_0][i][j]; - xRBM[i].q = configuration[_1][i]; // Rotation part - } - write(basis,xRBM,filename); + /** \brief Write a configuration given with respect to a scalar function space basis + */ + template <typename Basis> + static void write(const Basis& basis, + const Dune::TupleVector<std::vector<RealTuple<double,3> >, + std::vector<Rotation<double,3> > >& configuration, + const std::string& filename) + { + using namespace Dune::TypeTree::Indices; + std::vector<RigidBodyMotion<double,3> > xRBM(basis.size()); + for (std::size_t i = 0; i < basis.size(); i++) { + for (int j = 0; j < 3; j ++) // Displacement part + xRBM[i].r[j] = configuration[_0][i][j]; + xRBM[i].q = configuration[_1][i]; // Rotation part } - /** \brief Write a configuration given with respect to a scalar function space basis - */ - template <typename Basis, typename VectorType> - static void write(const Basis& basis, - const VectorType& configuration, - const std::string& filename) - { - using namespace Dune::TypeTree::Indices; - std::vector<RigidBodyMotion<double,3>> xRBM(basis.size()); - for (std::size_t i = 0; i < basis.size(); i++) { - for (int j = 0; j < 3; j ++) // Displacement part - xRBM[i].r[j] = configuration[i][j]; - } - write(basis,xRBM,filename); + write(basis,xRBM,filename); + } + /** \brief Write a configuration given with respect to a scalar function space basis + */ + template <typename Basis, typename VectorType> + static void write(const Basis& basis, + const VectorType& configuration, + const std::string& filename) + { + using namespace Dune::TypeTree::Indices; + std::vector<RigidBodyMotion<double,3> > xRBM(basis.size()); + for (std::size_t i = 0; i < basis.size(); i++) { + for (int j = 0; j < 3; j ++) // Displacement part + xRBM[i].r[j] = configuration[i][j]; } - - /** \brief Write a configuration given with respect to a scalar function space basis - */ - template <typename Basis> - static void write(const Basis& basis, - const std::vector<RigidBodyMotion<double,3> >& configuration, - const std::string& filename) + write(basis,xRBM,filename); + } + + /** \brief Write a configuration given with respect to a scalar function space basis + */ + template <typename Basis> + static void write(const Basis& basis, + const std::vector<RigidBodyMotion<double,3> >& configuration, + const std::string& filename) + { + assert(basis.size() == configuration.size()); + auto gridView = basis.gridView(); + + // Determine order of the basis + // We check for the order of the first element, and assume it is the same for all others + auto localView = basis.localView(); + localView.bind(*gridView.template begin<0>()); + const int order = localView.tree().finiteElement().localBasis().order(); + // order of the approximation of the VTK file -- can only be two or one + const auto vtkOrder = std::min(2,order); + + // Downsample 3rd-order functions onto a P2-space. That's all VTK can visualize today. + if (order>=3) { - assert(basis.size() == configuration.size()); - auto gridView = basis.gridView(); - - // Determine order of the basis - // We check for the order of the first element, and assume it is the same for all others - auto localView = basis.localView(); - localView.bind(*gridView.template begin<0>()); - const int order = localView.tree().finiteElement().localBasis().order(); - // order of the approximation of the VTK file -- can only be two or one - const auto vtkOrder = std::min(2,order); - - // Downsample 3rd-order functions onto a P2-space. That's all VTK can visualize today. - if (order>=3) - { - using namespace Dune::Functions::BasisFactory; - auto p2Basis = makeBasis(gridView, lagrange<2>()); - - auto blockedP2Basis = makeBasis( - gridView, - power<3>( - lagrange<2>(), - blockedInterleaved() + using namespace Dune::Functions::BasisFactory; + auto p2Basis = makeBasis(gridView, lagrange<2>()); + + auto blockedP2Basis = makeBasis( + gridView, + power<3>( + lagrange<2>(), + blockedInterleaved() )); - std::vector<RigidBodyMotion<double,3> > downsampledConfig; + std::vector<RigidBodyMotion<double,3> > downsampledConfig; - downsample(basis, configuration, blockedP2Basis, downsampledConfig); + downsample(basis, configuration, blockedP2Basis, downsampledConfig); - write(p2Basis, downsampledConfig, filename); - return; - } + write(p2Basis, downsampledConfig, filename); + return; + } - Dune::GFE::VTKFile vtkFile; + Dune::GFE::VTKFile vtkFile; - // Count the number of elements of the different types - std::map<Dune::GeometryType,std::size_t> numElements; - for (const auto t : gridView.indexSet().types(0)) - numElements[t] = 0; + // Count the number of elements of the different types + std::map<Dune::GeometryType,std::size_t> numElements; + for (const auto t : gridView.indexSet().types(0)) + numElements[t] = 0; - for (auto&& t : elements(gridView, Dune::Partitions::interior)) - numElements[t.type()]++; + for (auto&& t : elements(gridView, Dune::Partitions::interior)) + numElements[t.type()]++; - std::size_t totalNumElements = 0; - for (const auto nE : numElements) - totalNumElements += nE.second; + std::size_t totalNumElements = 0; + for (const auto nE : numElements) + totalNumElements += nE.second; - // Enter vertex coordinates - std::vector<Dune::FieldVector<double, 3> > points(configuration.size()); - for (size_t i=0; i<configuration.size(); i++) - points[i] = configuration[i].r; + // Enter vertex coordinates + std::vector<Dune::FieldVector<double, 3> > points(configuration.size()); + for (size_t i=0; i<configuration.size(); i++) + points[i] = configuration[i].r; - vtkFile.points_ = points; + vtkFile.points_ = points; - // Enter elements - std::size_t connectivitySize = 0; - for (const auto nE : numElements) + // Enter elements + std::size_t connectivitySize = 0; + for (const auto nE : numElements) + { + if (nE.first.isQuadrilateral()) + connectivitySize += ((vtkOrder==2) ? 8 : 4) * nE.second; + else if (nE.first.isTriangle()) + connectivitySize += ((vtkOrder==2) ? 6 : 3) * nE.second; + else if (nE.first.isHexahedron()) + connectivitySize += ((vtkOrder==2) ? 20 : 8) * nE.second; + else if (nE.first.isLine()) + connectivitySize += ((vtkOrder==2) ? 3 : 2) * nE.second; + else + DUNE_THROW(Dune::IOError, "Unsupported element type '" << nE.first << "' found!"); + } + std::vector<unsigned int> connectivity(connectivitySize); + + size_t i=0; + for (const auto& element : elements(gridView, Dune::Partitions::interior)) + { + localView.bind(element); + + if (element.type().isQuadrilateral()) + { + if (vtkOrder==2) { - if (nE.first.isQuadrilateral()) - connectivitySize += ((vtkOrder==2) ? 8 : 4) * nE.second; - else if (nE.first.isTriangle()) - connectivitySize += ((vtkOrder==2) ? 6 : 3) * nE.second; - else if (nE.first.isHexahedron()) - connectivitySize += ((vtkOrder==2) ? 20 : 8) * nE.second; - else if (nE.first.isLine()) - connectivitySize += ((vtkOrder==2) ? 3 : 2) * nE.second; - else - DUNE_THROW(Dune::IOError, "Unsupported element type '" << nE.first << "' found!"); + connectivity[i++] = localView.index(0); + connectivity[i++] = localView.index(2); + connectivity[i++] = localView.index(8); + connectivity[i++] = localView.index(6); + + connectivity[i++] = localView.index(1); + connectivity[i++] = localView.index(5); + connectivity[i++] = localView.index(7); + connectivity[i++] = localView.index(3); } - std::vector<unsigned int> connectivity(connectivitySize); - - size_t i=0; - for (const auto& element : elements(gridView, Dune::Partitions::interior)) + else // first order { - localView.bind(element); - - if (element.type().isQuadrilateral()) - { - if (vtkOrder==2) - { - connectivity[i++] = localView.index(0); - connectivity[i++] = localView.index(2); - connectivity[i++] = localView.index(8); - connectivity[i++] = localView.index(6); - - connectivity[i++] = localView.index(1); - connectivity[i++] = localView.index(5); - connectivity[i++] = localView.index(7); - connectivity[i++] = localView.index(3); - } - else // first order - { - connectivity[i++] = localView.index(0); - connectivity[i++] = localView.index(1); - connectivity[i++] = localView.index(3); - connectivity[i++] = localView.index(2); - } - } - if (element.type().isTriangle()) - { - if (vtkOrder==2) - { - connectivity[i++] = localView.index(0); - connectivity[i++] = localView.index(2); - connectivity[i++] = localView.index(5); - connectivity[i++] = localView.index(1); - connectivity[i++] = localView.index(4); - connectivity[i++] = localView.index(3); - } - else // first order - { - connectivity[i++] = localView.index(0); - connectivity[i++] = localView.index(1); - connectivity[i++] = localView.index(2); - } - } - if (element.type().isHexahedron()) - { - if (vtkOrder==2) - { - // Corner dofs - connectivity[i++] = localView.index(0); - connectivity[i++] = localView.index(2); - connectivity[i++] = localView.index(8); - connectivity[i++] = localView.index(6); - - connectivity[i++] = localView.index(18); - connectivity[i++] = localView.index(20); - connectivity[i++] = localView.index(26); - connectivity[i++] = localView.index(24); - - // Edge dofs - connectivity[i++] = localView.index(1); - connectivity[i++] = localView.index(5); - connectivity[i++] = localView.index(7); - connectivity[i++] = localView.index(3); - - connectivity[i++] = localView.index(19); - connectivity[i++] = localView.index(23); - connectivity[i++] = localView.index(25); - connectivity[i++] = localView.index(21); - - connectivity[i++] = localView.index(9); - connectivity[i++] = localView.index(11); - connectivity[i++] = localView.index(17); - connectivity[i++] = localView.index(15); - } - else // first order - { - connectivity[i++] = localView.index(0); - connectivity[i++] = localView.index(1); - connectivity[i++] = localView.index(3); - connectivity[i++] = localView.index(2); - connectivity[i++] = localView.index(4); - connectivity[i++] = localView.index(5); - connectivity[i++] = localView.index(7); - connectivity[i++] = localView.index(6); - } - } - - if (element.type().isLine()) - { - if (vtkOrder==2) - { - connectivity[i++] = localView.index(0); - connectivity[i++] = localView.index(2); - connectivity[i++] = localView.index(1); - } - else // first order - { - connectivity[i++] = localView.index(0); - connectivity[i++] = localView.index(1); - } - } + connectivity[i++] = localView.index(0); + connectivity[i++] = localView.index(1); + connectivity[i++] = localView.index(3); + connectivity[i++] = localView.index(2); } + } + if (element.type().isTriangle()) + { + if (vtkOrder==2) + { + connectivity[i++] = localView.index(0); + connectivity[i++] = localView.index(2); + connectivity[i++] = localView.index(5); + connectivity[i++] = localView.index(1); + connectivity[i++] = localView.index(4); + connectivity[i++] = localView.index(3); + } + else // first order + { + connectivity[i++] = localView.index(0); + connectivity[i++] = localView.index(1); + connectivity[i++] = localView.index(2); + } + } + if (element.type().isHexahedron()) + { + if (vtkOrder==2) + { + // Corner dofs + connectivity[i++] = localView.index(0); + connectivity[i++] = localView.index(2); + connectivity[i++] = localView.index(8); + connectivity[i++] = localView.index(6); + + connectivity[i++] = localView.index(18); + connectivity[i++] = localView.index(20); + connectivity[i++] = localView.index(26); + connectivity[i++] = localView.index(24); + + // Edge dofs + connectivity[i++] = localView.index(1); + connectivity[i++] = localView.index(5); + connectivity[i++] = localView.index(7); + connectivity[i++] = localView.index(3); + + connectivity[i++] = localView.index(19); + connectivity[i++] = localView.index(23); + connectivity[i++] = localView.index(25); + connectivity[i++] = localView.index(21); + + connectivity[i++] = localView.index(9); + connectivity[i++] = localView.index(11); + connectivity[i++] = localView.index(17); + connectivity[i++] = localView.index(15); + } + else // first order + { + connectivity[i++] = localView.index(0); + connectivity[i++] = localView.index(1); + connectivity[i++] = localView.index(3); + connectivity[i++] = localView.index(2); + connectivity[i++] = localView.index(4); + connectivity[i++] = localView.index(5); + connectivity[i++] = localView.index(7); + connectivity[i++] = localView.index(6); + } + } - vtkFile.cellConnectivity_ = connectivity; - - std::vector<int> offsets(totalNumElements); - i = 0; - int offsetCounter = 0; - for (const auto& element : elements(gridView, Dune::Partitions::interior)) + if (element.type().isLine()) + { + if (vtkOrder==2) { - if (element.type().isQuadrilateral()) - offsetCounter += (vtkOrder==2) ? 8 : 4; + connectivity[i++] = localView.index(0); + connectivity[i++] = localView.index(2); + connectivity[i++] = localView.index(1); + } + else // first order + { + connectivity[i++] = localView.index(0); + connectivity[i++] = localView.index(1); + } + } + } - if (element.type().isTriangle()) - offsetCounter += (vtkOrder==2) ? 6 : 3; + vtkFile.cellConnectivity_ = connectivity; - if (element.type().isHexahedron()) - offsetCounter += (vtkOrder==2) ? 20 : 8; + std::vector<int> offsets(totalNumElements); + i = 0; + int offsetCounter = 0; + for (const auto& element : elements(gridView, Dune::Partitions::interior)) + { + if (element.type().isQuadrilateral()) + offsetCounter += (vtkOrder==2) ? 8 : 4; - offsets[i++] += offsetCounter; - } + if (element.type().isTriangle()) + offsetCounter += (vtkOrder==2) ? 6 : 3; - vtkFile.cellOffsets_ = offsets; + if (element.type().isHexahedron()) + offsetCounter += (vtkOrder==2) ? 20 : 8; - std::vector<int> cellTypes(totalNumElements); - i = 0; - for (const auto& element : elements(gridView, Dune::Partitions::interior)) - { - if (element.type().isQuadrilateral()) - cellTypes[i++] = (vtkOrder==2) ? 23 : 9; + offsets[i++] += offsetCounter; + } - if (element.type().isTriangle()) - cellTypes[i++] = (vtkOrder==2) ? 22 : 5; + vtkFile.cellOffsets_ = offsets; - if (element.type().isHexahedron()) - cellTypes[i++] = (vtkOrder==2) ? 25 : 12; - } - vtkFile.cellTypes_ = cellTypes; + std::vector<int> cellTypes(totalNumElements); + i = 0; + for (const auto& element : elements(gridView, Dune::Partitions::interior)) + { + if (element.type().isQuadrilateral()) + cellTypes[i++] = (vtkOrder==2) ? 23 : 9; - // Z coordinate for better visualization of wrinkles - std::vector<double> zCoord(points.size()); - for (size_t i=0; i<configuration.size(); i++) - zCoord[i] = configuration[i].r[2]; + if (element.type().isTriangle()) + cellTypes[i++] = (vtkOrder==2) ? 22 : 5; - vtkFile.zCoord_ = zCoord; + if (element.type().isHexahedron()) + cellTypes[i++] = (vtkOrder==2) ? 25 : 12; + } + vtkFile.cellTypes_ = cellTypes; - // The three director fields - for (size_t i=0; i<3; i++) - { - vtkFile.directors_[i].resize(configuration.size()); - for (size_t j=0; j<configuration.size(); j++) - vtkFile.directors_[i][j] = configuration[j].q.director(i); - } + // Z coordinate for better visualization of wrinkles + std::vector<double> zCoord(points.size()); + for (size_t i=0; i<configuration.size(); i++) + zCoord[i] = configuration[i].r[2]; - // Actually write the VTK file to disk - vtkFile.write(filename); - } + vtkFile.zCoord_ = zCoord; - /** \brief Write a configuration given with respect to a scalar function space basis - */ - template <typename DisplacementBasis, typename OrientationBasis> - static void writeMixed(const DisplacementBasis& displacementBasis, - const std::vector<RealTuple<double,3> >& deformationConfiguration, - const OrientationBasis& orientationBasis, - const std::vector<Rotation<double,3> >& orientationConfiguration, - const std::string& filename) + // The three director fields + for (size_t i=0; i<3; i++) { - assert(displacementBasis.size() == deformationConfiguration.size()); - assert(orientationBasis.size() == orientationConfiguration.size()); - auto gridView = displacementBasis.gridView(); - - // Determine order of the displacement basis - auto localView = displacementBasis.localView(); - localView.bind(*gridView.template begin<0>()); - int order = localView.tree().finiteElement().localBasis().order(); + vtkFile.directors_[i].resize(configuration.size()); + for (size_t j=0; j<configuration.size(); j++) + vtkFile.directors_[i][j] = configuration[j].q.director(i); + } - // We only do P2 spaces at the moment - if (order != 2 and order != 3) - { - std::cout << "Warning: CosseratVTKWriter only supports P2 spaces -- skipping" << std::endl; - return; - } + // Actually write the VTK file to disk + vtkFile.write(filename); + } + + /** \brief Write a configuration given with respect to a scalar function space basis + */ + template <typename DisplacementBasis, typename OrientationBasis> + static void writeMixed(const DisplacementBasis& displacementBasis, + const std::vector<RealTuple<double,3> >& deformationConfiguration, + const OrientationBasis& orientationBasis, + const std::vector<Rotation<double,3> >& orientationConfiguration, + const std::string& filename) + { + assert(displacementBasis.size() == deformationConfiguration.size()); + assert(orientationBasis.size() == orientationConfiguration.size()); + auto gridView = displacementBasis.gridView(); + + // Determine order of the displacement basis + auto localView = displacementBasis.localView(); + localView.bind(*gridView.template begin<0>()); + int order = localView.tree().finiteElement().localBasis().order(); + + // We only do P2 spaces at the moment + if (order != 2 and order != 3) + { + std::cout << "Warning: CosseratVTKWriter only supports P2 spaces -- skipping" << std::endl; + return; + } - std::vector<RealTuple<double,3> > displacementConfiguration = deformationConfiguration; + std::vector<RealTuple<double,3> > displacementConfiguration = deformationConfiguration; - if (order == 3) - { - using namespace Dune::Functions::BasisFactory; + if (order == 3) + { + using namespace Dune::Functions::BasisFactory; - auto p2DeformationBasis = makeBasis( - gridView, - power<3>( - lagrange<2>(), - blockedInterleaved() + auto p2DeformationBasis = makeBasis( + gridView, + power<3>( + lagrange<2>(), + blockedInterleaved() )); - // resample to 2nd order -- vtk can't do anything higher - std::vector<RealTuple<double,3> > p2DeformationConfiguration; + // resample to 2nd order -- vtk can't do anything higher + std::vector<RealTuple<double,3> > p2DeformationConfiguration; - downsample(displacementBasis, displacementConfiguration, - p2DeformationBasis, p2DeformationConfiguration); + downsample(displacementBasis, displacementConfiguration, + p2DeformationBasis, p2DeformationConfiguration); - displacementConfiguration = p2DeformationConfiguration; - } + displacementConfiguration = p2DeformationConfiguration; + } - std::string fullfilename = filename + ".vtu"; + std::string fullfilename = filename + ".vtu"; - // Prepend rank and communicator size to the filename, if there are more than one process - if (gridView.comm().size() > 1) - fullfilename = getParallelPieceName(filename, "", gridView.comm().rank(), gridView.comm().size()); + // Prepend rank and communicator size to the filename, if there are more than one process + if (gridView.comm().size() > 1) + fullfilename = getParallelPieceName(filename, "", gridView.comm().rank(), gridView.comm().size()); - // Write the pvtu file that ties together the different parts - if (gridView.comm().size() > 1 && gridView.comm().rank()==0) - { - std::ofstream pvtuOutFile(getParallelName(filename, "", gridView.comm().size())); - Dune::VTK::PVTUWriter writer(pvtuOutFile, Dune::VTK::unstructuredGrid); + // Write the pvtu file that ties together the different parts + if (gridView.comm().size() > 1 && gridView.comm().rank()==0) + { + std::ofstream pvtuOutFile(getParallelName(filename, "", gridView.comm().size())); + Dune::VTK::PVTUWriter writer(pvtuOutFile, Dune::VTK::unstructuredGrid); - writer.beginMain(); + writer.beginMain(); - //writer.beginPointData(); - //writer.addArray<float>("director0", 3); - //writer.addArray<float>("director1", 3); - //writer.addArray<float>("director2", 3); - //writer.addArray<float>("zCoord", 1); - //writer.endPointData(); + //writer.beginPointData(); + //writer.addArray<float>("director0", 3); + //writer.addArray<float>("director1", 3); + //writer.addArray<float>("director2", 3); + //writer.addArray<float>("zCoord", 1); + //writer.endPointData(); - // dump point coordinates - writer.beginPoints(); - writer.addArray("Coordinates", 3, Dune::VTK::Precision::float32); - writer.endPoints(); + // dump point coordinates + writer.beginPoints(); + writer.addArray("Coordinates", 3, Dune::VTK::Precision::float32); + writer.endPoints(); - for (int i=0; i<gridView.comm().size(); i++) - writer.addPiece(getParallelPieceName(filename, "", i, gridView.comm().size())); + for (int i=0; i<gridView.comm().size(); i++) + writer.addPiece(getParallelPieceName(filename, "", i, gridView.comm().size())); - // finish main section - writer.endMain(); - } + // finish main section + writer.endMain(); + } - ///////////////////////////////////////////////////////////////////////////////// - // Write the actual vtu file - ///////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////// + // Write the actual vtu file + ///////////////////////////////////////////////////////////////////////////////// - // Stupid: I can't directly get the number of Interior_Partition elements - size_t numElements = std::distance(gridView.template begin<0, Dune::Interior_Partition>(), - gridView.template end<0, Dune::Interior_Partition>()); + // Stupid: I can't directly get the number of Interior_Partition elements + size_t numElements = std::distance(gridView.template begin<0, Dune::Interior_Partition>(), + gridView.template end<0, Dune::Interior_Partition>()); - std::ofstream outFile(fullfilename); + std::ofstream outFile(fullfilename); - // Write header - outFile << "<?xml version=\"1.0\"?>" << std::endl; - outFile << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\">" << std::endl; - outFile << " <UnstructuredGrid>" << std::endl; - outFile << " <Piece NumberOfCells=\"" << numElements << "\" NumberOfPoints=\"" << displacementConfiguration.size() << "\">" << std::endl; + // Write header + outFile << "<?xml version=\"1.0\"?>" << std::endl; + outFile << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"LittleEndian\">" << std::endl; + outFile << " <UnstructuredGrid>" << std::endl; + outFile << " <Piece NumberOfCells=\"" << numElements << "\" NumberOfPoints=\"" << displacementConfiguration.size() << "\">" << std::endl; - // Write vertex coordinates - outFile << " <Points>" << std::endl; - outFile << " <DataArray type=\"Float32\" Name=\"Coordinates\" NumberOfComponents=\"3\" format=\"ascii\">" << std::endl; - for (size_t i=0; i<displacementConfiguration.size(); i++) - outFile << " " << displacementConfiguration[i] << std::endl; - outFile << " </DataArray>" << std::endl; - outFile << " </Points>" << std::endl; + // Write vertex coordinates + outFile << " <Points>" << std::endl; + outFile << " <DataArray type=\"Float32\" Name=\"Coordinates\" NumberOfComponents=\"3\" format=\"ascii\">" << std::endl; + for (size_t i=0; i<displacementConfiguration.size(); i++) + outFile << " " << displacementConfiguration[i] << std::endl; + outFile << " </DataArray>" << std::endl; + outFile << " </Points>" << std::endl; - // Write elements - outFile << " <Cells>" << std::endl; + // Write elements + outFile << " <Cells>" << std::endl; - outFile << " <DataArray type=\"Int32\" Name=\"connectivity\" NumberOfComponents=\"1\" format=\"ascii\">" << std::endl; + outFile << " <DataArray type=\"Int32\" Name=\"connectivity\" NumberOfComponents=\"1\" format=\"ascii\">" << std::endl; - for (const auto& element : elements(gridView, Dune::Partitions::interior)) - { - localView.bind(element); - - outFile << " "; - if (element.type().isQuadrilateral()) - { - outFile << localView.index(0) << " "; - outFile << localView.index(2) << " "; - outFile << localView.index(8) << " "; - outFile << localView.index(6) << " "; - - outFile << localView.index(1) << " "; - outFile << localView.index(5) << " "; - outFile << localView.index(7) << " "; - outFile << localView.index(3) << " "; - - outFile << std::endl; - } - } - outFile << " </DataArray>" << std::endl; + for (const auto& element : elements(gridView, Dune::Partitions::interior)) + { + localView.bind(element); + + outFile << " "; + if (element.type().isQuadrilateral()) + { + outFile << localView.index(0) << " "; + outFile << localView.index(2) << " "; + outFile << localView.index(8) << " "; + outFile << localView.index(6) << " "; + + outFile << localView.index(1) << " "; + outFile << localView.index(5) << " "; + outFile << localView.index(7) << " "; + outFile << localView.index(3) << " "; + + outFile << std::endl; + } + } + outFile << " </DataArray>" << std::endl; - outFile << " <DataArray type=\"Int32\" Name=\"offsets\" NumberOfComponents=\"1\" format=\"ascii\">" << std::endl; - size_t offsetCounter = 0; - for (const auto& element : elements(gridView)) - { - outFile << " "; - if (element.type().isQuadrilateral()) - offsetCounter += 8; - outFile << offsetCounter << std::endl; - } - outFile << " </DataArray>" << std::endl; + outFile << " <DataArray type=\"Int32\" Name=\"offsets\" NumberOfComponents=\"1\" format=\"ascii\">" << std::endl; + size_t offsetCounter = 0; + for (const auto& element : elements(gridView)) + { + outFile << " "; + if (element.type().isQuadrilateral()) + offsetCounter += 8; + outFile << offsetCounter << std::endl; + } + outFile << " </DataArray>" << std::endl; - outFile << " <DataArray type=\"UInt8\" Name=\"types\" NumberOfComponents=\"1\" format=\"ascii\">" << std::endl; - for (const auto& element : elements(gridView)) - { - outFile << " "; - if (element.type().isQuadrilateral()) - outFile << "23" << std::endl; - } - outFile << " </DataArray>" << std::endl; + outFile << " <DataArray type=\"UInt8\" Name=\"types\" NumberOfComponents=\"1\" format=\"ascii\">" << std::endl; + for (const auto& element : elements(gridView)) + { + outFile << " "; + if (element.type().isQuadrilateral()) + outFile << "23" << std::endl; + } + outFile << " </DataArray>" << std::endl; - outFile << " </Cells>" << std::endl; + outFile << " </Cells>" << std::endl; #if 0 - // Point data - outFile << " <PointData Scalars=\"zCoord\" Vectors=\"director0\">" << std::endl; + // Point data + outFile << " <PointData Scalars=\"zCoord\" Vectors=\"director0\">" << std::endl; - // Z coordinate for better visualization of wrinkles - outFile << " <DataArray type=\"Float32\" Name=\"zCoord\" NumberOfComponents=\"1\" format=\"ascii\">" << std::endl; - for (size_t i=0; i<configuration.size(); i++) - outFile << " " << configuration[i].r[2] << std::endl; - outFile << " </DataArray>" << std::endl; + // Z coordinate for better visualization of wrinkles + outFile << " <DataArray type=\"Float32\" Name=\"zCoord\" NumberOfComponents=\"1\" format=\"ascii\">" << std::endl; + for (size_t i=0; i<configuration.size(); i++) + outFile << " " << configuration[i].r[2] << std::endl; + outFile << " </DataArray>" << std::endl; - // The three director fields - for (size_t i=0; i<3; i++) - { - outFile << " <DataArray type=\"Float32\" Name=\"director" << i <<"\" NumberOfComponents=\"3\" format=\"ascii\">" << std::endl; - for (size_t j=0; j<configuration.size(); j++) - outFile << " " << configuration[j].q.director(i) << std::endl; - outFile << " </DataArray>" << std::endl; - } + // The three director fields + for (size_t i=0; i<3; i++) + { + outFile << " <DataArray type=\"Float32\" Name=\"director" << i <<"\" NumberOfComponents=\"3\" format=\"ascii\">" << std::endl; + for (size_t j=0; j<configuration.size(); j++) + outFile << " " << configuration[j].q.director(i) << std::endl; + outFile << " </DataArray>" << std::endl; + } - outFile << " </PointData>" << std::endl; + outFile << " </PointData>" << std::endl; #endif - // Write footer - outFile << " </Piece>" << std::endl; - outFile << " </UnstructuredGrid>" << std::endl; - outFile << "</VTKFile>" << std::endl; + // Write footer + outFile << " </Piece>" << std::endl; + outFile << " </UnstructuredGrid>" << std::endl; + outFile << "</VTKFile>" << std::endl; - } + } }; diff --git a/dune/gfe/coupling/rodcontinuumcomplex.hh b/dune/gfe/coupling/rodcontinuumcomplex.hh index 2815b2c0..2cf3988a 100644 --- a/dune/gfe/coupling/rodcontinuumcomplex.hh +++ b/dune/gfe/coupling/rodcontinuumcomplex.hh @@ -18,113 +18,113 @@ template <class RodGrid, class ContinuumGrid> class RodContinuumComplex { - dune_static_assert(RodGrid::dimension==1, "The RodGrid has to be one-dimensional!"); - - static const int dim = ContinuumGrid::dimension; - - typedef std::vector<RigidBodyMotion<double,3> > RodConfiguration; - - typedef Dune::BlockVector<Dune::FieldVector<double,3> > ContinuumConfiguration; - - /** \brief Holds all data for a rod/continuum coupling */ - struct Coupling - { - BoundaryPatch<typename RodGrid::LeafGridView> rodInterfaceBoundary_; - - BoundaryPatch<typename ContinuumGrid::LeafGridView> continuumInterfaceBoundary_; - - /** \brief The orientation of the interface in the reference configuration */ - RigidBodyMotion<double,dim> referenceInterface_; - }; - - /** \brief Holds all data for a rod subproblem */ - struct RodData - { - Dune::shared_ptr<RodGrid> grid_; - - BoundaryPatch<typename RodGrid::LeafGridView> dirichletBoundary_; - - RodConfiguration dirichletValues_; - }; - - /** \brief Holds all data for a continuum subproblem */ - struct ContinuumData - { - Dune::shared_ptr<ContinuumGrid> grid_; - - BoundaryPatch<typename ContinuumGrid::LeafGridView> dirichletBoundary_; - - ContinuumConfiguration dirichletValues_; - }; - + dune_static_assert(RodGrid::dimension==1, "The RodGrid has to be one-dimensional!"); + + static const int dim = ContinuumGrid::dimension; + + typedef std::vector<RigidBodyMotion<double,3> > RodConfiguration; + + typedef Dune::BlockVector<Dune::FieldVector<double,3> > ContinuumConfiguration; + + /** \brief Holds all data for a rod/continuum coupling */ + struct Coupling + { + BoundaryPatch<typename RodGrid::LeafGridView> rodInterfaceBoundary_; + + BoundaryPatch<typename ContinuumGrid::LeafGridView> continuumInterfaceBoundary_; + + /** \brief The orientation of the interface in the reference configuration */ + RigidBodyMotion<double,dim> referenceInterface_; + }; + + /** \brief Holds all data for a rod subproblem */ + struct RodData + { + Dune::shared_ptr<RodGrid> grid_; + + BoundaryPatch<typename RodGrid::LeafGridView> dirichletBoundary_; + + RodConfiguration dirichletValues_; + }; + + /** \brief Holds all data for a continuum subproblem */ + struct ContinuumData + { + Dune::shared_ptr<ContinuumGrid> grid_; + + BoundaryPatch<typename ContinuumGrid::LeafGridView> dirichletBoundary_; + + ContinuumConfiguration dirichletValues_; + }; + public: - - /** \brief Iterator over the rods */ - typedef typename std::map<std::string, RodData>::iterator RodIterator; - typedef typename std::map<std::string, RodData>::const_iterator ConstRodIterator; - - /** \brief Iterator over the continua */ - typedef typename std::map<std::string, ContinuumData>::iterator ContinuumIterator; - typedef typename std::map<std::string, ContinuumData>::const_iterator ConstContinuumIterator; - - /** \brief Iterator over the couplings */ - typedef typename std::map<std::pair<std::string,std::string>, Coupling>::const_iterator ConstCouplingIterator; - - /** \brief Simple const access to rod grids */ - const Dune::shared_ptr<RodGrid> rodGrid(const std::string& name) const - { - assert(rods_.find(name) != rods_.end()); - return rods_.find(name)->second.grid_; - } - - /** \brief Simple const access to continuum grids */ - const Dune::shared_ptr<ContinuumGrid> continuumGrid(const std::string& name) const - { - assert(continua_.find(name) != continua_.end()); - return continua_.find(name)->second.grid_; - } - - /** \brief Simple const access to continua */ - const ContinuumData continuum(const std::string& name) const - { - assert(continua_.find(name) != continua_.end()); - return continua_.find(name)->second; - } - - /** \brief Simple const access to rods */ - const RodData rod(const std::string& name) const - { - assert(rods_.find(name) != rods_.end()); - return rods_.find(name)->second; - } - - /** \brief Simple const access to couplings */ - const Coupling& coupling(const std::pair<std::string,std::string>& name) const - { - assert(couplings_.find(name) != couplings_.end()); - return couplings_.find(name)->second; - } - - ///////////////////////////////////////////////////////////////////// - // Data concerning the individual rod problems - ///////////////////////////////////////////////////////////////////// - - /** \brief The set of rods, accessible by name (string) */ - std::map<std::string, RodData > rods_; - - ///////////////////////////////////////////////////////////////////// - // Data concerning the individual continuum problems - ///////////////////////////////////////////////////////////////////// - - /** \brief The set of continua, accessible by name (string) */ - std::map<std::string, ContinuumData> continua_; - - ///////////////////////////////////////////////////////////////////// - // Data about the couplings - ///////////////////////////////////////////////////////////////////// - - std::map<std::pair<std::string,std::string>, Coupling> couplings_; - + + /** \brief Iterator over the rods */ + typedef typename std::map<std::string, RodData>::iterator RodIterator; + typedef typename std::map<std::string, RodData>::const_iterator ConstRodIterator; + + /** \brief Iterator over the continua */ + typedef typename std::map<std::string, ContinuumData>::iterator ContinuumIterator; + typedef typename std::map<std::string, ContinuumData>::const_iterator ConstContinuumIterator; + + /** \brief Iterator over the couplings */ + typedef typename std::map<std::pair<std::string,std::string>, Coupling>::const_iterator ConstCouplingIterator; + + /** \brief Simple const access to rod grids */ + const Dune::shared_ptr<RodGrid> rodGrid(const std::string& name) const + { + assert(rods_.find(name) != rods_.end()); + return rods_.find(name)->second.grid_; + } + + /** \brief Simple const access to continuum grids */ + const Dune::shared_ptr<ContinuumGrid> continuumGrid(const std::string& name) const + { + assert(continua_.find(name) != continua_.end()); + return continua_.find(name)->second.grid_; + } + + /** \brief Simple const access to continua */ + const ContinuumData continuum(const std::string& name) const + { + assert(continua_.find(name) != continua_.end()); + return continua_.find(name)->second; + } + + /** \brief Simple const access to rods */ + const RodData rod(const std::string& name) const + { + assert(rods_.find(name) != rods_.end()); + return rods_.find(name)->second; + } + + /** \brief Simple const access to couplings */ + const Coupling& coupling(const std::pair<std::string,std::string>& name) const + { + assert(couplings_.find(name) != couplings_.end()); + return couplings_.find(name)->second; + } + + ///////////////////////////////////////////////////////////////////// + // Data concerning the individual rod problems + ///////////////////////////////////////////////////////////////////// + + /** \brief The set of rods, accessible by name (string) */ + std::map<std::string, RodData > rods_; + + ///////////////////////////////////////////////////////////////////// + // Data concerning the individual continuum problems + ///////////////////////////////////////////////////////////////////// + + /** \brief The set of continua, accessible by name (string) */ + std::map<std::string, ContinuumData> continua_; + + ///////////////////////////////////////////////////////////////////// + // Data about the couplings + ///////////////////////////////////////////////////////////////////// + + std::map<std::pair<std::string,std::string>, Coupling> couplings_; + }; #endif // ROD_CONTINUUM_COMPLEX_HH diff --git a/dune/gfe/coupling/rodcontinuumddstep.hh b/dune/gfe/coupling/rodcontinuumddstep.hh index bf5edb1e..be87336e 100644 --- a/dune/gfe/coupling/rodcontinuumddstep.hh +++ b/dune/gfe/coupling/rodcontinuumddstep.hh @@ -23,134 +23,134 @@ template <class RodGridType, class ContinuumGridType> class RodContinuumDDStep { - static const int dim = ContinuumGridType::dimension; - - // The type used for rod configurations - typedef std::vector<RigidBodyMotion<double,dim> > RodConfigurationType; - - // The type used for continuum configurations - typedef Dune::BlockVector<Dune::FieldVector<double,dim> > VectorType; - typedef Dune::BlockVector<Dune::FieldVector<double,dim> > ContinuumConfigurationType; - - typedef Dune::BlockVector<Dune::FieldVector<double,6> > RodCorrectionType; - - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,3,3> > MatrixType; - - typedef typename P1NodalBasis<typename ContinuumGridType::LeafGridView,double>::LocalFiniteElement ContinuumLocalFiniteElement; - + static const int dim = ContinuumGridType::dimension; + + // The type used for rod configurations + typedef std::vector<RigidBodyMotion<double,dim> > RodConfigurationType; + + // The type used for continuum configurations + typedef Dune::BlockVector<Dune::FieldVector<double,dim> > VectorType; + typedef Dune::BlockVector<Dune::FieldVector<double,dim> > ContinuumConfigurationType; + + typedef Dune::BlockVector<Dune::FieldVector<double,6> > RodCorrectionType; + + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,3,3> > MatrixType; + + typedef typename P1NodalBasis<typename ContinuumGridType::LeafGridView,double>::LocalFiniteElement ContinuumLocalFiniteElement; + public: - /** \brief Constructor for a complex with one rod and one continuum */ - RodContinuumDDStep(const RodContinuumComplex<RodGridType,ContinuumGridType>& complex/*, - RodAssembler<typename RodGridType::LeafGridView,3>* rodAssembler, - RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* rodSolver, - const MatrixType* stiffnessMatrix3d, - const Dune::shared_ptr< ::LoopSolver<VectorType> > solver*/) - : complex_(complex) - { + /** \brief Constructor for a complex with one rod and one continuum */ + RodContinuumDDStep(const RodContinuumComplex<RodGridType,ContinuumGridType>& complex /*, + RodAssembler<typename RodGridType::LeafGridView,3>* rodAssembler, + RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* rodSolver, + const MatrixType* stiffnessMatrix3d, + const Dune::shared_ptr< ::LoopSolver<VectorType> > solver*/) + : complex_(complex) + { #if 0 - rods_["rod"].assembler_ = rodAssembler; - rods_["rod"].solver_ = rodSolver; + rods_["rod"].assembler_ = rodAssembler; + rods_["rod"].solver_ = rodSolver; - continua_["continuum"].stiffnessMatrix_ = stiffnessMatrix3d; - continua_["continuum"].solver_ = solver; + continua_["continuum"].stiffnessMatrix_ = stiffnessMatrix3d; + continua_["continuum"].solver_ = solver; - mergeRodDirichletAndCouplingBoundaries(); - mergeContinuumDirichletAndCouplingBoundaries(); + mergeRodDirichletAndCouplingBoundaries(); + mergeContinuumDirichletAndCouplingBoundaries(); #endif - } - - /** \brief Do one domain decomposition step - * \param[in,out] lambda The old and new iterate - */ - virtual void iterate(std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >& lambda) = 0; + } + + /** \brief Do one domain decomposition step + * \param[in,out] lambda The old and new iterate + */ + virtual void iterate(std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >& lambda) = 0; protected: - std::set<std::string> rodsPerContinuum(const std::string& name) const; - - std::set<std::string> continuaPerRod(const std::string& name) const; - - /** \brief Add the content of one map to another, aborting rather than overwriting stuff - */ - template <class X, class Y> - static void insert(std::map<X,Y>& map1, const std::map<X,Y>& map2) - { - int oldSize = map1.size(); - map1.insert(map2.begin(), map2.end()); - assert(map1.size() == oldSize + map2.size()); - } - - ////////////////////////////////////////////////////////////////// - // Data members related to the coupled problem - ////////////////////////////////////////////////////////////////// - const RodContinuumComplex<RodGridType,ContinuumGridType>& complex_; + std::set<std::string> rodsPerContinuum(const std::string& name) const; + + std::set<std::string> continuaPerRod(const std::string& name) const; + + /** \brief Add the content of one map to another, aborting rather than overwriting stuff + */ + template <class X, class Y> + static void insert(std::map<X,Y>& map1, const std::map<X,Y>& map2) + { + int oldSize = map1.size(); + map1.insert(map2.begin(), map2.end()); + assert(map1.size() == oldSize + map2.size()); + } + + ////////////////////////////////////////////////////////////////// + // Data members related to the coupled problem + ////////////////////////////////////////////////////////////////// + const RodContinuumComplex<RodGridType,ContinuumGridType>& complex_; #if 0 protected: - - ////////////////////////////////////////////////////////////////// - // Data members related to the rod problems - ////////////////////////////////////////////////////////////////// - - struct RodData - { - Dune::BitSetVector<6> dirichletAndCouplingNodes_; - - RodAssembler<typename RodGridType::LeafGridView,3>* assembler_; - - RodLocalStiffness<typename RodGridType::LeafGridView,double>* localStiffness_; - - RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* solver_; - }; - - /** \brief Simple const access to rods */ - const RodData& rod(const std::string& name) const - { - assert(rods_.find(name) != rods_.end()); - return rods_.find(name)->second; - } - - std::map<std::string, RodData> rods_; - - typedef typename std::map<std::string, RodData>::iterator RodIterator; + + ////////////////////////////////////////////////////////////////// + // Data members related to the rod problems + ////////////////////////////////////////////////////////////////// + + struct RodData + { + Dune::BitSetVector<6> dirichletAndCouplingNodes_; + + RodAssembler<typename RodGridType::LeafGridView,3>* assembler_; + + RodLocalStiffness<typename RodGridType::LeafGridView,double>* localStiffness_; + + RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* solver_; + }; + + /** \brief Simple const access to rods */ + const RodData& rod(const std::string& name) const + { + assert(rods_.find(name) != rods_.end()); + return rods_.find(name)->second; + } + + std::map<std::string, RodData> rods_; + + typedef typename std::map<std::string, RodData>::iterator RodIterator; #endif -public: - /** \todo Should be part of RodData, too */ - mutable std::map<std::string, RodConfigurationType> rodSubdomainSolutions_; +public: + /** \todo Should be part of RodData, too */ + mutable std::map<std::string, RodConfigurationType> rodSubdomainSolutions_; #if 0 protected: - ////////////////////////////////////////////////////////////////// - // Data members related to the continuum problems - ////////////////////////////////////////////////////////////////// - - struct ContinuumData - { - const MatrixType* stiffnessMatrix_; - - Dune::shared_ptr< ::LoopSolver<VectorType> > solver_; - - Dune::BitSetVector<dim> dirichletAndCouplingNodes_; - - LocalOperatorAssembler<ContinuumGridType, - ContinuumLocalFiniteElement, - ContinuumLocalFiniteElement, - Dune::FieldMatrix<double,dim,dim> >* localAssembler_; - }; - - /** \brief Simple const access to continua */ - const ContinuumData& continuum(const std::string& name) const - { - assert(continua_.find(name) != continua_.end()); - return continua_.find(name)->second; - } - - std::map<std::string, ContinuumData> continua_; - - typedef typename std::map<std::string, ContinuumData>::iterator ContinuumIterator; -#endif + ////////////////////////////////////////////////////////////////// + // Data members related to the continuum problems + ////////////////////////////////////////////////////////////////// + + struct ContinuumData + { + const MatrixType* stiffnessMatrix_; + + Dune::shared_ptr< ::LoopSolver<VectorType> > solver_; + + Dune::BitSetVector<dim> dirichletAndCouplingNodes_; + + LocalOperatorAssembler<ContinuumGridType, + ContinuumLocalFiniteElement, + ContinuumLocalFiniteElement, + Dune::FieldMatrix<double,dim,dim> >* localAssembler_; + }; + + /** \brief Simple const access to continua */ + const ContinuumData& continuum(const std::string& name) const + { + assert(continua_.find(name) != continua_.end()); + return continua_.find(name)->second; + } + + std::map<std::string, ContinuumData> continua_; + + typedef typename std::map<std::string, ContinuumData>::iterator ContinuumIterator; +#endif public: - /** \todo Should be part of ContinuumData, too */ - mutable std::map<std::string, ContinuumConfigurationType> continuumSubdomainSolutions_; + /** \todo Should be part of ContinuumData, too */ + mutable std::map<std::string, ContinuumConfigurationType> continuumSubdomainSolutions_; }; @@ -159,28 +159,28 @@ template <class RodGridType, class ContinuumGridType> std::set<std::string> RodContinuumDDStep<RodGridType,ContinuumGridType>:: rodsPerContinuum(const std::string& name) const { - std::set<std::string> result; - - for (typename RodContinuumComplex<RodGridType,ContinuumGridType>::ConstCouplingIterator it = complex_.couplings_.begin(); - it!=complex_.couplings_.end(); ++it) - if (it->first.second == name) - result.insert(it->first.first); - - return result; + std::set<std::string> result; + + for (typename RodContinuumComplex<RodGridType,ContinuumGridType>::ConstCouplingIterator it = complex_.couplings_.begin(); + it!=complex_.couplings_.end(); ++it) + if (it->first.second == name) + result.insert(it->first.first); + + return result; } template <class RodGridType, class ContinuumGridType> std::set<std::string> RodContinuumDDStep<RodGridType,ContinuumGridType>:: continuaPerRod(const std::string& name) const { - std::set<std::string> result; - - for (typename RodContinuumComplex<RodGridType,ContinuumGridType>::ConstCouplingIterator it = complex_.couplings_.begin(); - it!=complex_.couplings_.end(); ++it) - if (it->first.first == name) - result.insert(it->first.second); - - return result; + std::set<std::string> result; + + for (typename RodContinuumComplex<RodGridType,ContinuumGridType>::ConstCouplingIterator it = complex_.couplings_.begin(); + it!=complex_.couplings_.end(); ++it) + if (it->first.first == name) + result.insert(it->first.second); + + return result; } #endif diff --git a/dune/gfe/coupling/rodcontinuumfixedpointstep.hh b/dune/gfe/coupling/rodcontinuumfixedpointstep.hh index b41f365a..f8909471 100644 --- a/dune/gfe/coupling/rodcontinuumfixedpointstep.hh +++ b/dune/gfe/coupling/rodcontinuumfixedpointstep.hh @@ -25,179 +25,179 @@ */ template <class RodGridType, class ContinuumGridType> class RodContinuumFixedPointStep -: public RodContinuumDDStep<RodGridType,ContinuumGridType> + : public RodContinuumDDStep<RodGridType,ContinuumGridType> { - static const int dim = ContinuumGridType::dimension; - - // The type used for rod configurations - typedef std::vector<RigidBodyMotion<double,dim> > RodConfigurationType; - - // The type used for continuum configurations - typedef Dune::BlockVector<Dune::FieldVector<double,dim> > VectorType; - typedef Dune::BlockVector<Dune::FieldVector<double,dim> > ContinuumConfigurationType; - - typedef Dune::BlockVector<Dune::FieldVector<double,6> > RodCorrectionType; - - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,3,3> > MatrixType; - - typedef typename P1NodalBasis<typename ContinuumGridType::LeafGridView,double>::LocalFiniteElement ContinuumLocalFiniteElement; - - typedef BoundaryPatch<typename RodGridType::LeafGridView> RodLeafBoundaryPatch; - typedef BoundaryPatch<typename ContinuumGridType::LeafGridView> ContinuumLeafBoundaryPatch; - -public: - - /** \brief Constructor for a complex with one rod and one continuum */ - RodContinuumFixedPointStep(const RodContinuumComplex<RodGridType,ContinuumGridType>& complex, - double damping, - RodAssembler<typename RodGridType::LeafGridView,3>* rodAssembler, - RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* rodSolver, - const MatrixType* stiffnessMatrix3d, - const Dune::shared_ptr< ::LoopSolver<VectorType> > solver) - : RodContinuumDDStep<RodGridType,ContinuumGridType>(complex), - damping_(damping) - { - rods_["rod"].assembler_ = rodAssembler; - rods_["rod"].solver_ = rodSolver; - - continua_["continuum"].stiffnessMatrix_ = stiffnessMatrix3d; - continua_["continuum"].solver_ = solver; - - mergeRodDirichletAndCouplingBoundaries(); - mergeContinuumDirichletAndCouplingBoundaries(); - } + static const int dim = ContinuumGridType::dimension; - /** \brief Constructor for a general complex */ - RodContinuumFixedPointStep(const RodContinuumComplex<RodGridType,ContinuumGridType>& complex, - double damping, - const std::map<std::string,RodAssembler<typename RodGridType::LeafGridView,3>*>& rodAssembler, - const std::map<std::string,RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >*>& rodSolver, - const std::map<std::string,const MatrixType*>& stiffnessMatrix3d, - const std::map<std::string, const Dune::shared_ptr< ::LoopSolver<VectorType> > >& solver) - : RodContinuumDDStep<RodGridType,ContinuumGridType>(complex), - damping_(damping) - { - /////////////////////////////////////////////////////////////////////////////////// - // Rod-related data - /////////////////////////////////////////////////////////////////////////////////// - for (typename std::map<std::string,RodAssembler<typename RodGridType::LeafGridView,3>*>::const_iterator it = rodAssembler.begin(); - it != rodAssembler.end(); - ++it) - rods_[it->first].assembler_ = it->second; - - for (typename std::map<std::string,RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >*>::const_iterator it = rodSolver.begin(); - it != rodSolver.end(); - ++it) - rods_[it->first].solver_ = it->second; - - /////////////////////////////////////////////////////////////////////////////////// - // Continuum-related data - /////////////////////////////////////////////////////////////////////////////////// - for (typename std::map<std::string,const MatrixType*>::const_iterator it = stiffnessMatrix3d.begin(); - it != stiffnessMatrix3d.end(); - ++it) - continua_[it->first].stiffnessMatrix_ = it->second; - - for (typename std::map<std::string,const Dune::shared_ptr< ::LoopSolver<VectorType> > >::const_iterator it = solver.begin(); - it != solver.end(); - ++it) - continua_[it->first].solver_ = it->second; - - mergeRodDirichletAndCouplingBoundaries(); - mergeContinuumDirichletAndCouplingBoundaries(); - } + // The type used for rod configurations + typedef std::vector<RigidBodyMotion<double,dim> > RodConfigurationType; - void mergeRodDirichletAndCouplingBoundaries(); + // The type used for continuum configurations + typedef Dune::BlockVector<Dune::FieldVector<double,dim> > VectorType; + typedef Dune::BlockVector<Dune::FieldVector<double,dim> > ContinuumConfigurationType; - void mergeContinuumDirichletAndCouplingBoundaries(); + typedef Dune::BlockVector<Dune::FieldVector<double,6> > RodCorrectionType; - - /** \brief Do one fixed-point step - * \param[in,out] lambda The old and new iterate - */ - void iterate(std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >& lambda); + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,3,3> > MatrixType; + + typedef typename P1NodalBasis<typename ContinuumGridType::LeafGridView,double>::LocalFiniteElement ContinuumLocalFiniteElement; + + typedef BoundaryPatch<typename RodGridType::LeafGridView> RodLeafBoundaryPatch; + typedef BoundaryPatch<typename ContinuumGridType::LeafGridView> ContinuumLeafBoundaryPatch; + +public: + + /** \brief Constructor for a complex with one rod and one continuum */ + RodContinuumFixedPointStep(const RodContinuumComplex<RodGridType,ContinuumGridType>& complex, + double damping, + RodAssembler<typename RodGridType::LeafGridView,3>* rodAssembler, + RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* rodSolver, + const MatrixType* stiffnessMatrix3d, + const Dune::shared_ptr< ::LoopSolver<VectorType> > solver) + : RodContinuumDDStep<RodGridType,ContinuumGridType>(complex), + damping_(damping) + { + rods_["rod"].assembler_ = rodAssembler; + rods_["rod"].solver_ = rodSolver; + + continua_["continuum"].stiffnessMatrix_ = stiffnessMatrix3d; + continua_["continuum"].solver_ = solver; + + mergeRodDirichletAndCouplingBoundaries(); + mergeContinuumDirichletAndCouplingBoundaries(); + } + + /** \brief Constructor for a general complex */ + RodContinuumFixedPointStep(const RodContinuumComplex<RodGridType,ContinuumGridType>& complex, + double damping, + const std::map<std::string,RodAssembler<typename RodGridType::LeafGridView,3>*>& rodAssembler, + const std::map<std::string,RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >*>& rodSolver, + const std::map<std::string,const MatrixType*>& stiffnessMatrix3d, + const std::map<std::string, const Dune::shared_ptr< ::LoopSolver<VectorType> > >& solver) + : RodContinuumDDStep<RodGridType,ContinuumGridType>(complex), + damping_(damping) + { + /////////////////////////////////////////////////////////////////////////////////// + // Rod-related data + /////////////////////////////////////////////////////////////////////////////////// + for (typename std::map<std::string,RodAssembler<typename RodGridType::LeafGridView,3>*>::const_iterator it = rodAssembler.begin(); + it != rodAssembler.end(); + ++it) + rods_[it->first].assembler_ = it->second; + + for (typename std::map<std::string,RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >*>::const_iterator it = rodSolver.begin(); + it != rodSolver.end(); + ++it) + rods_[it->first].solver_ = it->second; + + /////////////////////////////////////////////////////////////////////////////////// + // Continuum-related data + /////////////////////////////////////////////////////////////////////////////////// + for (typename std::map<std::string,const MatrixType*>::const_iterator it = stiffnessMatrix3d.begin(); + it != stiffnessMatrix3d.end(); + ++it) + continua_[it->first].stiffnessMatrix_ = it->second; + + for (typename std::map<std::string,const Dune::shared_ptr< ::LoopSolver<VectorType> > >::const_iterator it = solver.begin(); + it != solver.end(); + ++it) + continua_[it->first].solver_ = it->second; + + mergeRodDirichletAndCouplingBoundaries(); + mergeContinuumDirichletAndCouplingBoundaries(); + } + + void mergeRodDirichletAndCouplingBoundaries(); + + void mergeContinuumDirichletAndCouplingBoundaries(); + + + /** \brief Do one fixed-point step + * \param[in,out] lambda The old and new iterate + */ + void iterate(std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >& lambda); protected: - - std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> - rodDirichletToNeumannMap(const std::string& rodName, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda) const; - - std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> - rodDirichletToNeumannMap(const std::string& rodName, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda, - const RodConfigurationType& initialRodX) const; - - std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> > - continuumNeumannToDirichletMap(const std::string& continuumName, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>& forceTorque, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& centerOfTorque) const; - - ////////////////////////////////////////////////////////////////// - // Data members related to the coupled problem - ////////////////////////////////////////////////////////////////// - - /** \brief Damping factor */ - double damping_; - + + std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> + rodDirichletToNeumannMap(const std::string& rodName, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda) const; + + std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> + rodDirichletToNeumannMap(const std::string& rodName, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda, + const RodConfigurationType& initialRodX) const; + + std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> > + continuumNeumannToDirichletMap(const std::string& continuumName, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>& forceTorque, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& centerOfTorque) const; + + ////////////////////////////////////////////////////////////////// + // Data members related to the coupled problem + ////////////////////////////////////////////////////////////////// + + /** \brief Damping factor */ + double damping_; + protected: - - ////////////////////////////////////////////////////////////////// - // Data members related to the rod problems - ////////////////////////////////////////////////////////////////// - - struct RodData - { - Dune::BitSetVector<6> dirichletAndCouplingNodes_; - - RodAssembler<typename RodGridType::LeafGridView,3>* assembler_; - - RodLocalStiffness<typename RodGridType::LeafGridView,double>* localStiffness_; - - RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* solver_; - }; - - /** \brief Simple const access to rods */ - const RodData& rod(const std::string& name) const - { - assert(rods_.find(name) != rods_.end()); - return rods_.find(name)->second; - } - std::map<std::string, RodData> rods_; - - typedef typename std::map<std::string, RodData>::iterator RodIterator; - + ////////////////////////////////////////////////////////////////// + // Data members related to the rod problems + ////////////////////////////////////////////////////////////////// + + struct RodData + { + Dune::BitSetVector<6> dirichletAndCouplingNodes_; + + RodAssembler<typename RodGridType::LeafGridView,3>* assembler_; + + RodLocalStiffness<typename RodGridType::LeafGridView,double>* localStiffness_; + + RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* solver_; + }; + + /** \brief Simple const access to rods */ + const RodData& rod(const std::string& name) const + { + assert(rods_.find(name) != rods_.end()); + return rods_.find(name)->second; + } + + std::map<std::string, RodData> rods_; + + typedef typename std::map<std::string, RodData>::iterator RodIterator; + protected: - ////////////////////////////////////////////////////////////////// - // Data members related to the continuum problems - ////////////////////////////////////////////////////////////////// - - struct ContinuumData - { - const MatrixType* stiffnessMatrix_; - - Dune::shared_ptr< ::LoopSolver<VectorType> > solver_; - - Dune::BitSetVector<dim> dirichletAndCouplingNodes_; - - LocalOperatorAssembler<ContinuumGridType, - ContinuumLocalFiniteElement, - ContinuumLocalFiniteElement, - Dune::FieldMatrix<double,dim,dim> >* localAssembler_; - }; - - /** \brief Simple const access to continua */ - const ContinuumData& continuum(const std::string& name) const - { - assert(continua_.find(name) != continua_.end()); - return continua_.find(name)->second; - } + ////////////////////////////////////////////////////////////////// + // Data members related to the continuum problems + ////////////////////////////////////////////////////////////////// + + struct ContinuumData + { + const MatrixType* stiffnessMatrix_; + + Dune::shared_ptr< ::LoopSolver<VectorType> > solver_; + + Dune::BitSetVector<dim> dirichletAndCouplingNodes_; + + LocalOperatorAssembler<ContinuumGridType, + ContinuumLocalFiniteElement, + ContinuumLocalFiniteElement, + Dune::FieldMatrix<double,dim,dim> >* localAssembler_; + }; + + /** \brief Simple const access to continua */ + const ContinuumData& continuum(const std::string& name) const + { + assert(continua_.find(name) != continua_.end()); + return continua_.find(name)->second; + } - std::map<std::string, ContinuumData> continua_; + std::map<std::string, ContinuumData> continua_; + + typedef typename std::map<std::string, ContinuumData>::iterator ContinuumIterator; - typedef typename std::map<std::string, ContinuumData>::iterator ContinuumIterator; - }; @@ -205,55 +205,55 @@ template <class RodGridType, class ContinuumGridType> void RodContinuumFixedPointStep<RodGridType,ContinuumGridType>:: mergeRodDirichletAndCouplingBoundaries() { - //////////////////////////////////////////////////////////////////////////////////// - // For each rod, merge the Dirichlet boundary with all interface boundaries - // - // Currently, we can really only solve rod problems with complete Dirichlet - // boundary. Hence there are more direct ways to construct the - // dirichletAndCouplingNodes field. Yet like to keep the analogy to the continuum - // problem. And maybe one day we have a more flexible rod solver, too. - //////////////////////////////////////////////////////////////////////////////////// - - for (RodIterator rIt = rods_.begin(); rIt != rods_.end(); ++rIt) { - - // name of the current rod - const std::string& name = rIt->first; - - // short-cut to avoid frequent map look-up - Dune::BitSetVector<6>& dirichletAndCouplingNodes = rods_[name].dirichletAndCouplingNodes_; - - dirichletAndCouplingNodes.resize(this->complex_.rodGrid(name)->size(1)); - - // first copy the true Dirichlet boundary - const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rods_.find(name)->second.dirichletBoundary_; - - for (int i=0; i<dirichletAndCouplingNodes.size(); i++) - dirichletAndCouplingNodes[i] = dirichletBoundary.containsVertex(i); - - // get the names of all the continua that we couple with - std::set<std::string> continuumNames = this->continuaPerRod(name); - - for (std::set<std::string>::const_iterator cIt = continuumNames.begin(); - cIt != continuumNames.end(); - ++cIt) { - - const RodLeafBoundaryPatch& rodInterfaceBoundary - = this->complex_.coupling(std::make_pair(name,*cIt)).rodInterfaceBoundary_; - - /** \todo Use the BoundaryPatch iterator here, for increased efficiency */ - for (int i=0; i<dirichletAndCouplingNodes.size(); i++) { - bool v = rodInterfaceBoundary.containsVertex(i); - for (int j=0; j<6; j++) - dirichletAndCouplingNodes[i][j] = dirichletAndCouplingNodes[i][j] or v; - } - - } - - // We can only handle rod problems with a full Dirichlet boundary - assert(dirichletAndCouplingNodes.count()==12); - + //////////////////////////////////////////////////////////////////////////////////// + // For each rod, merge the Dirichlet boundary with all interface boundaries + // + // Currently, we can really only solve rod problems with complete Dirichlet + // boundary. Hence there are more direct ways to construct the + // dirichletAndCouplingNodes field. Yet like to keep the analogy to the continuum + // problem. And maybe one day we have a more flexible rod solver, too. + //////////////////////////////////////////////////////////////////////////////////// + + for (RodIterator rIt = rods_.begin(); rIt != rods_.end(); ++rIt) { + + // name of the current rod + const std::string& name = rIt->first; + + // short-cut to avoid frequent map look-up + Dune::BitSetVector<6>& dirichletAndCouplingNodes = rods_[name].dirichletAndCouplingNodes_; + + dirichletAndCouplingNodes.resize(this->complex_.rodGrid(name)->size(1)); + + // first copy the true Dirichlet boundary + const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rods_.find(name)->second.dirichletBoundary_; + + for (int i=0; i<dirichletAndCouplingNodes.size(); i++) + dirichletAndCouplingNodes[i] = dirichletBoundary.containsVertex(i); + + // get the names of all the continua that we couple with + std::set<std::string> continuumNames = this->continuaPerRod(name); + + for (std::set<std::string>::const_iterator cIt = continuumNames.begin(); + cIt != continuumNames.end(); + ++cIt) { + + const RodLeafBoundaryPatch& rodInterfaceBoundary + = this->complex_.coupling(std::make_pair(name,*cIt)).rodInterfaceBoundary_; + + /** \todo Use the BoundaryPatch iterator here, for increased efficiency */ + for (int i=0; i<dirichletAndCouplingNodes.size(); i++) { + bool v = rodInterfaceBoundary.containsVertex(i); + for (int j=0; j<6; j++) + dirichletAndCouplingNodes[i][j] = dirichletAndCouplingNodes[i][j] or v; + } + } - + + // We can only handle rod problems with a full Dirichlet boundary + assert(dirichletAndCouplingNodes.count()==12); + + } + } @@ -261,122 +261,122 @@ template <class RodGridType, class ContinuumGridType> void RodContinuumFixedPointStep<RodGridType,ContinuumGridType>:: mergeContinuumDirichletAndCouplingBoundaries() { - //////////////////////////////////////////////////////////////////////////////////// - // For each continuum, merge the Dirichlet boundary with all interface boundaries - //////////////////////////////////////////////////////////////////////////////////// - - for (ContinuumIterator cIt = continua_.begin(); cIt != continua_.end(); ++cIt) { - - // name of the current continuum - const std::string& name = cIt->first; - - // short-cut to avoid frequent map look-up - Dune::BitSetVector<dim>& dirichletAndCouplingNodes = continua_[name].dirichletAndCouplingNodes_; - - dirichletAndCouplingNodes.resize(this->complex_.continuumGrid(name)->size(dim)); - - // first copy the true Dirichlet boundary - const ContinuumLeafBoundaryPatch& dirichletBoundary = this->complex_.continua_.find(name)->second.dirichletBoundary_; - - for (int i=0; i<dirichletAndCouplingNodes.size(); i++) - dirichletAndCouplingNodes[i] = dirichletBoundary.containsVertex(i); - - // get the names of all the rods that we couple with - std::set<std::string> rodNames = this->rodsPerContinuum(name); - - for (std::set<std::string>::const_iterator rIt = rodNames.begin(); - rIt != rodNames.end(); - ++rIt) { - - const ContinuumLeafBoundaryPatch& continuumInterfaceBoundary - = this->complex_.coupling(std::make_pair(*rIt,name)).continuumInterfaceBoundary_; - - /** \todo Use the BoundaryPatch iterator here, for increased efficiency */ - for (int i=0; i<dirichletAndCouplingNodes.size(); i++) { - bool v = continuumInterfaceBoundary.containsVertex(i); - for (int j=0; j<dim; j++) - dirichletAndCouplingNodes[i][j] = dirichletAndCouplingNodes[i][j] or v; - } - - } - + //////////////////////////////////////////////////////////////////////////////////// + // For each continuum, merge the Dirichlet boundary with all interface boundaries + //////////////////////////////////////////////////////////////////////////////////// + + for (ContinuumIterator cIt = continua_.begin(); cIt != continua_.end(); ++cIt) { + + // name of the current continuum + const std::string& name = cIt->first; + + // short-cut to avoid frequent map look-up + Dune::BitSetVector<dim>& dirichletAndCouplingNodes = continua_[name].dirichletAndCouplingNodes_; + + dirichletAndCouplingNodes.resize(this->complex_.continuumGrid(name)->size(dim)); + + // first copy the true Dirichlet boundary + const ContinuumLeafBoundaryPatch& dirichletBoundary = this->complex_.continua_.find(name)->second.dirichletBoundary_; + + for (int i=0; i<dirichletAndCouplingNodes.size(); i++) + dirichletAndCouplingNodes[i] = dirichletBoundary.containsVertex(i); + + // get the names of all the rods that we couple with + std::set<std::string> rodNames = this->rodsPerContinuum(name); + + for (std::set<std::string>::const_iterator rIt = rodNames.begin(); + rIt != rodNames.end(); + ++rIt) { + + const ContinuumLeafBoundaryPatch& continuumInterfaceBoundary + = this->complex_.coupling(std::make_pair(*rIt,name)).continuumInterfaceBoundary_; + + /** \todo Use the BoundaryPatch iterator here, for increased efficiency */ + for (int i=0; i<dirichletAndCouplingNodes.size(); i++) { + bool v = continuumInterfaceBoundary.containsVertex(i); + for (int j=0; j<dim; j++) + dirichletAndCouplingNodes[i][j] = dirichletAndCouplingNodes[i][j] or v; + } + } - + + } + } template <class RodGridType, class ContinuumGridType> std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> RodContinuumFixedPointStep<RodGridType,ContinuumGridType>:: -rodDirichletToNeumannMap(const std::string& rodName, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda) const +rodDirichletToNeumannMap(const std::string& rodName, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda) const { - // container for the subdomain solution - RodConfigurationType& rodX = this->rodSubdomainSolutions_[rodName]; - rodX.resize(this->complex_.rodGrid(rodName)->size(1)); - - /////////////////////////////////////////////////////////// - // Set the complete set of Dirichlet values - /////////////////////////////////////////////////////////// - const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rod(rodName).dirichletBoundary_; - const RodConfigurationType& dirichletValues = this->complex_.rod(rodName).dirichletValues_; - + // container for the subdomain solution + RodConfigurationType& rodX = this->rodSubdomainSolutions_[rodName]; + rodX.resize(this->complex_.rodGrid(rodName)->size(1)); + + /////////////////////////////////////////////////////////// + // Set the complete set of Dirichlet values + /////////////////////////////////////////////////////////// + const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rod(rodName).dirichletBoundary_; + const RodConfigurationType& dirichletValues = this->complex_.rod(rodName).dirichletValues_; + + for (size_t i=0; i<rodX.size(); i++) + if (dirichletBoundary.containsVertex(i)) + rodX[i] = dirichletValues[i]; + + typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::const_iterator it = lambda.begin(); + for (; it!=lambda.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.first != rodName) + continue; + + // Use \lambda as a Dirichlet value for the rod + const RodLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; + + /** \todo Use the BoundaryPatch iterator, which will be a lot faster + * once we use EntitySeed for its implementation + */ for (size_t i=0; i<rodX.size(); i++) - if (dirichletBoundary.containsVertex(i)) - rodX[i] = dirichletValues[i]; - - typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::const_iterator it = lambda.begin(); - for (; it!=lambda.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.first != rodName) - continue; - - // Use \lambda as a Dirichlet value for the rod - const RodLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; - - /** \todo Use the BoundaryPatch iterator, which will be a lot faster - * once we use EntitySeed for its implementation - */ - for (size_t i=0; i<rodX.size(); i++) - if (interfaceBoundary.containsVertex(i)) - rodX[i] = it->second; - } - - // Set the correct Dirichlet nodes - rod(rodName).solver_->setIgnoreNodes(rod(rodName).dirichletAndCouplingNodes_); - - //////////////////////////////////////////////////////////////////////////////// - // Solve the Dirichlet problem - //////////////////////////////////////////////////////////////////////////////// - - // Set initial iterate by interpolating between the Dirichlet values - RodFactory<typename RodGridType::LeafGridView> rodFactory(this->complex_.rodGrid(rodName)->leafGridView()); - rodFactory.create(rodX); - - rod(rodName).solver_->setInitialSolution(rodX); - - // Solve the Dirichlet problem - rod(rodName).solver_->solve(); - - rodX = rod(rodName).solver_->getSol(); - - // Extract Neumann values - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector > result; - - for (it = lambda.begin(); it!=lambda.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.first != rodName) - continue; - - const RodLeafBoundaryPatch& couplingBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; - - result[couplingName] = rod(rodName).assembler_->getResultantForce(couplingBoundary, rodX); - } - - return result; + if (interfaceBoundary.containsVertex(i)) + rodX[i] = it->second; + } + + // Set the correct Dirichlet nodes + rod(rodName).solver_->setIgnoreNodes(rod(rodName).dirichletAndCouplingNodes_); + + //////////////////////////////////////////////////////////////////////////////// + // Solve the Dirichlet problem + //////////////////////////////////////////////////////////////////////////////// + + // Set initial iterate by interpolating between the Dirichlet values + RodFactory<typename RodGridType::LeafGridView> rodFactory(this->complex_.rodGrid(rodName)->leafGridView()); + rodFactory.create(rodX); + + rod(rodName).solver_->setInitialSolution(rodX); + + // Solve the Dirichlet problem + rod(rodName).solver_->solve(); + + rodX = rod(rodName).solver_->getSol(); + + // Extract Neumann values + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector > result; + + for (it = lambda.begin(); it!=lambda.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.first != rodName) + continue; + + const RodLeafBoundaryPatch& couplingBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; + + result[couplingName] = rod(rodName).assembler_->getResultantForce(couplingBoundary, rodX); + } + + return result; } @@ -384,258 +384,258 @@ rodDirichletToNeumannMap(const std::string& rodName, template <class RodGridType, class ContinuumGridType> std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> RodContinuumFixedPointStep<RodGridType,ContinuumGridType>:: -rodDirichletToNeumannMap(const std::string& rodName, +rodDirichletToNeumannMap(const std::string& rodName, const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda, const RodConfigurationType& initialRodX ) const { - // container for the subdomain solution - RodConfigurationType& rodX = this->rodSubdomainSolutions_[rodName]; - rodX.resize(this->complex_.rodGrid(rodName)->size(1)); - - rodX = initialRodX; - - /////////////////////////////////////////////////////////// - // Set the complete set of Dirichlet values - /////////////////////////////////////////////////////////// - const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rod(rodName).dirichletBoundary_; - const RodConfigurationType& dirichletValues = this->complex_.rod(rodName).dirichletValues_; - + // container for the subdomain solution + RodConfigurationType& rodX = this->rodSubdomainSolutions_[rodName]; + rodX.resize(this->complex_.rodGrid(rodName)->size(1)); + + rodX = initialRodX; + + /////////////////////////////////////////////////////////// + // Set the complete set of Dirichlet values + /////////////////////////////////////////////////////////// + const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rod(rodName).dirichletBoundary_; + const RodConfigurationType& dirichletValues = this->complex_.rod(rodName).dirichletValues_; + + for (size_t i=0; i<rodX.size(); i++) + if (dirichletBoundary.containsVertex(i)) + rodX[i] = dirichletValues[i]; + + typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::const_iterator it = lambda.begin(); + for (; it!=lambda.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.first != rodName) + continue; + + // Use \lambda as a Dirichlet value for the rod + const RodLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; + + /** \todo Use the BoundaryPatch iterator, which will be a lot faster + * once we use EntitySeed for its implementation + */ for (size_t i=0; i<rodX.size(); i++) - if (dirichletBoundary.containsVertex(i)) - rodX[i] = dirichletValues[i]; - - typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::const_iterator it = lambda.begin(); - for (; it!=lambda.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.first != rodName) - continue; - - // Use \lambda as a Dirichlet value for the rod - const RodLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; - - /** \todo Use the BoundaryPatch iterator, which will be a lot faster - * once we use EntitySeed for its implementation - */ - for (size_t i=0; i<rodX.size(); i++) - if (interfaceBoundary.containsVertex(i)) - rodX[i] = it->second; - } - - // Set the correct Dirichlet nodes - rod(rodName).solver_->setIgnoreNodes(rod(rodName).dirichletAndCouplingNodes_); - - //////////////////////////////////////////////////////////////////////////////// - // Solve the Dirichlet problem - //////////////////////////////////////////////////////////////////////////////// - - - rod(rodName).solver_->setInitialSolution(rodX); - - // Solve the Dirichlet problem - rod(rodName).solver_->solve(); - - rodX = rod(rodName).solver_->getSol(); - - // Extract Neumann values - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector > result; - - for (it = lambda.begin(); it!=lambda.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.first != rodName) - continue; - - const RodLeafBoundaryPatch& couplingBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; - - result[couplingName] = rod(rodName).assembler_->getResultantForce(couplingBoundary, rodX); - } - - return result; + if (interfaceBoundary.containsVertex(i)) + rodX[i] = it->second; + } + + // Set the correct Dirichlet nodes + rod(rodName).solver_->setIgnoreNodes(rod(rodName).dirichletAndCouplingNodes_); + + //////////////////////////////////////////////////////////////////////////////// + // Solve the Dirichlet problem + //////////////////////////////////////////////////////////////////////////////// + + + rod(rodName).solver_->setInitialSolution(rodX); + + // Solve the Dirichlet problem + rod(rodName).solver_->solve(); + + rodX = rod(rodName).solver_->getSol(); + + // Extract Neumann values + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector > result; + + for (it = lambda.begin(); it!=lambda.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.first != rodName) + continue; + + const RodLeafBoundaryPatch& couplingBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; + + result[couplingName] = rod(rodName).assembler_->getResultantForce(couplingBoundary, rodX); + } + + return result; } template <class RodGridType, class ContinuumGridType> -std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> > +std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> > RodContinuumFixedPointStep<RodGridType,ContinuumGridType>:: continuumNeumannToDirichletMap(const std::string& continuumName, const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>& forceTorque, const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& centerOfTorque) const { - //////////////////////////////////////////////////// - // Assemble the problem - //////////////////////////////////////////////////// - - typedef P1NodalBasis<typename ContinuumGridType::LeafGridView,double> P1Basis; - const ContinuumLeafBoundaryPatch& dirichletBoundary = this->complex_.continuum(continuumName).dirichletBoundary_; - P1Basis basis(dirichletBoundary.gridView()); - - const MatrixType& stiffnessMatrix = *continuum(continuumName).stiffnessMatrix_; - - ///////////////////////////////////////////////////////////////////// - // Turn the input force and torque into a Neumann boundary field - ///////////////////////////////////////////////////////////////////// - - // The weak form of the volume and Neumann data - /** \brief Not implemented yet! */ - VectorType rhs(this->complex_.continuumGrid(continuumName)->size(dim)); - rhs = 0; - - typedef typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>::const_iterator ForceIterator; - - for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.second != continuumName) - continue; - - // Use 'forceTorque' as a Neumann value for the rod - const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; - - // - VectorType localNeumannValues; - computeAveragePressure<typename ContinuumGridType::LeafGridView>(forceTorque.find(couplingName)->second, - interfaceBoundary, - centerOfTorque.find(couplingName)->second.r, - localNeumannValues); - - BoundaryFunctionalAssembler<P1Basis> boundaryFunctionalAssembler(basis, interfaceBoundary); - BasisGridFunction<P1Basis,VectorType> neumannValuesFunction(basis,localNeumannValues); - NeumannBoundaryAssembler<ContinuumGridType, Dune::FieldVector<double,3> > localNeumannAssembler(neumannValuesFunction); - boundaryFunctionalAssembler.assemble(localNeumannAssembler, rhs, false); + //////////////////////////////////////////////////// + // Assemble the problem + //////////////////////////////////////////////////// - } - - // Set the correct Dirichlet nodes - /** \brief Don't write this BitSetVector at each iteration */ - Dune::BitSetVector<dim> dirichletNodes(rhs.size(),false); - for (size_t i=0; i<dirichletNodes.size(); i++) - dirichletNodes[i] = dirichletBoundary.containsVertex(i); - dynamic_cast<IterationStep<VectorType>* >(continuum(continuumName).solver_->iterationStep_)->ignoreNodes_ - = &dirichletNodes; - - // Initial iterate is 0, all Dirichlet values are 0 (because we solve a correction problem - VectorType x(dirichletNodes.size()); - x = 0; - - assert( (dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)) ); - dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)->setProblem(stiffnessMatrix, x, rhs); - - //solver.preprocess(); - - continuum(continuumName).solver_->solve(); - - x = continuum(continuumName).solver_->iterationStep_->getSol(); - - ///////////////////////////////////////////////////////////////////////////////// - // Average the continuum displacement on the coupling boundary - ///////////////////////////////////////////////////////////////////////////////// - std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> > averageInterface; - - for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.second != continuumName) - continue; - - // Use 'forceTorque' as a Neumann value for the rod - const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; - - computeAverageInterface(interfaceBoundary, x, averageInterface[couplingName]); - - } - - return averageInterface; + typedef P1NodalBasis<typename ContinuumGridType::LeafGridView,double> P1Basis; + const ContinuumLeafBoundaryPatch& dirichletBoundary = this->complex_.continuum(continuumName).dirichletBoundary_; + P1Basis basis(dirichletBoundary.gridView()); + + const MatrixType& stiffnessMatrix = *continuum(continuumName).stiffnessMatrix_; + + ///////////////////////////////////////////////////////////////////// + // Turn the input force and torque into a Neumann boundary field + ///////////////////////////////////////////////////////////////////// + + // The weak form of the volume and Neumann data + /** \brief Not implemented yet! */ + VectorType rhs(this->complex_.continuumGrid(continuumName)->size(dim)); + rhs = 0; + + typedef typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>::const_iterator ForceIterator; + + for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.second != continuumName) + continue; + + // Use 'forceTorque' as a Neumann value for the rod + const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; + + // + VectorType localNeumannValues; + computeAveragePressure<typename ContinuumGridType::LeafGridView>(forceTorque.find(couplingName)->second, + interfaceBoundary, + centerOfTorque.find(couplingName)->second.r, + localNeumannValues); + + BoundaryFunctionalAssembler<P1Basis> boundaryFunctionalAssembler(basis, interfaceBoundary); + BasisGridFunction<P1Basis,VectorType> neumannValuesFunction(basis,localNeumannValues); + NeumannBoundaryAssembler<ContinuumGridType, Dune::FieldVector<double,3> > localNeumannAssembler(neumannValuesFunction); + boundaryFunctionalAssembler.assemble(localNeumannAssembler, rhs, false); + + } + + // Set the correct Dirichlet nodes + /** \brief Don't write this BitSetVector at each iteration */ + Dune::BitSetVector<dim> dirichletNodes(rhs.size(),false); + for (size_t i=0; i<dirichletNodes.size(); i++) + dirichletNodes[i] = dirichletBoundary.containsVertex(i); + dynamic_cast<IterationStep<VectorType>* >(continuum(continuumName).solver_->iterationStep_)->ignoreNodes_ + = &dirichletNodes; + + // Initial iterate is 0, all Dirichlet values are 0 (because we solve a correction problem + VectorType x(dirichletNodes.size()); + x = 0; + + assert( (dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)) ); + dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)->setProblem(stiffnessMatrix, x, rhs); + + //solver.preprocess(); + + continuum(continuumName).solver_->solve(); + + x = continuum(continuumName).solver_->iterationStep_->getSol(); + + ///////////////////////////////////////////////////////////////////////////////// + // Average the continuum displacement on the coupling boundary + ///////////////////////////////////////////////////////////////////////////////// + std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> > averageInterface; + + for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.second != continuumName) + continue; + + // Use 'forceTorque' as a Neumann value for the rod + const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; + + computeAverageInterface(interfaceBoundary, x, averageInterface[couplingName]); + + } + + return averageInterface; } /** \brief One preconditioned Richardson step -*/ + */ template <class RodGridType, class ContinuumGridType> void RodContinuumFixedPointStep<RodGridType,ContinuumGridType>:: iterate(std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >& lambda) { - /////////////////////////////////////////////////////////////////// - // Evaluate the Dirichlet-to-Neumann maps for the rods - /////////////////////////////////////////////////////////////////// - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> rodForceTorque; - - for (RodIterator it = rods_.begin(); it != rods_.end(); ++it) { - - const std::string& rodName = it->first; - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> forceTorque = rodDirichletToNeumannMap(rodName, lambda); - - this->insert(rodForceTorque, forceTorque); - - } + /////////////////////////////////////////////////////////////////// + // Evaluate the Dirichlet-to-Neumann maps for the rods + /////////////////////////////////////////////////////////////////// - std::cout << "resultant rod forces and torques: " << std::endl; - typedef typename std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector>::iterator ForceIterator; - for (ForceIterator it = rodForceTorque.begin(); it != rodForceTorque.end(); ++it) - std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " - << it->second << std::endl; - - /////////////////////////////////////////////////////////////// - // Flip orientation of all rod forces, to account for opposing normals. - /////////////////////////////////////////////////////////////// - - for (ForceIterator it = rodForceTorque.begin(); it != rodForceTorque.end(); ++it) - it->second *= -1; - - - /////////////////////////////////////////////////////////////// - // Solve the Neumann problems for the continua - /////////////////////////////////////////////////////////////// - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> > averageInterface; - - for (ContinuumIterator it = continua_.begin(); it != continua_.end(); ++it) { - - const std::string& continuumName = it->first; - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> > localAverageInterface - = continuumNeumannToDirichletMap(continuumName, - rodForceTorque, - lambda); - - this->insert(averageInterface, localAverageInterface); - - } + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> rodForceTorque; - std::cout << "averaged interfaces: " << std::endl; - for (typename std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >::const_iterator it = averageInterface.begin(); it != averageInterface.end(); ++it) - std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " - << it->second << std::endl; + for (RodIterator it = rods_.begin(); it != rods_.end(); ++it) { - ////////////////////////////////////////////////////////////// - // Compute new damped interface value - ////////////////////////////////////////////////////////////// - - typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::iterator it = lambda.begin(); - typename std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >::const_iterator aIIt = averageInterface.begin(); + const std::string& rodName = it->first; - for (; it!=lambda.end(); ++it, ++aIIt) { + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> forceTorque = rodDirichletToNeumannMap(rodName, lambda); - assert(it->first == aIIt->first); - - const std::pair<std::string,std::string>& interfaceName = it->first; - - const RigidBodyMotion<double,dim>& referenceInterface = this->complex_.coupling(interfaceName).referenceInterface_; + this->insert(rodForceTorque, forceTorque); - for (int j=0; j<dim; j++) - it->second.r[j] = (1-damping_) * it->second.r[j] - + damping_ * (referenceInterface.r[j] + averageInterface[interfaceName].r[j]); + } - lambda[interfaceName].q = Rotation<double,3>::interpolate(lambda[interfaceName].q, - referenceInterface.q.mult(averageInterface[interfaceName].q), - damping_); - - } + std::cout << "resultant rod forces and torques: " << std::endl; + typedef typename std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector>::iterator ForceIterator; + for (ForceIterator it = rodForceTorque.begin(); it != rodForceTorque.end(); ++it) + std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " + << it->second << std::endl; + + /////////////////////////////////////////////////////////////// + // Flip orientation of all rod forces, to account for opposing normals. + /////////////////////////////////////////////////////////////// + + for (ForceIterator it = rodForceTorque.begin(); it != rodForceTorque.end(); ++it) + it->second *= -1; + + + /////////////////////////////////////////////////////////////// + // Solve the Neumann problems for the continua + /////////////////////////////////////////////////////////////// + + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> > averageInterface; + + for (ContinuumIterator it = continua_.begin(); it != continua_.end(); ++it) { + + const std::string& continuumName = it->first; + + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> > localAverageInterface + = continuumNeumannToDirichletMap(continuumName, + rodForceTorque, + lambda); + + this->insert(averageInterface, localAverageInterface); + + } + + std::cout << "averaged interfaces: " << std::endl; + for (typename std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >::const_iterator it = averageInterface.begin(); it != averageInterface.end(); ++it) + std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " + << it->second << std::endl; + + ////////////////////////////////////////////////////////////// + // Compute new damped interface value + ////////////////////////////////////////////////////////////// + + typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::iterator it = lambda.begin(); + typename std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >::const_iterator aIIt = averageInterface.begin(); + + for (; it!=lambda.end(); ++it, ++aIIt) { + + assert(it->first == aIIt->first); + + const std::pair<std::string,std::string>& interfaceName = it->first; + + const RigidBodyMotion<double,dim>& referenceInterface = this->complex_.coupling(interfaceName).referenceInterface_; + + for (int j=0; j<dim; j++) + it->second.r[j] = (1-damping_) * it->second.r[j] + + damping_ * (referenceInterface.r[j] + averageInterface[interfaceName].r[j]); + + lambda[interfaceName].q = Rotation<double,3>::interpolate(lambda[interfaceName].q, + referenceInterface.q.mult(averageInterface[interfaceName].q), + damping_); + + } } diff --git a/dune/gfe/coupling/rodcontinuumsteklovpoincarestep.hh b/dune/gfe/coupling/rodcontinuumsteklovpoincarestep.hh index 1b2be7d4..2cddcfb3 100644 --- a/dune/gfe/coupling/rodcontinuumsteklovpoincarestep.hh +++ b/dune/gfe/coupling/rodcontinuumsteklovpoincarestep.hh @@ -26,240 +26,240 @@ template <class RodGridType, class ContinuumGridType> class RodContinuumSteklovPoincareStep -: public RodContinuumDDStep<RodGridType,ContinuumGridType> + : public RodContinuumDDStep<RodGridType,ContinuumGridType> { - static const int dim = ContinuumGridType::dimension; - - // The type used for rod configurations - typedef std::vector<RigidBodyMotion<double,dim> > RodConfigurationType; - - // The type used for continuum configurations - typedef Dune::BlockVector<Dune::FieldVector<double,dim> > VectorType; - typedef Dune::BlockVector<Dune::FieldVector<double,dim> > ContinuumConfigurationType; - - typedef Dune::BlockVector<Dune::FieldVector<double,6> > RodCorrectionType; - - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,3,3> > MatrixType; - - typedef typename P1NodalBasis<typename ContinuumGridType::LeafGridView,double>::LocalFiniteElement ContinuumLocalFiniteElement; - - typedef BoundaryPatch<typename RodGridType::LeafGridView> RodLeafBoundaryPatch; - typedef BoundaryPatch<typename ContinuumGridType::LeafGridView> ContinuumLeafBoundaryPatch; - -public: - - /** \brief Constructor for a complex with one rod and one continuum */ - RodContinuumSteklovPoincareStep(const RodContinuumComplex<RodGridType,ContinuumGridType>& complex, - const std::string& preconditioner, - const std::array<double,2>& alpha, - double richardsonDamping, - RodAssembler<typename RodGridType::LeafGridView,3>* rodAssembler, - RodLocalStiffness<typename RodGridType::LeafGridView,double>* rodLocalStiffness, - RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* rodSolver, - const MatrixType* stiffnessMatrix3d, - const Dune::shared_ptr< ::LoopSolver<VectorType> > solver, - LocalOperatorAssembler<ContinuumGridType, - ContinuumLocalFiniteElement, - ContinuumLocalFiniteElement, - Dune::FieldMatrix<double,dim,dim> >* localAssembler) - : RodContinuumDDStep<RodGridType,ContinuumGridType>(complex), - preconditioner_(preconditioner), - alpha_(alpha), - richardsonDamping_(richardsonDamping), - neumannRegularization_(0) - { - rods_["rod"].assembler_ = rodAssembler; - rods_["rod"].localStiffness_ = rodLocalStiffness; - rods_["rod"].solver_ = rodSolver; - - continua_["continuum"].stiffnessMatrix_ = stiffnessMatrix3d; - continua_["continuum"].solver_ = solver; - continua_["continuum"].localAssembler_ = localAssembler; - - mergeRodDirichletAndCouplingBoundaries(); - mergeContinuumDirichletAndCouplingBoundaries(); - } + static const int dim = ContinuumGridType::dimension; - /** \brief Constructor for a general complex */ - RodContinuumSteklovPoincareStep(const RodContinuumComplex<RodGridType,ContinuumGridType>& complex, - const std::string& preconditioner, - const std::array<double,2>& alpha, - double richardsonDamping, - const std::map<std::string,RodAssembler<typename RodGridType::LeafGridView,3>*>& rodAssembler, - const std::map<std::string,RodLocalStiffness<typename RodGridType::LeafGridView,double>*>& rodLocalStiffness, - const std::map<std::string,RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >*>& rodSolver, - const std::map<std::string,const MatrixType*>& stiffnessMatrix3d, - const std::map<std::string, const Dune::shared_ptr< ::LoopSolver<VectorType> > >& solver, - const std::map<std::string,LocalOperatorAssembler<ContinuumGridType, - ContinuumLocalFiniteElement, - ContinuumLocalFiniteElement, - Dune::FieldMatrix<double,dim,dim> >*>& localAssembler) - : RodContinuumDDStep<RodGridType,ContinuumGridType>(complex), - preconditioner_(preconditioner), - alpha_(alpha), - richardsonDamping_(richardsonDamping), - neumannRegularization_(0) - { - /////////////////////////////////////////////////////////////////////////////////// - // Rod-related data - /////////////////////////////////////////////////////////////////////////////////// - for (typename std::map<std::string,RodAssembler<typename RodGridType::LeafGridView,3>*>::const_iterator it = rodAssembler.begin(); - it != rodAssembler.end(); - ++it) - rods_[it->first].assembler_ = it->second; - - for (typename std::map<std::string,RodLocalStiffness<typename RodGridType::LeafGridView,double>*>::const_iterator it = rodLocalStiffness.begin(); - it != rodLocalStiffness.end(); - ++it) - rods_[it->first].localStiffness_ = it->second; - - for (typename std::map<std::string,RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >*>::const_iterator it = rodSolver.begin(); - it != rodSolver.end(); - ++it) - rods_[it->first].solver_ = it->second; - - /////////////////////////////////////////////////////////////////////////////////// - // Continuum-related data - /////////////////////////////////////////////////////////////////////////////////// - for (typename std::map<std::string,const MatrixType*>::const_iterator it = stiffnessMatrix3d.begin(); - it != stiffnessMatrix3d.end(); - ++it) - continua_[it->first].stiffnessMatrix_ = it->second; - - for (typename std::map<std::string,const Dune::shared_ptr< ::LoopSolver<VectorType> > >::const_iterator it = solver.begin(); - it != solver.end(); - ++it) - continua_[it->first].solver_ = it->second; - - for (typename std::map<std::string,LocalOperatorAssembler<ContinuumGridType, - ContinuumLocalFiniteElement, - ContinuumLocalFiniteElement, - Dune::FieldMatrix<double,dim,dim> >*>::const_iterator it = localAssembler.begin(); - it != localAssembler.end(); - ++it) - continua_[it->first].localAssembler_ = it->second; - - mergeRodDirichletAndCouplingBoundaries(); - mergeContinuumDirichletAndCouplingBoundaries(); - } + // The type used for rod configurations + typedef std::vector<RigidBodyMotion<double,dim> > RodConfigurationType; - void mergeRodDirichletAndCouplingBoundaries(); + // The type used for continuum configurations + typedef Dune::BlockVector<Dune::FieldVector<double,dim> > VectorType; + typedef Dune::BlockVector<Dune::FieldVector<double,dim> > ContinuumConfigurationType; - void mergeContinuumDirichletAndCouplingBoundaries(); + typedef Dune::BlockVector<Dune::FieldVector<double,6> > RodCorrectionType; - - /** \brief Do one Steklov-Poincare step - * \param[in,out] lambda The old and new iterate - */ - void iterate(std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >& lambda); + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,3,3> > MatrixType; + + typedef typename P1NodalBasis<typename ContinuumGridType::LeafGridView,double>::LocalFiniteElement ContinuumLocalFiniteElement; + + typedef BoundaryPatch<typename RodGridType::LeafGridView> RodLeafBoundaryPatch; + typedef BoundaryPatch<typename ContinuumGridType::LeafGridView> ContinuumLeafBoundaryPatch; + +public: + + /** \brief Constructor for a complex with one rod and one continuum */ + RodContinuumSteklovPoincareStep(const RodContinuumComplex<RodGridType,ContinuumGridType>& complex, + const std::string& preconditioner, + const std::array<double,2>& alpha, + double richardsonDamping, + RodAssembler<typename RodGridType::LeafGridView,3>* rodAssembler, + RodLocalStiffness<typename RodGridType::LeafGridView,double>* rodLocalStiffness, + RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* rodSolver, + const MatrixType* stiffnessMatrix3d, + const Dune::shared_ptr< ::LoopSolver<VectorType> > solver, + LocalOperatorAssembler<ContinuumGridType, + ContinuumLocalFiniteElement, + ContinuumLocalFiniteElement, + Dune::FieldMatrix<double,dim,dim> >* localAssembler) + : RodContinuumDDStep<RodGridType,ContinuumGridType>(complex), + preconditioner_(preconditioner), + alpha_(alpha), + richardsonDamping_(richardsonDamping), + neumannRegularization_(0) + { + rods_["rod"].assembler_ = rodAssembler; + rods_["rod"].localStiffness_ = rodLocalStiffness; + rods_["rod"].solver_ = rodSolver; + + continua_["continuum"].stiffnessMatrix_ = stiffnessMatrix3d; + continua_["continuum"].solver_ = solver; + continua_["continuum"].localAssembler_ = localAssembler; + + mergeRodDirichletAndCouplingBoundaries(); + mergeContinuumDirichletAndCouplingBoundaries(); + } + + /** \brief Constructor for a general complex */ + RodContinuumSteklovPoincareStep(const RodContinuumComplex<RodGridType,ContinuumGridType>& complex, + const std::string& preconditioner, + const std::array<double,2>& alpha, + double richardsonDamping, + const std::map<std::string,RodAssembler<typename RodGridType::LeafGridView,3>*>& rodAssembler, + const std::map<std::string,RodLocalStiffness<typename RodGridType::LeafGridView,double>*>& rodLocalStiffness, + const std::map<std::string,RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >*>& rodSolver, + const std::map<std::string,const MatrixType*>& stiffnessMatrix3d, + const std::map<std::string, const Dune::shared_ptr< ::LoopSolver<VectorType> > >& solver, + const std::map<std::string,LocalOperatorAssembler<ContinuumGridType, + ContinuumLocalFiniteElement, + ContinuumLocalFiniteElement, + Dune::FieldMatrix<double,dim,dim> >*>& localAssembler) + : RodContinuumDDStep<RodGridType,ContinuumGridType>(complex), + preconditioner_(preconditioner), + alpha_(alpha), + richardsonDamping_(richardsonDamping), + neumannRegularization_(0) + { + /////////////////////////////////////////////////////////////////////////////////// + // Rod-related data + /////////////////////////////////////////////////////////////////////////////////// + for (typename std::map<std::string,RodAssembler<typename RodGridType::LeafGridView,3>*>::const_iterator it = rodAssembler.begin(); + it != rodAssembler.end(); + ++it) + rods_[it->first].assembler_ = it->second; + + for (typename std::map<std::string,RodLocalStiffness<typename RodGridType::LeafGridView,double>*>::const_iterator it = rodLocalStiffness.begin(); + it != rodLocalStiffness.end(); + ++it) + rods_[it->first].localStiffness_ = it->second; + + for (typename std::map<std::string,RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >*>::const_iterator it = rodSolver.begin(); + it != rodSolver.end(); + ++it) + rods_[it->first].solver_ = it->second; + + /////////////////////////////////////////////////////////////////////////////////// + // Continuum-related data + /////////////////////////////////////////////////////////////////////////////////// + for (typename std::map<std::string,const MatrixType*>::const_iterator it = stiffnessMatrix3d.begin(); + it != stiffnessMatrix3d.end(); + ++it) + continua_[it->first].stiffnessMatrix_ = it->second; + + for (typename std::map<std::string,const Dune::shared_ptr< ::LoopSolver<VectorType> > >::const_iterator it = solver.begin(); + it != solver.end(); + ++it) + continua_[it->first].solver_ = it->second; + + for (typename std::map<std::string,LocalOperatorAssembler<ContinuumGridType, + ContinuumLocalFiniteElement, + ContinuumLocalFiniteElement, + Dune::FieldMatrix<double,dim,dim> >*>::const_iterator it = localAssembler.begin(); + it != localAssembler.end(); + ++it) + continua_[it->first].localAssembler_ = it->second; + + mergeRodDirichletAndCouplingBoundaries(); + mergeContinuumDirichletAndCouplingBoundaries(); + } + + void mergeRodDirichletAndCouplingBoundaries(); + + void mergeContinuumDirichletAndCouplingBoundaries(); + + + /** \brief Do one Steklov-Poincare step + * \param[in,out] lambda The old and new iterate + */ + void iterate(std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >& lambda); protected: - - std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> - rodDirichletToNeumannMap(const std::string& rodName, + + std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> + rodDirichletToNeumannMap(const std::string& rodName, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda) const; + + std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> + continuumDirichletToNeumannMap(const std::string& continuumName, const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda) const; - - std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> - continuumDirichletToNeumannMap(const std::string& continuumName, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda) const; - - /** \brief Map a Neumann value to a Dirichlet value - * - * \param currentIterate The rod configuration that we are linearizing about - * - * \return The Dirichlet value - */ - std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> - linearizedRodNeumannToDirichletMap(const std::string& rodName, - const RodConfigurationType& currentIterate, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>& forceTorque, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& centerOfTorque) const; - - /** \brief Map a Neumann value to a Dirichlet value - * - * \param currentIterate The continuum configuration that we are linearizing about - * - * \return The infinitesimal movement of the interface - */ - std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> - linearizedContinuumNeumannToDirichletMap(const std::string& continuumName, - const VectorType& currentIterate, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>& forceTorque, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& centerOfTorque) const; - - ////////////////////////////////////////////////////////////////// - // Data members related to the coupled problem - ////////////////////////////////////////////////////////////////// - - /** \brief Decides which preconditioner is used */ - std::string preconditioner_; - - /** \brief Neumann-Neumann damping */ - std::array<double,2> alpha_; - - /** \brief Damping factor for the Richardson iteration */ - double richardsonDamping_; - + + /** \brief Map a Neumann value to a Dirichlet value + * + * \param currentIterate The rod configuration that we are linearizing about + * + * \return The Dirichlet value + */ + std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> + linearizedRodNeumannToDirichletMap(const std::string& rodName, + const RodConfigurationType& currentIterate, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>& forceTorque, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& centerOfTorque) const; + + /** \brief Map a Neumann value to a Dirichlet value + * + * \param currentIterate The continuum configuration that we are linearizing about + * + * \return The infinitesimal movement of the interface + */ + std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> + linearizedContinuumNeumannToDirichletMap(const std::string& continuumName, + const VectorType& currentIterate, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>& forceTorque, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& centerOfTorque) const; + + ////////////////////////////////////////////////////////////////// + // Data members related to the coupled problem + ////////////////////////////////////////////////////////////////// + + /** \brief Decides which preconditioner is used */ + std::string preconditioner_; + + /** \brief Neumann-Neumann damping */ + std::array<double,2> alpha_; + + /** \brief Damping factor for the Richardson iteration */ + double richardsonDamping_; + public: - double neumannRegularization_; + double neumannRegularization_; protected: - - ////////////////////////////////////////////////////////////////// - // Data members related to the rod problems - ////////////////////////////////////////////////////////////////// - - struct RodData - { - Dune::BitSetVector<6> dirichletAndCouplingNodes_; - - RodAssembler<typename RodGridType::LeafGridView,3>* assembler_; - - RodLocalStiffness<typename RodGridType::LeafGridView,double>* localStiffness_; - - RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* solver_; - }; - - /** \brief Simple const access to rods */ - const RodData& rod(const std::string& name) const - { - assert(rods_.find(name) != rods_.end()); - return rods_.find(name)->second; - } - std::map<std::string, RodData> rods_; - - typedef typename std::map<std::string, RodData>::iterator RodIterator; - + ////////////////////////////////////////////////////////////////// + // Data members related to the rod problems + ////////////////////////////////////////////////////////////////// + + struct RodData + { + Dune::BitSetVector<6> dirichletAndCouplingNodes_; + + RodAssembler<typename RodGridType::LeafGridView,3>* assembler_; + + RodLocalStiffness<typename RodGridType::LeafGridView,double>* localStiffness_; + + RiemannianTrustRegionSolver<RodGridType,RigidBodyMotion<double,3> >* solver_; + }; + + /** \brief Simple const access to rods */ + const RodData& rod(const std::string& name) const + { + assert(rods_.find(name) != rods_.end()); + return rods_.find(name)->second; + } + + std::map<std::string, RodData> rods_; + + typedef typename std::map<std::string, RodData>::iterator RodIterator; + protected: - ////////////////////////////////////////////////////////////////// - // Data members related to the continuum problems - ////////////////////////////////////////////////////////////////// - - struct ContinuumData - { - const MatrixType* stiffnessMatrix_; - - Dune::shared_ptr< ::LoopSolver<VectorType> > solver_; - - Dune::BitSetVector<dim> dirichletAndCouplingNodes_; - - LocalOperatorAssembler<ContinuumGridType, - ContinuumLocalFiniteElement, - ContinuumLocalFiniteElement, - Dune::FieldMatrix<double,dim,dim> >* localAssembler_; - }; - - /** \brief Simple const access to continua */ - const ContinuumData& continuum(const std::string& name) const - { - assert(continua_.find(name) != continua_.end()); - return continua_.find(name)->second; - } + ////////////////////////////////////////////////////////////////// + // Data members related to the continuum problems + ////////////////////////////////////////////////////////////////// + + struct ContinuumData + { + const MatrixType* stiffnessMatrix_; + + Dune::shared_ptr< ::LoopSolver<VectorType> > solver_; + + Dune::BitSetVector<dim> dirichletAndCouplingNodes_; + + LocalOperatorAssembler<ContinuumGridType, + ContinuumLocalFiniteElement, + ContinuumLocalFiniteElement, + Dune::FieldMatrix<double,dim,dim> >* localAssembler_; + }; + + /** \brief Simple const access to continua */ + const ContinuumData& continuum(const std::string& name) const + { + assert(continua_.find(name) != continua_.end()); + return continua_.find(name)->second; + } + + std::map<std::string, ContinuumData> continua_; - std::map<std::string, ContinuumData> continua_; + typedef typename std::map<std::string, ContinuumData>::iterator ContinuumIterator; - typedef typename std::map<std::string, ContinuumData>::iterator ContinuumIterator; - }; @@ -267,55 +267,55 @@ template <class RodGridType, class ContinuumGridType> void RodContinuumSteklovPoincareStep<RodGridType,ContinuumGridType>:: mergeRodDirichletAndCouplingBoundaries() { - //////////////////////////////////////////////////////////////////////////////////// - // For each rod, merge the Dirichlet boundary with all interface boundaries - // - // Currently, we can really only solve rod problems with complete Dirichlet - // boundary. Hence there are more direct ways to construct the - // dirichletAndCouplingNodes field. Yet like to keep the analogy to the continuum - // problem. And maybe one day we have a more flexible rod solver, too. - //////////////////////////////////////////////////////////////////////////////////// - - for (RodIterator rIt = rods_.begin(); rIt != rods_.end(); ++rIt) { - - // name of the current rod - const std::string& name = rIt->first; - - // short-cut to avoid frequent map look-up - Dune::BitSetVector<6>& dirichletAndCouplingNodes = rods_[name].dirichletAndCouplingNodes_; - - dirichletAndCouplingNodes.resize(this->complex_.rodGrid(name)->size(1)); - - // first copy the true Dirichlet boundary - const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rods_.find(name)->second.dirichletBoundary_; - - for (int i=0; i<dirichletAndCouplingNodes.size(); i++) - dirichletAndCouplingNodes[i] = dirichletBoundary.containsVertex(i); - - // get the names of all the continua that we couple with - std::set<std::string> continuumNames = this->continuaPerRod(name); - - for (std::set<std::string>::const_iterator cIt = continuumNames.begin(); - cIt != continuumNames.end(); - ++cIt) { - - const RodLeafBoundaryPatch& rodInterfaceBoundary - = this->complex_.coupling(std::make_pair(name,*cIt)).rodInterfaceBoundary_; - - /** \todo Use the BoundaryPatch iterator here, for increased efficiency */ - for (int i=0; i<dirichletAndCouplingNodes.size(); i++) { - bool v = rodInterfaceBoundary.containsVertex(i); - for (int j=0; j<6; j++) - dirichletAndCouplingNodes[i][j] = dirichletAndCouplingNodes[i][j] or v; - } - - } - - // We can only handle rod problems with a full Dirichlet boundary - assert(dirichletAndCouplingNodes.count()==12); - + //////////////////////////////////////////////////////////////////////////////////// + // For each rod, merge the Dirichlet boundary with all interface boundaries + // + // Currently, we can really only solve rod problems with complete Dirichlet + // boundary. Hence there are more direct ways to construct the + // dirichletAndCouplingNodes field. Yet like to keep the analogy to the continuum + // problem. And maybe one day we have a more flexible rod solver, too. + //////////////////////////////////////////////////////////////////////////////////// + + for (RodIterator rIt = rods_.begin(); rIt != rods_.end(); ++rIt) { + + // name of the current rod + const std::string& name = rIt->first; + + // short-cut to avoid frequent map look-up + Dune::BitSetVector<6>& dirichletAndCouplingNodes = rods_[name].dirichletAndCouplingNodes_; + + dirichletAndCouplingNodes.resize(this->complex_.rodGrid(name)->size(1)); + + // first copy the true Dirichlet boundary + const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rods_.find(name)->second.dirichletBoundary_; + + for (int i=0; i<dirichletAndCouplingNodes.size(); i++) + dirichletAndCouplingNodes[i] = dirichletBoundary.containsVertex(i); + + // get the names of all the continua that we couple with + std::set<std::string> continuumNames = this->continuaPerRod(name); + + for (std::set<std::string>::const_iterator cIt = continuumNames.begin(); + cIt != continuumNames.end(); + ++cIt) { + + const RodLeafBoundaryPatch& rodInterfaceBoundary + = this->complex_.coupling(std::make_pair(name,*cIt)).rodInterfaceBoundary_; + + /** \todo Use the BoundaryPatch iterator here, for increased efficiency */ + for (int i=0; i<dirichletAndCouplingNodes.size(); i++) { + bool v = rodInterfaceBoundary.containsVertex(i); + for (int j=0; j<6; j++) + dirichletAndCouplingNodes[i][j] = dirichletAndCouplingNodes[i][j] or v; + } + } - + + // We can only handle rod problems with a full Dirichlet boundary + assert(dirichletAndCouplingNodes.count()==12); + + } + } @@ -323,230 +323,230 @@ template <class RodGridType, class ContinuumGridType> void RodContinuumSteklovPoincareStep<RodGridType,ContinuumGridType>:: mergeContinuumDirichletAndCouplingBoundaries() { - //////////////////////////////////////////////////////////////////////////////////// - // For each continuum, merge the Dirichlet boundary with all interface boundaries - //////////////////////////////////////////////////////////////////////////////////// - - for (ContinuumIterator cIt = continua_.begin(); cIt != continua_.end(); ++cIt) { - - // name of the current continuum - const std::string& name = cIt->first; - - // short-cut to avoid frequent map look-up - Dune::BitSetVector<dim>& dirichletAndCouplingNodes = continua_[name].dirichletAndCouplingNodes_; - - dirichletAndCouplingNodes.resize(this->complex_.continuumGrid(name)->size(dim)); - - // first copy the true Dirichlet boundary - const ContinuumLeafBoundaryPatch& dirichletBoundary = this->complex_.continua_.find(name)->second.dirichletBoundary_; - - for (int i=0; i<dirichletAndCouplingNodes.size(); i++) - dirichletAndCouplingNodes[i] = dirichletBoundary.containsVertex(i); - - // get the names of all the rods that we couple with - std::set<std::string> rodNames = this->rodsPerContinuum(name); - - for (std::set<std::string>::const_iterator rIt = rodNames.begin(); - rIt != rodNames.end(); - ++rIt) { - - const ContinuumLeafBoundaryPatch& continuumInterfaceBoundary - = this->complex_.coupling(std::make_pair(*rIt,name)).continuumInterfaceBoundary_; - - /** \todo Use the BoundaryPatch iterator here, for increased efficiency */ - for (int i=0; i<dirichletAndCouplingNodes.size(); i++) { - bool v = continuumInterfaceBoundary.containsVertex(i); - for (int j=0; j<dim; j++) - dirichletAndCouplingNodes[i][j] = dirichletAndCouplingNodes[i][j] or v; - } - - } - + //////////////////////////////////////////////////////////////////////////////////// + // For each continuum, merge the Dirichlet boundary with all interface boundaries + //////////////////////////////////////////////////////////////////////////////////// + + for (ContinuumIterator cIt = continua_.begin(); cIt != continua_.end(); ++cIt) { + + // name of the current continuum + const std::string& name = cIt->first; + + // short-cut to avoid frequent map look-up + Dune::BitSetVector<dim>& dirichletAndCouplingNodes = continua_[name].dirichletAndCouplingNodes_; + + dirichletAndCouplingNodes.resize(this->complex_.continuumGrid(name)->size(dim)); + + // first copy the true Dirichlet boundary + const ContinuumLeafBoundaryPatch& dirichletBoundary = this->complex_.continua_.find(name)->second.dirichletBoundary_; + + for (int i=0; i<dirichletAndCouplingNodes.size(); i++) + dirichletAndCouplingNodes[i] = dirichletBoundary.containsVertex(i); + + // get the names of all the rods that we couple with + std::set<std::string> rodNames = this->rodsPerContinuum(name); + + for (std::set<std::string>::const_iterator rIt = rodNames.begin(); + rIt != rodNames.end(); + ++rIt) { + + const ContinuumLeafBoundaryPatch& continuumInterfaceBoundary + = this->complex_.coupling(std::make_pair(*rIt,name)).continuumInterfaceBoundary_; + + /** \todo Use the BoundaryPatch iterator here, for increased efficiency */ + for (int i=0; i<dirichletAndCouplingNodes.size(); i++) { + bool v = continuumInterfaceBoundary.containsVertex(i); + for (int j=0; j<dim; j++) + dirichletAndCouplingNodes[i][j] = dirichletAndCouplingNodes[i][j] or v; + } + } - + + } + } template <class RodGridType, class ContinuumGridType> std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> RodContinuumSteklovPoincareStep<RodGridType,ContinuumGridType>:: -rodDirichletToNeumannMap(const std::string& rodName, - const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda) const +rodDirichletToNeumannMap(const std::string& rodName, + const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda) const { - // container for the subdomain solution - RodConfigurationType& rodX = this->rodSubdomainSolutions_[rodName]; - rodX.resize(this->complex_.rodGrid(rodName)->size(1)); - - /////////////////////////////////////////////////////////// - // Set the complete set of Dirichlet values - /////////////////////////////////////////////////////////// - const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rod(rodName).dirichletBoundary_; - const RodConfigurationType& dirichletValues = this->complex_.rod(rodName).dirichletValues_; - + // container for the subdomain solution + RodConfigurationType& rodX = this->rodSubdomainSolutions_[rodName]; + rodX.resize(this->complex_.rodGrid(rodName)->size(1)); + + /////////////////////////////////////////////////////////// + // Set the complete set of Dirichlet values + /////////////////////////////////////////////////////////// + const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rod(rodName).dirichletBoundary_; + const RodConfigurationType& dirichletValues = this->complex_.rod(rodName).dirichletValues_; + + for (size_t i=0; i<rodX.size(); i++) + if (dirichletBoundary.containsVertex(i)) + rodX[i] = dirichletValues[i]; + + typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::const_iterator it = lambda.begin(); + for (; it!=lambda.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.first != rodName) + continue; + + // Use \lambda as a Dirichlet value for the rod + const RodLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; + + /** \todo Use the BoundaryPatch iterator, which will be a lot faster + * once we use EntitySeed for its implementation + */ for (size_t i=0; i<rodX.size(); i++) - if (dirichletBoundary.containsVertex(i)) - rodX[i] = dirichletValues[i]; - - typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::const_iterator it = lambda.begin(); - for (; it!=lambda.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.first != rodName) - continue; - - // Use \lambda as a Dirichlet value for the rod - const RodLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; - - /** \todo Use the BoundaryPatch iterator, which will be a lot faster - * once we use EntitySeed for its implementation - */ - for (size_t i=0; i<rodX.size(); i++) - if (interfaceBoundary.containsVertex(i)) - rodX[i] = it->second; - } - - // Set the correct Dirichlet nodes - rod(rodName).solver_->setIgnoreNodes(rod(rodName).dirichletAndCouplingNodes_); - - //////////////////////////////////////////////////////////////////////////////// - // Solve the Dirichlet problem - //////////////////////////////////////////////////////////////////////////////// - - // Set initial iterate by interpolating between the Dirichlet values - RodFactory<typename RodGridType::LeafGridView> rodFactory(this->complex_.rodGrid(rodName)->leafGridView()); - rodFactory.create(rodX); - - rod(rodName).solver_->setInitialSolution(rodX); - - // Solve the Dirichlet problem - rod(rodName).solver_->solve(); - - rodX = rod(rodName).solver_->getSol(); - - // Extract Neumann values - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector > result; - - for (it = lambda.begin(); it!=lambda.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.first != rodName) - continue; - - const RodLeafBoundaryPatch& couplingBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; - - result[couplingName] = rod(rodName).assembler_->getResultantForce(couplingBoundary, rodX); - } - - return result; + if (interfaceBoundary.containsVertex(i)) + rodX[i] = it->second; + } + + // Set the correct Dirichlet nodes + rod(rodName).solver_->setIgnoreNodes(rod(rodName).dirichletAndCouplingNodes_); + + //////////////////////////////////////////////////////////////////////////////// + // Solve the Dirichlet problem + //////////////////////////////////////////////////////////////////////////////// + + // Set initial iterate by interpolating between the Dirichlet values + RodFactory<typename RodGridType::LeafGridView> rodFactory(this->complex_.rodGrid(rodName)->leafGridView()); + rodFactory.create(rodX); + + rod(rodName).solver_->setInitialSolution(rodX); + + // Solve the Dirichlet problem + rod(rodName).solver_->solve(); + + rodX = rod(rodName).solver_->getSol(); + + // Extract Neumann values + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector > result; + + for (it = lambda.begin(); it!=lambda.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.first != rodName) + continue; + + const RodLeafBoundaryPatch& couplingBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; + + result[couplingName] = rod(rodName).assembler_->getResultantForce(couplingBoundary, rodX); + } + + return result; } template <class RodGridType, class ContinuumGridType> std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> RodContinuumSteklovPoincareStep<RodGridType,ContinuumGridType>:: -continuumDirichletToNeumannMap(const std::string& continuumName, +continuumDirichletToNeumannMap(const std::string& continuumName, const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& lambda) const { - // Set initial iterate - VectorType& x3d = this->continuumSubdomainSolutions_[continuumName]; - x3d.resize(this->complex_.continuumGrid(continuumName)->size(dim)); - x3d = 0; - - // Copy the true Dirichlet values into it - const ContinuumLeafBoundaryPatch& dirichletBoundary = this->complex_.continuum(continuumName).dirichletBoundary_; - const VectorType& dirichletValues = this->complex_.continuum(continuumName).dirichletValues_; - - for (size_t i=0; i<x3d.size(); i++) - if (dirichletBoundary.containsVertex(i)) - x3d[i] = dirichletValues[i]; - - typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::const_iterator it = lambda.begin(); - for (; it!=lambda.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.second != continuumName) - continue; - - // Turn \lambda \in TSE(3) into a Dirichlet value for the continuum - const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; - const RigidBodyMotion<double,dim>& referenceInterface = this->complex_.coupling(couplingName).referenceInterface_; - - setRotation(interfaceBoundary, x3d, referenceInterface, it->second); - - } - - // Set the correct Dirichlet nodes - dynamic_cast<IterationStep<VectorType>* >(continuum(continuumName).solver_->iterationStep_)->ignoreNodes_ - = &continuum(continuumName).dirichletAndCouplingNodes_; - - // Right hand side vector: currently without Neumann and volume terms - VectorType rhs3d(x3d.size()); - rhs3d = 0; - - // Solve the Dirichlet problem - assert( (dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)) ); - dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)->setProblem(*continuum(continuumName).stiffnessMatrix_, x3d, rhs3d); - - continuum(continuumName).solver_->preprocess(); - - continuum(continuumName).solver_->solve(); - - x3d = dynamic_cast<IterationStep<VectorType>* >(continuum(continuumName).solver_->iterationStep_)->getSol(); - - // Integrate over the residual on the coupling boundary to obtain - // an element of T^*SE. - Dune::FieldVector<double,3> continuumForce, continuumTorque; - - VectorType residual = rhs3d; - continuum(continuumName).stiffnessMatrix_->mmv(x3d,residual); - - ////////////////////////////////////////////////////////////////////////////// - // Extract the residual stresses - ////////////////////////////////////////////////////////////////////////////// - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector > result; - - for (it = lambda.begin(); it!=lambda.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.second != continuumName) - continue; - - const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; - - VectorType neumannForces(residual.size()); - neumannForces = 0; - - weakToStrongBoundaryStress(interfaceBoundary, residual, neumannForces); - - /** \todo Is referenceInterface.r the correct center of rotation? */ - const RigidBodyMotion<double,dim>& referenceInterface = this->complex_.coupling(couplingName).referenceInterface_; - - computeTotalForceAndTorque(interfaceBoundary, - neumannForces, - referenceInterface.r, - continuumForce, continuumTorque); - - result[couplingName][0] = continuumForce[0]; - result[couplingName][1] = continuumForce[1]; - result[couplingName][2] = continuumForce[2]; - result[couplingName][3] = continuumTorque[0]; - result[couplingName][4] = continuumTorque[1]; - result[couplingName][5] = continuumTorque[2]; - - } - - return result; + // Set initial iterate + VectorType& x3d = this->continuumSubdomainSolutions_[continuumName]; + x3d.resize(this->complex_.continuumGrid(continuumName)->size(dim)); + x3d = 0; + + // Copy the true Dirichlet values into it + const ContinuumLeafBoundaryPatch& dirichletBoundary = this->complex_.continuum(continuumName).dirichletBoundary_; + const VectorType& dirichletValues = this->complex_.continuum(continuumName).dirichletValues_; + + for (size_t i=0; i<x3d.size(); i++) + if (dirichletBoundary.containsVertex(i)) + x3d[i] = dirichletValues[i]; + + typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::const_iterator it = lambda.begin(); + for (; it!=lambda.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.second != continuumName) + continue; + + // Turn \lambda \in TSE(3) into a Dirichlet value for the continuum + const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; + const RigidBodyMotion<double,dim>& referenceInterface = this->complex_.coupling(couplingName).referenceInterface_; + + setRotation(interfaceBoundary, x3d, referenceInterface, it->second); + + } + + // Set the correct Dirichlet nodes + dynamic_cast<IterationStep<VectorType>* >(continuum(continuumName).solver_->iterationStep_)->ignoreNodes_ + = &continuum(continuumName).dirichletAndCouplingNodes_; + + // Right hand side vector: currently without Neumann and volume terms + VectorType rhs3d(x3d.size()); + rhs3d = 0; + + // Solve the Dirichlet problem + assert( (dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)) ); + dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)->setProblem(*continuum(continuumName).stiffnessMatrix_, x3d, rhs3d); + + continuum(continuumName).solver_->preprocess(); + + continuum(continuumName).solver_->solve(); + + x3d = dynamic_cast<IterationStep<VectorType>* >(continuum(continuumName).solver_->iterationStep_)->getSol(); + + // Integrate over the residual on the coupling boundary to obtain + // an element of T^*SE. + Dune::FieldVector<double,3> continuumForce, continuumTorque; + + VectorType residual = rhs3d; + continuum(continuumName).stiffnessMatrix_->mmv(x3d,residual); + + ////////////////////////////////////////////////////////////////////////////// + // Extract the residual stresses + ////////////////////////////////////////////////////////////////////////////// + + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector > result; + + for (it = lambda.begin(); it!=lambda.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.second != continuumName) + continue; + + const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; + + VectorType neumannForces(residual.size()); + neumannForces = 0; + + weakToStrongBoundaryStress(interfaceBoundary, residual, neumannForces); + + /** \todo Is referenceInterface.r the correct center of rotation? */ + const RigidBodyMotion<double,dim>& referenceInterface = this->complex_.coupling(couplingName).referenceInterface_; + + computeTotalForceAndTorque(interfaceBoundary, + neumannForces, + referenceInterface.r, + continuumForce, continuumTorque); + + result[couplingName][0] = continuumForce[0]; + result[couplingName][1] = continuumForce[1]; + result[couplingName][2] = continuumForce[2]; + result[couplingName][3] = continuumTorque[0]; + result[couplingName][4] = continuumTorque[1]; + result[couplingName][5] = continuumTorque[2]; + + } + + return result; } /** \brief Map a Neumann value to a Dirichlet value -* -* \param currentIterate The rod configuration that we are linearizing about -* -* \return The Dirichlet value -*/ + * + * \param currentIterate The rod configuration that we are linearizing about + * + * \return The Dirichlet value + */ template <class RodGridType, class ContinuumGridType> std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> RodContinuumSteklovPoincareStep<RodGridType,ContinuumGridType>:: linearizedRodNeumannToDirichletMap(const std::string& rodName, @@ -554,160 +554,160 @@ linearizedRodNeumannToDirichletMap(const std::string& rodName, const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>& forceTorque, const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& centerOfTorque) const { - //////////////////////////////////////////////////// - // Assemble the linearized rod problem - //////////////////////////////////////////////////// - - const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rod(rodName).dirichletBoundary_; - - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,6,6> > MatrixType; - GeodesicFEAssembler<P1NodalBasis<typename RodGridType::LeafGridView>, RigidBodyMotion<double,3> > assembler(dirichletBoundary.gridView(), - rod(rodName).localStiffness_); - MatrixType stiffnessMatrix; - assembler.assembleMatrix(currentIterate, - stiffnessMatrix, - true // assemble occupation pattern - ); - - RodCorrectionType rhs(currentIterate.size()); - rhs = 0; - assembler.assembleGradient(currentIterate, rhs); - - // The right hand side is the _negative_ gradient - rhs *= -1; - - // If we do not have a Dirichlet boundary at all we add a scaled - // identity matrix. That way the matrix gets elliptic again. - // Seems to be a common hack in this situation - for (size_t i=0; i<stiffnessMatrix.N(); i++) - for (int j=0; j<6; j++) - stiffnessMatrix[i][i][j][j] += neumannRegularization_; - - ///////////////////////////////////////////////////////////////////// - // Turn the input force and torque into a Neumann boundary field - ///////////////////////////////////////////////////////////////////// - - typedef typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>::const_iterator ForceIterator; - - for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.first != rodName) - continue; - - // Transform force from canonical coordinates to coordinates given by lambda - Dune::FieldVector<double,3> canonicalForce, canonicalTorque; - for (int i=0; i<3; i++) { - canonicalForce[i] = it->second[i]; - canonicalTorque[i] = it->second[3+i]; - } - - Dune::FieldMatrix<double,3,3> orientationMatrix; - centerOfTorque.find(couplingName)->second.q.matrix(orientationMatrix); - - /** \todo Why don't we have to transform the force as well? */ - Dune::FieldVector<double,3> localForce, localTorque; - orientationMatrix.mtv(canonicalTorque,localTorque); - localForce = canonicalForce; - - RigidBodyMotion<double,3>::TangentVector localForceTorque; - for (int i=0; i<3; i++) { - localForceTorque[i] = localForce[i]; - localForceTorque[3+i] = localTorque[i]; - } - - // Use 'forceTorque' as a Neumann value for the rod - const RodLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; - - const typename RodGridType::LeafGridView::IndexSet& indexSet = interfaceBoundary.gridView().indexSet(); - - for (typename RodLeafBoundaryPatch::iterator bIt = interfaceBoundary.begin(); - bIt != interfaceBoundary.end(); - ++bIt) { - - // vertex index corresponding to the current boundary segment - size_t idx = indexSet.subIndex(*bIt->inside(), bIt->indexInInside(), 1); - - rhs[idx] += localForceTorque; - - } + //////////////////////////////////////////////////// + // Assemble the linearized rod problem + //////////////////////////////////////////////////// + + const RodLeafBoundaryPatch& dirichletBoundary = this->complex_.rod(rodName).dirichletBoundary_; + + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,6,6> > MatrixType; + GeodesicFEAssembler<P1NodalBasis<typename RodGridType::LeafGridView>, RigidBodyMotion<double,3> > assembler(dirichletBoundary.gridView(), + rod(rodName).localStiffness_); + MatrixType stiffnessMatrix; + assembler.assembleMatrix(currentIterate, + stiffnessMatrix, + true // assemble occupation pattern + ); + + RodCorrectionType rhs(currentIterate.size()); + rhs = 0; + assembler.assembleGradient(currentIterate, rhs); + + // The right hand side is the _negative_ gradient + rhs *= -1; + + // If we do not have a Dirichlet boundary at all we add a scaled + // identity matrix. That way the matrix gets elliptic again. + // Seems to be a common hack in this situation + for (size_t i=0; i<stiffnessMatrix.N(); i++) + for (int j=0; j<6; j++) + stiffnessMatrix[i][i][j][j] += neumannRegularization_; + + ///////////////////////////////////////////////////////////////////// + // Turn the input force and torque into a Neumann boundary field + ///////////////////////////////////////////////////////////////////// + typedef typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>::const_iterator ForceIterator; + + for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.first != rodName) + continue; + + // Transform force from canonical coordinates to coordinates given by lambda + Dune::FieldVector<double,3> canonicalForce, canonicalTorque; + for (int i=0; i<3; i++) { + canonicalForce[i] = it->second[i]; + canonicalTorque[i] = it->second[3+i]; + } + + Dune::FieldMatrix<double,3,3> orientationMatrix; + centerOfTorque.find(couplingName)->second.q.matrix(orientationMatrix); + + /** \todo Why don't we have to transform the force as well? */ + Dune::FieldVector<double,3> localForce, localTorque; + orientationMatrix.mtv(canonicalTorque,localTorque); + localForce = canonicalForce; + + RigidBodyMotion<double,3>::TangentVector localForceTorque; + for (int i=0; i<3; i++) { + localForceTorque[i] = localForce[i]; + localForceTorque[3+i] = localTorque[i]; } - - /////////////////////////////////////////////////////////// - // Modify matrix and rhs to contain one Dirichlet node - /////////////////////////////////////////////////////////// - - for (size_t i=0; i<rhs.size(); i++) - if (dirichletBoundary.containsVertex(i)) { - rhs[i] = 0; - stiffnessMatrix[i] = 0; - stiffnessMatrix[i][i] = Dune::ScaledIdentityMatrix<double,6>(1); - } - - ////////////////////////////////////////////////// - // Solve the Neumann problem - ////////////////////////////////////////////////// - - RodCorrectionType x(rhs.size()); - x = 0; // initial iterate - - // Technicality: turn the matrix into a linear operator - Dune::MatrixAdapter<MatrixType,RodCorrectionType,RodCorrectionType> op(stiffnessMatrix); - - // A preconditioner - Dune::SeqILU0<MatrixType,RodCorrectionType,RodCorrectionType> ilu0(stiffnessMatrix,1.0); - - // A preconditioned conjugate-gradient solver - Dune::CGSolver<RodCorrectionType> cg(op,ilu0,1E-4,100,0); - - // Object storing some statistics about the solving process - Dune::InverseOperatorResult statistics; - - // Solve! + + // Use 'forceTorque' as a Neumann value for the rod + const RodLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; + + const typename RodGridType::LeafGridView::IndexSet& indexSet = interfaceBoundary.gridView().indexSet(); + + for (typename RodLeafBoundaryPatch::iterator bIt = interfaceBoundary.begin(); + bIt != interfaceBoundary.end(); + ++bIt) { + + // vertex index corresponding to the current boundary segment + size_t idx = indexSet.subIndex(*bIt->inside(), bIt->indexInInside(), 1); + + rhs[idx] += localForceTorque; + + } + + } + + /////////////////////////////////////////////////////////// + // Modify matrix and rhs to contain one Dirichlet node + /////////////////////////////////////////////////////////// + + for (size_t i=0; i<rhs.size(); i++) + if (dirichletBoundary.containsVertex(i)) { + rhs[i] = 0; + stiffnessMatrix[i] = 0; + stiffnessMatrix[i][i] = Dune::ScaledIdentityMatrix<double,6>(1); + } + + ////////////////////////////////////////////////// + // Solve the Neumann problem + ////////////////////////////////////////////////// + + RodCorrectionType x(rhs.size()); + x = 0; // initial iterate + + // Technicality: turn the matrix into a linear operator + Dune::MatrixAdapter<MatrixType,RodCorrectionType,RodCorrectionType> op(stiffnessMatrix); + + // A preconditioner + Dune::SeqILU0<MatrixType,RodCorrectionType,RodCorrectionType> ilu0(stiffnessMatrix,1.0); + + // A preconditioned conjugate-gradient solver + Dune::CGSolver<RodCorrectionType> cg(op,ilu0,1E-4,100,0); + + // Object storing some statistics about the solving process + Dune::InverseOperatorResult statistics; + + // Solve! #ifndef NDEBUG - RodCorrectionType residual = rhs; + RodCorrectionType residual = rhs; #endif - cg.apply(x, rhs, statistics); - + cg.apply(x, rhs, statistics); + #ifndef NDEBUG - // paranoia - stiffnessMatrix.mmv(x,residual); - assert(residual.infinity_norm() < 1e-4); + // paranoia + stiffnessMatrix.mmv(x,residual); + assert(residual.infinity_norm() < 1e-4); #endif - - /////////////////////////////////////////////////////////////////////////////////////// - // Extract the solution at the interface boundaries - /////////////////////////////////////////////////////////////////////////////////////// - std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> interfaceCorrection; - - for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.first != rodName) - continue; - - const RodLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; - - const typename RodGridType::LeafGridView::IndexSet& indexSet = interfaceBoundary.gridView().indexSet(); - - for (typename RodLeafBoundaryPatch::iterator bIt = interfaceBoundary.begin(); - bIt != interfaceBoundary.end(); - ++bIt) { - - // vertex index corresponding to the current boundary segment - size_t idx = indexSet.subIndex(*bIt->inside(), bIt->indexInInside(), 1); - - /** \todo We assume here that a coupling boundary consists of a single point only (not two) - */ - interfaceCorrection[couplingName] = x[idx]; - } + /////////////////////////////////////////////////////////////////////////////////////// + // Extract the solution at the interface boundaries + /////////////////////////////////////////////////////////////////////////////////////// + std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> interfaceCorrection; + + for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.first != rodName) + continue; + + const RodLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).rodInterfaceBoundary_; + + const typename RodGridType::LeafGridView::IndexSet& indexSet = interfaceBoundary.gridView().indexSet(); + + for (typename RodLeafBoundaryPatch::iterator bIt = interfaceBoundary.begin(); + bIt != interfaceBoundary.end(); + ++bIt) { + + // vertex index corresponding to the current boundary segment + size_t idx = indexSet.subIndex(*bIt->inside(), bIt->indexInInside(), 1); + + /** \todo We assume here that a coupling boundary consists of a single point only (not two) + */ + interfaceCorrection[couplingName] = x[idx]; } - - return interfaceCorrection; + + } + + return interfaceCorrection; } @@ -718,293 +718,293 @@ linearizedContinuumNeumannToDirichletMap(const std::string& continuumName, const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>& forceTorque, const std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >& centerOfTorque) const { - //////////////////////////////////////////////////// - // Assemble the linearized problem - //////////////////////////////////////////////////// - - /** \todo We are actually assembling standard linear elasticity, - * i.e. the linearization at zero - */ - typedef P1NodalBasis<typename ContinuumGridType::LeafGridView,double> P1Basis; - const ContinuumLeafBoundaryPatch& dirichletBoundary = this->complex_.continuum(continuumName).dirichletBoundary_; - P1Basis basis(dirichletBoundary.gridView()); - OperatorAssembler<P1Basis,P1Basis> assembler(basis, basis); - - MatrixType stiffnessMatrix; - assembler.assemble(*continuum(continuumName).localAssembler_, stiffnessMatrix); - - - ///////////////////////////////////////////////////////////////////// - // Turn the input force and torque into a Neumann boundary field - ///////////////////////////////////////////////////////////////////// - - // The weak form of the volume and Neumann data - /** \brief Not implemented yet! */ - VectorType rhs(this->complex_.continuumGrid(continuumName)->size(dim)); - rhs = 0; - - typedef typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>::const_iterator ForceIterator; - - for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.second != continuumName) - continue; - - // Use 'forceTorque' as a Neumann value for the rod - const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; - - // - VectorType localNeumannValues; - computeAveragePressure<typename ContinuumGridType::LeafGridView>(forceTorque.find(couplingName)->second, - interfaceBoundary, - centerOfTorque.find(couplingName)->second.r, - localNeumannValues); - - BoundaryFunctionalAssembler<P1Basis> boundaryFunctionalAssembler(basis, interfaceBoundary); - BasisGridFunction<P1Basis,VectorType> neumannValuesFunction(basis,localNeumannValues); - NeumannBoundaryAssembler<ContinuumGridType, Dune::FieldVector<double,3> > localNeumannAssembler(neumannValuesFunction); - boundaryFunctionalAssembler.assemble(localNeumannAssembler, rhs, false); + //////////////////////////////////////////////////// + // Assemble the linearized problem + //////////////////////////////////////////////////// - } - - // Set the correct Dirichlet nodes - /** \brief Don't write this BitSetVector at each iteration */ - Dune::BitSetVector<dim> dirichletNodes(rhs.size(),false); - for (size_t i=0; i<dirichletNodes.size(); i++) - dirichletNodes[i] = dirichletBoundary.containsVertex(i); - dynamic_cast<IterationStep<VectorType>* >(continuum(continuumName).solver_->iterationStep_)->ignoreNodes_ - = &dirichletNodes; - - // Initial iterate is 0, all Dirichlet values are 0 (because we solve a correction problem - VectorType x(dirichletNodes.size()); - x = 0; - - assert( (dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)) ); - dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)->setProblem(stiffnessMatrix, x, rhs); - - //solver.preprocess(); - - continuum(continuumName).solver_->solve(); - - x = continuum(continuumName).solver_->iterationStep_->getSol(); - - ///////////////////////////////////////////////////////////////////////////////// - // Average the continuum displacement on the coupling boundary - ///////////////////////////////////////////////////////////////////////////////// - std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> interfaceCorrection; - - for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { - - const std::pair<std::string,std::string>& couplingName = it->first; - - if (couplingName.second != continuumName) - continue; - - // Use 'forceTorque' as a Neumann value for the rod - const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; - - RigidBodyMotion<double,3> averageInterface; - computeAverageInterface(interfaceBoundary, x, averageInterface); - - // Compute the linearization - /** \todo We could loop directly over interfaceCorrection (and save the name lookup) - * if we could be sure that interfaceCorrection contains all possible entries already - */ - interfaceCorrection[couplingName][0] = averageInterface.r[0]; - interfaceCorrection[couplingName][1] = averageInterface.r[1]; - interfaceCorrection[couplingName][2] = averageInterface.r[2]; - - Dune::FieldVector<double,3> infinitesimalRotation = Rotation<double,3>::expInv(averageInterface.q); - interfaceCorrection[couplingName][3] = infinitesimalRotation[0]; - interfaceCorrection[couplingName][4] = infinitesimalRotation[1]; - interfaceCorrection[couplingName][5] = infinitesimalRotation[2]; - - } - - return interfaceCorrection; + /** \todo We are actually assembling standard linear elasticity, + * i.e. the linearization at zero + */ + typedef P1NodalBasis<typename ContinuumGridType::LeafGridView,double> P1Basis; + const ContinuumLeafBoundaryPatch& dirichletBoundary = this->complex_.continuum(continuumName).dirichletBoundary_; + P1Basis basis(dirichletBoundary.gridView()); + OperatorAssembler<P1Basis,P1Basis> assembler(basis, basis); + + MatrixType stiffnessMatrix; + assembler.assemble(*continuum(continuumName).localAssembler_, stiffnessMatrix); + + + ///////////////////////////////////////////////////////////////////// + // Turn the input force and torque into a Neumann boundary field + ///////////////////////////////////////////////////////////////////// + + // The weak form of the volume and Neumann data + /** \brief Not implemented yet! */ + VectorType rhs(this->complex_.continuumGrid(continuumName)->size(dim)); + rhs = 0; + + typedef typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector>::const_iterator ForceIterator; + + for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.second != continuumName) + continue; + + // Use 'forceTorque' as a Neumann value for the rod + const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; + + // + VectorType localNeumannValues; + computeAveragePressure<typename ContinuumGridType::LeafGridView>(forceTorque.find(couplingName)->second, + interfaceBoundary, + centerOfTorque.find(couplingName)->second.r, + localNeumannValues); + + BoundaryFunctionalAssembler<P1Basis> boundaryFunctionalAssembler(basis, interfaceBoundary); + BasisGridFunction<P1Basis,VectorType> neumannValuesFunction(basis,localNeumannValues); + NeumannBoundaryAssembler<ContinuumGridType, Dune::FieldVector<double,3> > localNeumannAssembler(neumannValuesFunction); + boundaryFunctionalAssembler.assemble(localNeumannAssembler, rhs, false); + + } + + // Set the correct Dirichlet nodes + /** \brief Don't write this BitSetVector at each iteration */ + Dune::BitSetVector<dim> dirichletNodes(rhs.size(),false); + for (size_t i=0; i<dirichletNodes.size(); i++) + dirichletNodes[i] = dirichletBoundary.containsVertex(i); + dynamic_cast<IterationStep<VectorType>* >(continuum(continuumName).solver_->iterationStep_)->ignoreNodes_ + = &dirichletNodes; + + // Initial iterate is 0, all Dirichlet values are 0 (because we solve a correction problem + VectorType x(dirichletNodes.size()); + x = 0; + + assert( (dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)) ); + dynamic_cast<LinearIterationStep<MatrixType,VectorType>* >(continuum(continuumName).solver_->iterationStep_)->setProblem(stiffnessMatrix, x, rhs); + + //solver.preprocess(); + + continuum(continuumName).solver_->solve(); + + x = continuum(continuumName).solver_->iterationStep_->getSol(); + + ///////////////////////////////////////////////////////////////////////////////// + // Average the continuum displacement on the coupling boundary + ///////////////////////////////////////////////////////////////////////////////// + std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3>::TangentVector> interfaceCorrection; + + for (ForceIterator it = forceTorque.begin(); it!=forceTorque.end(); ++it) { + + const std::pair<std::string,std::string>& couplingName = it->first; + + if (couplingName.second != continuumName) + continue; + + // Use 'forceTorque' as a Neumann value for the rod + const ContinuumLeafBoundaryPatch& interfaceBoundary = this->complex_.coupling(couplingName).continuumInterfaceBoundary_; + + RigidBodyMotion<double,3> averageInterface; + computeAverageInterface(interfaceBoundary, x, averageInterface); + + // Compute the linearization + /** \todo We could loop directly over interfaceCorrection (and save the name lookup) + * if we could be sure that interfaceCorrection contains all possible entries already + */ + interfaceCorrection[couplingName][0] = averageInterface.r[0]; + interfaceCorrection[couplingName][1] = averageInterface.r[1]; + interfaceCorrection[couplingName][2] = averageInterface.r[2]; + + Dune::FieldVector<double,3> infinitesimalRotation = Rotation<double,3>::expInv(averageInterface.q); + interfaceCorrection[couplingName][3] = infinitesimalRotation[0]; + interfaceCorrection[couplingName][4] = infinitesimalRotation[1]; + interfaceCorrection[couplingName][5] = infinitesimalRotation[2]; + + } + + return interfaceCorrection; } /** \brief One preconditioned Richardson step -*/ + */ template <class RodGridType, class ContinuumGridType> void RodContinuumSteklovPoincareStep<RodGridType,ContinuumGridType>:: iterate(std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3> >& lambda) { - /////////////////////////////////////////////////////////////////// - // Evaluate the Dirichlet-to-Neumann maps for the rods - /////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////// + // Evaluate the Dirichlet-to-Neumann maps for the rods + /////////////////////////////////////////////////////////////////// - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> rodForceTorque; - - for (RodIterator it = rods_.begin(); it != rods_.end(); ++it) { - - const std::string& rodName = it->first; - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> forceTorque = rodDirichletToNeumannMap(rodName, lambda); + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> rodForceTorque; - this->insert(rodForceTorque, forceTorque); - - } + for (RodIterator it = rods_.begin(); it != rods_.end(); ++it) { + + const std::string& rodName = it->first; + + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> forceTorque = rodDirichletToNeumannMap(rodName, lambda); + + this->insert(rodForceTorque, forceTorque); + + } + + std::cout << "resultant rod forces and torques: " << std::endl; + typedef typename std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector>::iterator ForceIterator; + for (ForceIterator it = rodForceTorque.begin(); it != rodForceTorque.end(); ++it) + std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " + << it->second << std::endl; + + /////////////////////////////////////////////////////////////////// + // Evaluate the Dirichlet-to-Neumann map for the continuum + /////////////////////////////////////////////////////////////////// - std::cout << "resultant rod forces and torques: " << std::endl; - typedef typename std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector>::iterator ForceIterator; - for (ForceIterator it = rodForceTorque.begin(); it != rodForceTorque.end(); ++it) - std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " - << it->second << std::endl; + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> continuumForceTorque; - /////////////////////////////////////////////////////////////////// - // Evaluate the Dirichlet-to-Neumann map for the continuum - /////////////////////////////////////////////////////////////////// - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> continuumForceTorque; + for (ContinuumIterator it = continua_.begin(); it != continua_.end(); ++it) { + + const std::string& continuumName = it->first; + + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> forceTorque + = continuumDirichletToNeumannMap(continuumName, lambda); + + this->insert(continuumForceTorque, forceTorque); + + } + + std::cout << "resultant continuum force and torque: " << std::endl; + for (ForceIterator it = continuumForceTorque.begin(); it != continuumForceTorque.end(); ++it) + std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " + << it->second << std::endl; + + /////////////////////////////////////////////////////////////// + // Compute the overall Steklov-Poincare residual + /////////////////////////////////////////////////////////////// + + // Flip orientation of all rod forces, to account for opposing normals. + for (ForceIterator it = rodForceTorque.begin(); it != rodForceTorque.end(); ++it) + it->second *= -1; + + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> residualForceTorque = rodForceTorque; + + for (ForceIterator it = residualForceTorque.begin(), it2 = continuumForceTorque.begin(); + it != residualForceTorque.end(); + ++it, ++it2) { + assert(it->first == it2->first); + it->second += it2->second; + } + + /////////////////////////////////////////////////////////////// + // Apply the preconditioner + /////////////////////////////////////////////////////////////// + + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> continuumCorrection; + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> rodCorrection; + + if (preconditioner_=="DirichletNeumann" or preconditioner_=="NeumannNeumann") { + + //////////////////////////////////////////////////////////////////// + // Preconditioner is the linearized Neumann-to-Dirichlet map + // of the continuum. + //////////////////////////////////////////////////////////////////// for (ContinuumIterator it = continua_.begin(); it != continua_.end(); ++it) { - - const std::string& continuumName = it->first; - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> forceTorque - = continuumDirichletToNeumannMap(continuumName, lambda); - - this->insert(continuumForceTorque, forceTorque); - + + const std::string& continuumName = it->first; + + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> continuumInterfaceCorrection + = linearizedContinuumNeumannToDirichletMap(continuumName, + this->continuumSubdomainSolutions_[continuumName], + residualForceTorque, + lambda); + + this->insert(continuumCorrection, continuumInterfaceCorrection); + } - std::cout << "resultant continuum force and torque: " << std::endl; - for (ForceIterator it = continuumForceTorque.begin(); it != continuumForceTorque.end(); ++it) - std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " - << it->second << std::endl; - - /////////////////////////////////////////////////////////////// - // Compute the overall Steklov-Poincare residual - /////////////////////////////////////////////////////////////// - - // Flip orientation of all rod forces, to account for opposing normals. - for (ForceIterator it = rodForceTorque.begin(); it != rodForceTorque.end(); ++it) - it->second *= -1; - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> residualForceTorque = rodForceTorque; - - for (ForceIterator it = residualForceTorque.begin(), it2 = continuumForceTorque.begin(); - it != residualForceTorque.end(); - ++it, ++it2) { - assert(it->first == it2->first); - it->second += it2->second; + std::cout << "resultant continuum interface corrections: " << std::endl; + for (ForceIterator it = continuumCorrection.begin(); it != continuumCorrection.end(); ++it) + std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " + << it->second << std::endl; + + + } + + if (preconditioner_=="NeumannDirichlet" or preconditioner_=="NeumannNeumann") { + + //////////////////////////////////////////////////////////////////// + // Preconditioner is the linearized Neumann-to-Dirichlet map + // of the rod. + //////////////////////////////////////////////////////////////////// + + for (RodIterator it = rods_.begin(); it != rods_.end(); ++it) { + + const std::string& rodName = it->first; + + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> rodInterfaceCorrection + = linearizedRodNeumannToDirichletMap(rodName, + this->rodSubdomainSolutions_[rodName], + residualForceTorque, + lambda); + + this->insert(rodCorrection, rodInterfaceCorrection); + } - - /////////////////////////////////////////////////////////////// - // Apply the preconditioner - /////////////////////////////////////////////////////////////// - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> continuumCorrection; - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> rodCorrection; - - if (preconditioner_=="DirichletNeumann" or preconditioner_=="NeumannNeumann") { - - //////////////////////////////////////////////////////////////////// - // Preconditioner is the linearized Neumann-to-Dirichlet map - // of the continuum. - //////////////////////////////////////////////////////////////////// - - for (ContinuumIterator it = continua_.begin(); it != continua_.end(); ++it) { - - const std::string& continuumName = it->first; - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> continuumInterfaceCorrection - = linearizedContinuumNeumannToDirichletMap(continuumName, - this->continuumSubdomainSolutions_[continuumName], - residualForceTorque, - lambda); - - this->insert(continuumCorrection, continuumInterfaceCorrection); - - } - - std::cout << "resultant continuum interface corrections: " << std::endl; - for (ForceIterator it = continuumCorrection.begin(); it != continuumCorrection.end(); ++it) - std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " - << it->second << std::endl; - - - } - - if (preconditioner_=="NeumannDirichlet" or preconditioner_=="NeumannNeumann") { - - //////////////////////////////////////////////////////////////////// - // Preconditioner is the linearized Neumann-to-Dirichlet map - // of the rod. - //////////////////////////////////////////////////////////////////// - - for (RodIterator it = rods_.begin(); it != rods_.end(); ++it) { - - const std::string& rodName = it->first; - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> rodInterfaceCorrection - = linearizedRodNeumannToDirichletMap(rodName, - this->rodSubdomainSolutions_[rodName], - residualForceTorque, - lambda); - - this->insert(rodCorrection, rodInterfaceCorrection); - - } - - std::cout << "resultant rod interface corrections: " << std::endl; - for (ForceIterator it = rodCorrection.begin(); it != rodCorrection.end(); ++it) - std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " - << it->second << std::endl; - - } - - std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> interfaceCorrection; - - if (preconditioner_=="DirichletNeumann") { - - interfaceCorrection = continuumCorrection; - - } else if (preconditioner_=="NeumannDirichlet") { - - interfaceCorrection = rodCorrection; - - } else if (preconditioner_=="NeumannNeumann") { - - std::cout << "resultant interface corrections: " << std::endl; - for (ForceIterator it = rodCorrection.begin(); it != rodCorrection.end(); ++it) { - - const std::pair<std::string,std::string> interfaceName = it->first; - - std::cout << " [" << interfaceName.first << ", " << interfaceName.second << "]" - << " -- " << it->second - << " -- " << continuumCorrection[interfaceName] << std::endl; - - // Compute weighted combination of both - RigidBodyMotion<double,3>::TangentVector& correction = interfaceCorrection[interfaceName]; - for (int j=0; j<6; j++) - correction[j] = (alpha_[0] * continuumCorrection[interfaceName][j] + alpha_[1] * rodCorrection[interfaceName][j]) - / (alpha_[0] + alpha_[1]); - - } - - } else if (preconditioner_=="RobinRobin") { - - DUNE_THROW(Dune::NotImplemented, "Robin-Robin preconditioner not implemented yet"); - - } else - DUNE_THROW(Dune::NotImplemented, preconditioner_ << " is not a known preconditioner"); - - /////////////////////////////////////////////////////////////////////////////// - // Apply the damped correction to the current interface value - /////////////////////////////////////////////////////////////////////////////// - - typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::iterator it = lambda.begin(); - ForceIterator fIt = interfaceCorrection.begin(); - for (; it!=lambda.end(); ++it, ++fIt) { - assert(it->first == fIt->first); - fIt->second *= richardsonDamping_; - it->second = RigidBodyMotion<double,3>::exp(it->second, fIt->second); + + std::cout << "resultant rod interface corrections: " << std::endl; + for (ForceIterator it = rodCorrection.begin(); it != rodCorrection.end(); ++it) + std::cout << " [" << it->first.first << ", " << it->first.second << "] -- " + << it->second << std::endl; + + } + + std::map<std::pair<std::string,std::string>, RigidBodyMotion<double,3>::TangentVector> interfaceCorrection; + + if (preconditioner_=="DirichletNeumann") { + + interfaceCorrection = continuumCorrection; + + } else if (preconditioner_=="NeumannDirichlet") { + + interfaceCorrection = rodCorrection; + + } else if (preconditioner_=="NeumannNeumann") { + + std::cout << "resultant interface corrections: " << std::endl; + for (ForceIterator it = rodCorrection.begin(); it != rodCorrection.end(); ++it) { + + const std::pair<std::string,std::string> interfaceName = it->first; + + std::cout << " [" << interfaceName.first << ", " << interfaceName.second << "]" + << " -- " << it->second + << " -- " << continuumCorrection[interfaceName] << std::endl; + + // Compute weighted combination of both + RigidBodyMotion<double,3>::TangentVector& correction = interfaceCorrection[interfaceName]; + for (int j=0; j<6; j++) + correction[j] = (alpha_[0] * continuumCorrection[interfaceName][j] + alpha_[1] * rodCorrection[interfaceName][j]) + / (alpha_[0] + alpha_[1]); + } + + } else if (preconditioner_=="RobinRobin") { + + DUNE_THROW(Dune::NotImplemented, "Robin-Robin preconditioner not implemented yet"); + + } else + DUNE_THROW(Dune::NotImplemented, preconditioner_ << " is not a known preconditioner"); + + /////////////////////////////////////////////////////////////////////////////// + // Apply the damped correction to the current interface value + /////////////////////////////////////////////////////////////////////////////// + + typename std::map<std::pair<std::string,std::string>,RigidBodyMotion<double,3> >::iterator it = lambda.begin(); + ForceIterator fIt = interfaceCorrection.begin(); + for (; it!=lambda.end(); ++it, ++fIt) { + assert(it->first == fIt->first); + fIt->second *= richardsonDamping_; + it->second = RigidBodyMotion<double,3>::exp(it->second, fIt->second); + } } #endif diff --git a/dune/gfe/densities/bulkcosseratdensity.hh b/dune/gfe/densities/bulkcosseratdensity.hh index faa1fb80..9f4cddbd 100644 --- a/dune/gfe/densities/bulkcosseratdensity.hh +++ b/dune/gfe/densities/bulkcosseratdensity.hh @@ -12,85 +12,85 @@ namespace Dune::GFE { -template<class field_type = double, class ctype = double> -class BulkCosseratDensity final + template<class field_type = double, class ctype = double> + class BulkCosseratDensity final : public GFE::LocalDensity<3,field_type,ctype> -{ -private: - // gridDim = 3 - this can be hardwired here, because BulkCosseratDensity only works for 3d->3d problems - static const int gridDim = 3; - static const int embeddedDim = Rotation<field_type,gridDim>::embeddedDim; - using OrientationDerivativeType = FieldMatrix<field_type, embeddedDim, gridDim>; - - static FieldMatrix<field_type,gridDim,gridDim> curl(const Tensor3<field_type,gridDim,gridDim,gridDim>& DR) { + private: + // gridDim = 3 - this can be hardwired here, because BulkCosseratDensity only works for 3d->3d problems + static const int gridDim = 3; + static const int embeddedDim = Rotation<field_type,gridDim>::embeddedDim; + using OrientationDerivativeType = FieldMatrix<field_type, embeddedDim, gridDim>; + + static FieldMatrix<field_type,gridDim,gridDim> curl(const Tensor3<field_type,gridDim,gridDim,gridDim>& DR) + { FieldMatrix<field_type,gridDim,gridDim> result; for (int i=0; i<gridDim; i++) { - result[i][0] = DR[i][2][1] - DR[i][1][2]; - result[i][1] = DR[i][0][2] - DR[i][2][0]; - result[i][2] = DR[i][1][0] - DR[i][0][1]; + result[i][0] = DR[i][2][1] - DR[i][1][2]; + result[i][1] = DR[i][0][2] - DR[i][2][0]; + result[i][2] = DR[i][1][0] - DR[i][0][1]; } return result; - } - -public: - - /** \brief Constructor with a set of material parameters - * \param parameters The material parameters - */ - BulkCosseratDensity(const ParameterTree& parameters) - { - mu_ = parameters.template get<double>("mu"); - lambda_ = parameters.template get<double>("lambda"); - - // Cosserat couple modulus - mu_c_ = parameters.template get<double>("mu_c"); - - // Length scale parameter - L_c_ = parameters.template get<double>("L_c"); - - // Curvature exponent - q_ = parameters.template get<double>("q"); - - // Curvature parameters - b1_ = parameters.template get<double>("b1"); - b2_ = parameters.template get<double>("b2"); - b3_ = parameters.template get<double>("b3"); - } - - /** \brief The energy \f$ W_{mp}(\overline{U}) \f$, as written in - * the first equation of (4.4) in Neff's paper from 2006: A geometrically exact planar Cosserat shell model with microstructure: Existence of minimizers for zero Cosserat couple modulus - * OR: the equation (2.27) of 2019: Refined dimensional reduction for isotropic elastic Cosserat shells with initial curvature - */ - field_type quadraticEnergy(const GFE::CosseratStrain<field_type,3,gridDim>& U) const - { + } + + public: + + /** \brief Constructor with a set of material parameters + * \param parameters The material parameters + */ + BulkCosseratDensity(const ParameterTree& parameters) + { + mu_ = parameters.template get<double>("mu"); + lambda_ = parameters.template get<double>("lambda"); + + // Cosserat couple modulus + mu_c_ = parameters.template get<double>("mu_c"); + + // Length scale parameter + L_c_ = parameters.template get<double>("L_c"); + + // Curvature exponent + q_ = parameters.template get<double>("q"); + + // Curvature parameters + b1_ = parameters.template get<double>("b1"); + b2_ = parameters.template get<double>("b2"); + b3_ = parameters.template get<double>("b3"); + } + + /** \brief The energy \f$ W_{mp}(\overline{U}) \f$, as written in + * the first equation of (4.4) in Neff's paper from 2006: A geometrically exact planar Cosserat shell model with microstructure: Existence of minimizers for zero Cosserat couple modulus + * OR: the equation (2.27) of 2019: Refined dimensional reduction for isotropic elastic Cosserat shells with initial curvature + */ + field_type quadraticEnergy(const GFE::CosseratStrain<field_type,3,gridDim>& U) const + { FieldMatrix<field_type,3,3> UMinus1 = U.matrix(); for (int i=0; i<gridDim; i++) - UMinus1[i][i] -= 1; + UMinus1[i][i] -= 1; return mu_ * GFE::sym(UMinus1).frobenius_norm2() - + mu_c_ * GFE::skew(UMinus1).frobenius_norm2() + + mu_c_ * GFE::skew(UMinus1).frobenius_norm2() #ifdef QUADRATIC_2006 - + (mu_*lambda_)/(2*mu_ + lambda_) * GFE::traceSquared(UMinus1); // GFE::traceSquared(UMinus1) = GFE::traceSquared(GFE::sym(UMinus1)) + + (mu_*lambda_)/(2*mu_ + lambda_) * GFE::traceSquared(UMinus1); // GFE::traceSquared(UMinus1) = GFE::traceSquared(GFE::sym(UMinus1)) #else - + lambda_/2 * GFE::traceSquared(UMinus1); // GFE::traceSquared(UMinus1) = GFE::traceSquared(GFE::sym(UMinus1)) + + lambda_/2 * GFE::traceSquared(UMinus1); // GFE::traceSquared(UMinus1) = GFE::traceSquared(GFE::sym(UMinus1)) #endif - } + } - field_type curvatureWithWryness(const FieldMatrix<field_type,gridDim,gridDim>& R, const Tensor3<field_type,3,3,3>& DR) const - { + field_type curvatureWithWryness(const FieldMatrix<field_type,gridDim,gridDim>& R, const Tensor3<field_type,3,3,3>& DR) const + { // construct Wryness tensor \Gamma as in "Refined dimensional reduction for isotropic elastic Cosserat shells with initial curvature" FieldMatrix<field_type,3,3> dRx1(0); //Derivative of R with respect to x1 FieldMatrix<field_type,3,3> dRx2(0); //Derivative of R with respect to x2 FieldMatrix<field_type,3,3> dRx3(0); //Derivative of R with respect to x3 for (int i=0; i<3; i++) - for (int j=0; j<3; j++) { - dRx1[i][j] = DR[i][j][0]; - dRx2[i][j] = DR[i][j][1]; - dRx3[i][j] = DR[i][j][2]; - } + for (int j=0; j<3; j++) { + dRx1[i][j] = DR[i][j][0]; + dRx2[i][j] = DR[i][j][1]; + dRx3[i][j] = DR[i][j][2]; + } FieldMatrix<field_type,3,3> wryness(0); @@ -98,82 +98,82 @@ public: auto axialVectorx2 = SkewMatrix<field_type,3>(transpose(R)*dRx2).axial(); auto axialVectorx3 = SkewMatrix<field_type,3>(transpose(R)*dRx3).axial(); for (int i=0; i<3; i++) { - wryness[i][0] = axialVectorx1[i]; - wryness[i][1] = axialVectorx2[i]; - wryness[i][2] = axialVectorx3[i]; + wryness[i][0] = axialVectorx1[i]; + wryness[i][1] = axialVectorx2[i]; + wryness[i][2] = axialVectorx3[i]; } // The choice for the Frobenius norm here is b1=b2=1 and b3 = 1/3 return mu_ * L_c_ * L_c_ * (b1_ * GFE::dev(GFE::sym(wryness)).frobenius_norm2() - + b2_ * GFE::skew(wryness).frobenius_norm2() + b3_ * GFE::traceSquared(wryness)); - } - - /** \brief Evaluation with the current position, the deformation value and its derivative, the orientation value and its derivative - * - * \param x The current position - * \param deformationValue The deformation at the current position - * \param deformationDerivative The derivative of the deformation at the current position - * \param orientationValue The orientation at the current position - * \param orientationDerivative The derivative of the orientation at the current position - */ - field_type operator() (const FieldVector<ctype,gridDim>& x, - const RealTuple<field_type,gridDim>& deformationValue, - const FieldMatrix<field_type,gridDim,gridDim>& deformationDerivative, - const Rotation<field_type,gridDim>& orientationValue, - const OrientationDerivativeType& orientationDerivative) const - { - field_type strainEnergyDensity = 0; - ///////////////////////////////////////////////////////// - // compute U, the Cosserat strain - ///////////////////////////////////////////////////////// - - FieldMatrix<field_type,gridDim,gridDim> R; - orientationValue.matrix(R); - - GFE::CosseratStrain<field_type,gridDim,gridDim> U(deformationDerivative,R); - - ///////////////////////////////////////////////////////////////////// - // Transfer the derivative of the rotation into matrix coordinates - //////////////////////////////////////////////////////////////////// - - Tensor3<field_type,3,3,gridDim> DR = orientationValue.quaternionTangentToMatrixTangent(orientationDerivative); - strainEnergyDensity = quadraticEnergy(U); + + b2_ * GFE::skew(wryness).frobenius_norm2() + b3_ * GFE::traceSquared(wryness)); + } + + /** \brief Evaluation with the current position, the deformation value and its derivative, the orientation value and its derivative + * + * \param x The current position + * \param deformationValue The deformation at the current position + * \param deformationDerivative The derivative of the deformation at the current position + * \param orientationValue The orientation at the current position + * \param orientationDerivative The derivative of the orientation at the current position + */ + field_type operator() (const FieldVector<ctype,gridDim>& x, + const RealTuple<field_type,gridDim>& deformationValue, + const FieldMatrix<field_type,gridDim,gridDim>& deformationDerivative, + const Rotation<field_type,gridDim>& orientationValue, + const OrientationDerivativeType& orientationDerivative) const + { + field_type strainEnergyDensity = 0; + ///////////////////////////////////////////////////////// + // compute U, the Cosserat strain + ///////////////////////////////////////////////////////// + + FieldMatrix<field_type,gridDim,gridDim> R; + orientationValue.matrix(R); + + GFE::CosseratStrain<field_type,gridDim,gridDim> U(deformationDerivative,R); + + ///////////////////////////////////////////////////////////////////// + // Transfer the derivative of the rotation into matrix coordinates + //////////////////////////////////////////////////////////////////// + + Tensor3<field_type,3,3,gridDim> DR = orientationValue.quaternionTangentToMatrixTangent(orientationDerivative); + strainEnergyDensity = quadraticEnergy(U); #ifdef CURVATURE_WITH_WRYNESS - strainEnergyDensity += curvatureWithWryness(R,DR); + strainEnergyDensity += curvatureWithWryness(R,DR); #else - + #ifdef DONT_USE_CURL - auto argument = DR; + auto argument = DR; #else - auto argument = curl(DR); + auto argument = curl(DR); #endif - auto norm = (b1_ * GFE::dev(GFE::sym(argument)).frobenius_norm2() - + b2_ * GFE::skew(argument).frobenius_norm2() + b3_ * GFE::traceSquared(argument)); + auto norm = (b1_ * GFE::dev(GFE::sym(argument)).frobenius_norm2() + + b2_ * GFE::skew(argument).frobenius_norm2() + b3_ * GFE::traceSquared(argument)); - strainEnergyDensity += mu_ * std::pow(L_c_ * L_c_ * norm,q_/2.0); + strainEnergyDensity += mu_ * std::pow(L_c_ * L_c_ * norm,q_/2.0); #endif - return strainEnergyDensity; - } + return strainEnergyDensity; + } - /** \brief Lame constants */ - double mu_, lambda_; + /** \brief Lame constants */ + double mu_, lambda_; - /** \brief Cosserat couple modulus, preferably 0 */ - double mu_c_; + /** \brief Cosserat couple modulus, preferably 0 */ + double mu_c_; - /** \brief Length scale parameter */ - double L_c_; + /** \brief Length scale parameter */ + double L_c_; - /** \brief Curvature exponent */ - double q_; + /** \brief Curvature exponent */ + double q_; - /** \brief Curvature parameters */ - double b1_, b2_, b3_; + /** \brief Curvature parameters */ + double b1_, b2_, b3_; -}; + }; } // namespace GFE diff --git a/dune/gfe/densities/localdensity.hh b/dune/gfe/densities/localdensity.hh index c1c11262..8f29d58d 100644 --- a/dune/gfe/densities/localdensity.hh +++ b/dune/gfe/densities/localdensity.hh @@ -7,33 +7,33 @@ namespace Dune::GFE { -/** \brief A base class for energy densities to be evaluated in an integral energy - * - * \tparam field_type type of the gradient entries - * \tparam ctype type of the coordinates - */ -template<int dim, class field_type = double, class ctype = double> -class LocalDensity -{ -private: - static const int embeddedDim = Rotation<field_type,dim>::embeddedDim; -public: - - /** \brief Evaluation with the current position, the deformation function, the deformation gradient, the rotation and the rotation gradient + /** \brief A base class for energy densities to be evaluated in an integral energy * - * \param x The current position - * \param deformationValue The deformation at the current position - * \param deformationDerivative The derivative of the deformation at the current position - * \param orientationValue The orientation at the current position - * \param orientationDerivative The derivative of the orientation at the current position + * \tparam field_type type of the gradient entries + * \tparam ctype type of the coordinates */ - virtual field_type operator() (const FieldVector<ctype,dim>& x, - const RealTuple<field_type,dim>& deformation, - const FieldMatrix<field_type,dim,dim>& gradient, - const Rotation<field_type,dim>& rotation, - const FieldMatrix<field_type, embeddedDim, dim>& rotationGradient) const = 0; + template<int dim, class field_type = double, class ctype = double> + class LocalDensity + { + private: + static const int embeddedDim = Rotation<field_type,dim>::embeddedDim; + public: + + /** \brief Evaluation with the current position, the deformation function, the deformation gradient, the rotation and the rotation gradient + * + * \param x The current position + * \param deformationValue The deformation at the current position + * \param deformationDerivative The derivative of the deformation at the current position + * \param orientationValue The orientation at the current position + * \param orientationDerivative The derivative of the orientation at the current position + */ + virtual field_type operator() (const FieldVector<ctype,dim>& x, + const RealTuple<field_type,dim>& deformation, + const FieldMatrix<field_type,dim,dim>& gradient, + const Rotation<field_type,dim>& rotation, + const FieldMatrix<field_type, embeddedDim, dim>& rotationGradient) const = 0; -}; + }; } // namespace Dune::GFE diff --git a/dune/gfe/embeddedglobalgfefunction.hh b/dune/gfe/embeddedglobalgfefunction.hh index afeede5b..fabc5f2d 100644 --- a/dune/gfe/embeddedglobalgfefunction.hh +++ b/dune/gfe/embeddedglobalgfefunction.hh @@ -20,369 +20,369 @@ namespace Dune::GFE { -template<typename EGGF> -class EmbeddedGlobalGFEFunctionDerivative; - -/** - * \brief A geometric finite element function with an embedding into Euclidean space - * - * The `GlobalGFEFunction` implements a geometric finite element function. - * The values of that function implement the `TargetSpace` model. - * In contrast, the values of the `EmbeddedGlobalGFEFunction` implemented here - * are the corresponding values in Euclidean space. The precise type is - * `TargetSpace::CoordinateType`, which is typically a vector or matrix type. - * - * \tparam B Type of global scalar(!) basis - * \tparam LIR Local interpolation rule for manifold-valued data - * \tparam TargetSpace Range type of this function - */ -template<typename B, typename LIR, typename TargetSpace> -class EmbeddedGlobalGFEFunction -// There is no separate base class for EmbeddedGlobalGFEFunction, because the base class -// only handles coefficients and indices. It is independent of the type of function values. + template<typename EGGF> + class EmbeddedGlobalGFEFunctionDerivative; + + /** + * \brief A geometric finite element function with an embedding into Euclidean space + * + * The `GlobalGFEFunction` implements a geometric finite element function. + * The values of that function implement the `TargetSpace` model. + * In contrast, the values of the `EmbeddedGlobalGFEFunction` implemented here + * are the corresponding values in Euclidean space. The precise type is + * `TargetSpace::CoordinateType`, which is typically a vector or matrix type. + * + * \tparam B Type of global scalar(!) basis + * \tparam LIR Local interpolation rule for manifold-valued data + * \tparam TargetSpace Range type of this function + */ + template<typename B, typename LIR, typename TargetSpace> + class EmbeddedGlobalGFEFunction + // There is no separate base class for EmbeddedGlobalGFEFunction, because the base class + // only handles coefficients and indices. It is independent of the type of function values. #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - : public Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR, typename TargetSpace::CoordinateType> + : public Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR, typename TargetSpace::CoordinateType> #else - : public Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR> + : public Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR> #endif -{ + { #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Base = Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR, typename TargetSpace::CoordinateType>; + using Base = Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR, typename TargetSpace::CoordinateType>; #else - using Base = Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR>; - using Data = typename Base::Data; + using Base = Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR>; + using Data = typename Base::Data; #endif -public: - using Basis = typename Base::Basis; - using Vector = typename Base::Vector; + public: + using Basis = typename Base::Basis; + using Vector = typename Base::Vector; #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Data = typename Impl::Data<Basis,Vector>; + using Data = typename Impl::Data<Basis,Vector>; #endif - using LocalInterpolationRule = LIR; + using LocalInterpolationRule = LIR; - using Domain = typename Base::Domain; - using Range = typename TargetSpace::CoordinateType; + using Domain = typename Base::Domain; + using Range = typename TargetSpace::CoordinateType; - using Traits = Functions::Imp::GridFunctionTraits<Range(Domain), typename Base::EntitySet, Functions::DefaultDerivativeTraits, 16>; + using Traits = Functions::Imp::GridFunctionTraits<Range(Domain), typename Base::EntitySet, Functions::DefaultDerivativeTraits, 16>; - class LocalFunction - : public Base::LocalFunctionBase - { - using LocalBase = typename Base::LocalFunctionBase; - using size_type = typename Base::Tree::size_type; - - public: + class LocalFunction + : public Base::LocalFunctionBase + { + using LocalBase = typename Base::LocalFunctionBase; + using size_type = typename Base::Tree::size_type; + + public: + + using GlobalFunction = EmbeddedGlobalGFEFunction; + using Domain = typename LocalBase::Domain; + using Range = GlobalFunction::Range; + using Element = typename LocalBase::Element; + + //! Create a local-function from the associated grid-function + LocalFunction(const EmbeddedGlobalGFEFunction& globalFunction) + : LocalBase(globalFunction.data_) + { + /* Nothing. */ + } + + /** + * \brief Evaluate this local-function in coordinates `x` in the bound element. + * + * The result of this method is undefined if you did + * not call bind() beforehand or changed the coefficient + * vector after the last call to bind(). In the latter case + * you have to call bind() again in order to make operator() + * usable. + */ + Range operator()(const Domain& x) const + { + return this->localInterpolationRule_->evaluate(x).globalCoordinates(); + } + + //! Local function of the derivative + friend typename EmbeddedGlobalGFEFunctionDerivative<EmbeddedGlobalGFEFunction>::LocalFunction derivative(const LocalFunction& lf) + { + auto dlf = localFunction(EmbeddedGlobalGFEFunctionDerivative<EmbeddedGlobalGFEFunction>(lf.data_)); + if (lf.bound()) + dlf.bind(lf.localContext()); + return dlf; + } + }; + + //! Create a grid-function, by wrapping the arguments in `std::shared_ptr`. + template<class B_T, class V_T> + EmbeddedGlobalGFEFunction(B_T && basis, V_T && coefficients) + : Base(std::make_shared<Data>(Data{{basis.gridView()}, wrap_or_move(std::forward<B_T>(basis)), wrap_or_move(std::forward<V_T>(coefficients))})) + {} + + //! Create a grid-function, by moving the arguments in `std::shared_ptr`. + EmbeddedGlobalGFEFunction(std::shared_ptr<const Basis> basis, std::shared_ptr<const Vector> coefficients) + : Base(std::make_shared<Data>(Data{{basis->gridView()}, basis, coefficients})) + {} + + /** \brief Evaluate at a point given in world coordinates + * + * \warning This has to find the element that the evaluation point is in. + * It is therefore very slow. + */ + Range operator() (const Domain& x) const + { + HierarchicSearch search(this->data_->basis->gridView().grid(), this->data_->basis->gridView().indexSet()); - using GlobalFunction = EmbeddedGlobalGFEFunction; - using Domain = typename LocalBase::Domain; - using Range = GlobalFunction::Range; - using Element = typename LocalBase::Element; + const auto e = search.findEntity(x); + auto localThis = localFunction(*this); + localThis.bind(e); + return localThis(e.geometry().local(x)); + } - //! Create a local-function from the associated grid-function - LocalFunction(const EmbeddedGlobalGFEFunction& globalFunction) - : LocalBase(globalFunction.data_) + //! Derivative of the `EmbeddedGlobalGFEFunction` + friend EmbeddedGlobalGFEFunctionDerivative<EmbeddedGlobalGFEFunction> derivative(const EmbeddedGlobalGFEFunction& f) { - /* Nothing. */ + return EmbeddedGlobalGFEFunctionDerivative<EmbeddedGlobalGFEFunction>(f.data_); } /** - * \brief Evaluate this local-function in coordinates `x` in the bound element. + * \brief Construct local function from a EmbeddedGlobalGFEFunction. * - * The result of this method is undefined if you did - * not call bind() beforehand or changed the coefficient - * vector after the last call to bind(). In the latter case - * you have to call bind() again in order to make operator() - * usable. + * The obtained local function satisfies the concept + * `Dune::Functions::Concept::LocalFunction`. It must be bound + * to an entity from the entity set of the EmbeddedGlobalGFEFunction + * before it can be used. */ - Range operator()(const Domain& x) const + friend LocalFunction localFunction(const EmbeddedGlobalGFEFunction& t) { - return this->localInterpolationRule_->evaluate(x).globalCoordinates(); + return LocalFunction(t); } - //! Local function of the derivative - friend typename EmbeddedGlobalGFEFunctionDerivative<EmbeddedGlobalGFEFunction>::LocalFunction derivative(const LocalFunction& lf) +#if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) + using Element = typename Basis::GridView::template Codim<0>::Entity; + /** \brief Evaluate the function at local coordinates. */ + void evaluateLocal(const Element& element, const Domain& local, typename TargetSpace::CoordinateType& out) const override { - auto dlf = localFunction(EmbeddedGlobalGFEFunctionDerivative<EmbeddedGlobalGFEFunction>(lf.data_)); - if (lf.bound()) - dlf.bind(lf.localContext()); - return dlf; + out = this->operator()(element,local); } - }; - //! Create a grid-function, by wrapping the arguments in `std::shared_ptr`. - template<class B_T, class V_T> - EmbeddedGlobalGFEFunction(B_T && basis, V_T && coefficients) - : Base(std::make_shared<Data>(Data{{basis.gridView()}, wrap_or_move(std::forward<B_T>(basis)), wrap_or_move(std::forward<V_T>(coefficients))})) - {} + /** \brief Evaluate the function at local coordinates. */ + typename TargetSpace::CoordinateType operator()(const Element& element, const Domain& local) const + { + auto localView = this->basis().localView(); + localView.bind(element); + auto numOfBaseFct = localView.size(); - //! Create a grid-function, by moving the arguments in `std::shared_ptr`. - EmbeddedGlobalGFEFunction(std::shared_ptr<const Basis> basis, std::shared_ptr<const Vector> coefficients) - : Base(std::make_shared<Data>(Data{{basis->gridView()}, basis, coefficients})) - {} + // Extract local coefficients + std::vector<TargetSpace> localCoeff(numOfBaseFct); - /** \brief Evaluate at a point given in world coordinates - * - * \warning This has to find the element that the evaluation point is in. - * It is therefore very slow. - */ - Range operator() (const Domain& x) const - { - HierarchicSearch search(this->data_->basis->gridView().grid(), this->data_->basis->gridView().indexSet()); + for (size_t i=0; i<numOfBaseFct; i++) + localCoeff[i] = this->dofs()[localView.index(i)]; - const auto e = search.findEntity(x); - auto localThis = localFunction(*this); - localThis.bind(e); - return localThis(e.geometry().local(x)); - } + // create local gfe function + LocalInterpolationRule localInterpolationRule(localView.tree().finiteElement(),localCoeff); + return localInterpolationRule.evaluate(local).globalCoordinates(); + } - //! Derivative of the `EmbeddedGlobalGFEFunction` - friend EmbeddedGlobalGFEFunctionDerivative<EmbeddedGlobalGFEFunction> derivative(const EmbeddedGlobalGFEFunction& f) - { - return EmbeddedGlobalGFEFunctionDerivative<EmbeddedGlobalGFEFunction>(f.data_); - } + /** \brief evaluation of derivative in local coordinates + * + * \param e Evaluate in local coordinates with respect to this element. + * \param x point in local coordinates at which to evaluate the derivative + * \param d will contain the derivative at x after return + */ + void evaluateDerivativeLocal(const Element& element, const Domain& local, + typename Functions::SignatureTraits<typename EmbeddedGlobalGFEFunction::Traits::DerivativeInterface>::Range& out) const override + { + auto localView = this->basis().localView(); + localView.bind(element); + auto numOfBaseFct = localView.size(); - /** - * \brief Construct local function from a EmbeddedGlobalGFEFunction. - * - * The obtained local function satisfies the concept - * `Dune::Functions::Concept::LocalFunction`. It must be bound - * to an entity from the entity set of the EmbeddedGlobalGFEFunction - * before it can be used. - */ - friend LocalFunction localFunction(const EmbeddedGlobalGFEFunction& t) - { - return LocalFunction(t); - } + // Extract local coefficients + std::vector<TargetSpace> localCoeff(numOfBaseFct); -#if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Element = typename Basis::GridView::template Codim<0>::Entity; - /** \brief Evaluate the function at local coordinates. */ - void evaluateLocal(const Element& element, const Domain& local, typename TargetSpace::CoordinateType& out) const override - { - out = this->operator()(element,local); - } + for (decltype(numOfBaseFct) i=0; i<numOfBaseFct; i++) + localCoeff[i] = this->dofs()[localView.index(i)]; - /** \brief Evaluate the function at local coordinates. */ - typename TargetSpace::CoordinateType operator()(const Element& element, const Domain& local) const - { - auto localView = this->basis().localView(); - localView.bind(element); - auto numOfBaseFct = localView.size(); + // create local gfe function + LocalInterpolationRule localInterpolationRule(localView.tree().finiteElement(),localCoeff); + + // use it to evaluate the derivative + auto refJac = localInterpolationRule.evaluateDerivative(local); - // Extract local coefficients - std::vector<TargetSpace> localCoeff(numOfBaseFct); + out =0.0; - for (size_t i=0; i<numOfBaseFct; i++) - localCoeff[i] = this->dofs()[localView.index(i)]; + //transform the gradient + const auto jacInvTrans = element.geometry().jacobianInverseTransposed(local); + for (size_t k=0; k< refJac.N(); k++) + jacInvTrans.umv(refJac[k],out[k]); + } +#endif + }; - // create local gfe function - LocalInterpolationRule localInterpolationRule(localView.tree().finiteElement(),localCoeff); - return localInterpolationRule.evaluate(local).globalCoordinates(); - } - /** \brief evaluation of derivative in local coordinates + /** + * \brief Derivative of a `EmbeddedGlobalGFEFunction` + * + * Function returning the derivative of the given `EmbeddedGlobalGFEFunction` + * with respect to global coordinates. * - * \param e Evaluate in local coordinates with respect to this element. - * \param x point in local coordinates at which to evaluate the derivative - * \param d will contain the derivative at x after return + * \tparam EGGF instance of the `EmbeddedGlobalGFEFunction` this is a derivative of */ - void evaluateDerivativeLocal(const Element& element, const Domain& local, - typename Functions::SignatureTraits<typename EmbeddedGlobalGFEFunction::Traits::DerivativeInterface>::Range& out) const override - { - auto localView = this->basis().localView(); - localView.bind(element); - auto numOfBaseFct = localView.size(); - - // Extract local coefficients - std::vector<TargetSpace> localCoeff(numOfBaseFct); - - for (decltype(numOfBaseFct) i=0; i<numOfBaseFct; i++) - localCoeff[i] = this->dofs()[localView.index(i)]; - - // create local gfe function - LocalInterpolationRule localInterpolationRule(localView.tree().finiteElement(),localCoeff); - - // use it to evaluate the derivative - auto refJac = localInterpolationRule.evaluateDerivative(local); - - out =0.0; - - //transform the gradient - const auto jacInvTrans = element.geometry().jacobianInverseTransposed(local); - for (size_t k=0; k< refJac.N(); k++) - jacInvTrans.umv(refJac[k],out[k]); - } -#endif -}; - - -/** - * \brief Derivative of a `EmbeddedGlobalGFEFunction` - * - * Function returning the derivative of the given `EmbeddedGlobalGFEFunction` - * with respect to global coordinates. - * - * \tparam EGGF instance of the `EmbeddedGlobalGFEFunction` this is a derivative of - */ -template<typename EGGF> -class EmbeddedGlobalGFEFunctionDerivative -// There is no separate base class for EmbeddedGlobalGFEFunction, because the base class -// only handles coefficients and indices. It is independent of the type of function values. + template<typename EGGF> + class EmbeddedGlobalGFEFunctionDerivative + // There is no separate base class for EmbeddedGlobalGFEFunction, because the base class + // only handles coefficients and indices. It is independent of the type of function values. #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - : public Impl::GlobalGFEFunctionBase<typename EGGF::Basis, typename EGGF::Vector, typename EGGF::LocalInterpolationRule, - Dune::FieldMatrix<double, EGGF::Vector::value_type::EmbeddedTangentVector::dimension, EGGF::Basis::GridView::dimensionworld> > + : public Impl::GlobalGFEFunctionBase<typename EGGF::Basis, typename EGGF::Vector, typename EGGF::LocalInterpolationRule, + Dune::FieldMatrix<double, EGGF::Vector::value_type::EmbeddedTangentVector::dimension, EGGF::Basis::GridView::dimensionworld> > #else - : public Impl::GlobalGFEFunctionBase<typename EGGF::Basis, typename EGGF::Vector, typename EGGF::LocalInterpolationRule> + : public Impl::GlobalGFEFunctionBase<typename EGGF::Basis, typename EGGF::Vector, typename EGGF::LocalInterpolationRule> #endif -{ + { #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Base = Impl::GlobalGFEFunctionBase<typename EGGF::Basis, typename EGGF::Vector, typename EGGF::LocalInterpolationRule, - Dune::FieldMatrix<double, EGGF::Vector::value_type::EmbeddedTangentVector::dimension, EGGF::Basis::GridView::dimensionworld> >; + using Base = Impl::GlobalGFEFunctionBase<typename EGGF::Basis, typename EGGF::Vector, typename EGGF::LocalInterpolationRule, + Dune::FieldMatrix<double, EGGF::Vector::value_type::EmbeddedTangentVector::dimension, EGGF::Basis::GridView::dimensionworld> >; #else - using Base = Impl::GlobalGFEFunctionBase<typename EGGF::Basis, typename EGGF::Vector, typename EGGF::LocalInterpolationRule>; - using Data = typename Base::Data; + using Base = Impl::GlobalGFEFunctionBase<typename EGGF::Basis, typename EGGF::Vector, typename EGGF::LocalInterpolationRule>; + using Data = typename Base::Data; #endif -public: - using EmbeddedGlobalGFEFunction = EGGF; + public: + using EmbeddedGlobalGFEFunction = EGGF; - using Basis = typename Base::Basis; - using Vector = typename Base::Vector; + using Basis = typename Base::Basis; + using Vector = typename Base::Vector; #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Data = typename Impl::Data<Basis,Vector>; + using Data = typename Impl::Data<Basis,Vector>; #endif - using Domain = typename Base::Domain; - using Range = typename Functions::SignatureTraits<typename EmbeddedGlobalGFEFunction::Traits::DerivativeInterface>::Range; + using Domain = typename Base::Domain; + using Range = typename Functions::SignatureTraits<typename EmbeddedGlobalGFEFunction::Traits::DerivativeInterface>::Range; - using Traits = Functions::Imp::GridFunctionTraits<Range(Domain), typename Base::EntitySet, Functions::DefaultDerivativeTraits, 16>; - - /** - * \brief local function evaluating the derivative in reference coordinates - * - * Note that the function returns the derivative with respect to global - * coordinates even when the point is given in reference coordinates on - * an element. - */ - class LocalFunction - : public Base::LocalFunctionBase - { - using LocalBase = typename Base::LocalFunctionBase; - using size_type = typename Base::Tree::size_type; - - public: - using GlobalFunction = EmbeddedGlobalGFEFunctionDerivative; - using Domain = typename LocalBase::Domain; - using Range = GlobalFunction::Range; - using Element = typename LocalBase::Element; - - //! Create a local function from the associated grid function - LocalFunction(const GlobalFunction& globalFunction) - : LocalBase(globalFunction.data_) - { - /* Nothing. */ - } + using Traits = Functions::Imp::GridFunctionTraits<Range(Domain), typename Base::EntitySet, Functions::DefaultDerivativeTraits, 16>; /** - * \brief Bind LocalFunction to grid element. + * \brief local function evaluating the derivative in reference coordinates * - * You must call this method before `operator()` - * and after changes to the coefficient vector. + * Note that the function returns the derivative with respect to global + * coordinates even when the point is given in reference coordinates on + * an element. */ - void bind(const Element& element) + class LocalFunction + : public Base::LocalFunctionBase { - LocalBase::bind(element); - geometry_.emplace(element.geometry()); - } + using LocalBase = typename Base::LocalFunctionBase; + using size_type = typename Base::Tree::size_type; + + public: + using GlobalFunction = EmbeddedGlobalGFEFunctionDerivative; + using Domain = typename LocalBase::Domain; + using Range = GlobalFunction::Range; + using Element = typename LocalBase::Element; + + //! Create a local function from the associated grid function + LocalFunction(const GlobalFunction& globalFunction) + : LocalBase(globalFunction.data_) + { + /* Nothing. */ + } + + /** + * \brief Bind LocalFunction to grid element. + * + * You must call this method before `operator()` + * and after changes to the coefficient vector. + */ + void bind(const Element& element) + { + LocalBase::bind(element); + geometry_.emplace(element.geometry()); + } + + //! Unbind the local-function. + void unbind() + { + geometry_.reset(); + LocalBase::unbind(); + } + + /** + * \brief Evaluate this local-function in coordinates `x` in the bound element. + * + * The result of this method is undefined if you did + * not call bind() beforehand or changed the coefficient + * vector after the last call to bind(). In the latter case + * you have to call bind() again in order to make operator() + * usable. + * + * Note that the function returns the derivative with respect to global + * coordinates even though the evaluation point is given in reference coordinates + * on the current element. + */ + Range operator()(const Domain& x) const + { + // Jacobian with respect to local coordinates + auto refJac = this->localInterpolationRule_->evaluateDerivative(x); - //! Unbind the local-function. - void unbind() - { - geometry_.reset(); - LocalBase::unbind(); - } + // Transform to world coordinates + return refJac * geometry_->jacobianInverse(x); + } + + //! Not implemented + friend typename Traits::LocalFunctionTraits::DerivativeInterface derivative(const LocalFunction&) + { + DUNE_THROW(NotImplemented, "derivative of derivative is not implemented"); + } + + private: + std::optional<typename Element::Geometry> geometry_; + }; /** - * \brief Evaluate this local-function in coordinates `x` in the bound element. + * \brief create object from `EmbeddedGlobalGFEFunction` data * - * The result of this method is undefined if you did - * not call bind() beforehand or changed the coefficient - * vector after the last call to bind(). In the latter case - * you have to call bind() again in order to make operator() - * usable. + * Please call `derivative(embeddedGlobalGFEFunction)` to create an instance + * of this class. + */ + EmbeddedGlobalGFEFunctionDerivative(const std::shared_ptr<const Data>& data) + : Base(data) + { + /* Nothing. */ + } + + /** \brief Evaluate the discrete grid-function derivative in global coordinates * - * Note that the function returns the derivative with respect to global - * coordinates even though the evaluation point is given in reference coordinates - * on the current element. + * \warning This has to find the element that the evaluation point is in. + * It is therefore very slow. */ Range operator()(const Domain& x) const { - // Jacobian with respect to local coordinates - auto refJac = this->localInterpolationRule_->evaluateDerivative(x); + HierarchicSearch search(this->data_->basis->gridView().grid(), this->data_->basis->gridView().indexSet()); - // Transform to world coordinates - return refJac * geometry_->jacobianInverse(x); + const auto e = search.findEntity(x); + auto localThis = localFunction(*this); + localThis.bind(e); + return localThis(e.geometry().local(x)); } - //! Not implemented - friend typename Traits::LocalFunctionTraits::DerivativeInterface derivative(const LocalFunction&) + friend typename Traits::DerivativeInterface derivative(const EmbeddedGlobalGFEFunctionDerivative& f) { DUNE_THROW(NotImplemented, "derivative of derivative is not implemented"); } - private: - std::optional<typename Element::Geometry> geometry_; - }; - - /** - * \brief create object from `EmbeddedGlobalGFEFunction` data - * - * Please call `derivative(embeddedGlobalGFEFunction)` to create an instance - * of this class. - */ - EmbeddedGlobalGFEFunctionDerivative(const std::shared_ptr<const Data>& data) - : Base(data) - { - /* Nothing. */ - } - - /** \brief Evaluate the discrete grid-function derivative in global coordinates - * - * \warning This has to find the element that the evaluation point is in. - * It is therefore very slow. - */ - Range operator()(const Domain& x) const - { - HierarchicSearch search(this->data_->basis->gridView().grid(), this->data_->basis->gridView().indexSet()); - - const auto e = search.findEntity(x); - auto localThis = localFunction(*this); - localThis.bind(e); - return localThis(e.geometry().local(x)); - } - - friend typename Traits::DerivativeInterface derivative(const EmbeddedGlobalGFEFunctionDerivative& f) - { - DUNE_THROW(NotImplemented, "derivative of derivative is not implemented"); - } - - //! Construct local function from a `EmbeddedGlobalGFEFunctionDerivative` - friend LocalFunction localFunction(const EmbeddedGlobalGFEFunctionDerivative& f) - { - return LocalFunction(f); - } + //! Construct local function from a `EmbeddedGlobalGFEFunctionDerivative` + friend LocalFunction localFunction(const EmbeddedGlobalGFEFunctionDerivative& f) + { + return LocalFunction(f); + } #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Element = typename Basis::GridView::template Codim<0>::Entity; - /** \brief Evaluate the function at local coordinates. */ - void evaluateLocal(const Element& element, const Domain& local, Range& out) const override - { - // This method will never be called. - } + using Element = typename Basis::GridView::template Codim<0>::Entity; + /** \brief Evaluate the function at local coordinates. */ + void evaluateLocal(const Element& element, const Domain& local, Range& out) const override + { + // This method will never be called. + } #endif -}; + }; } // namespace Dune::GFE diff --git a/dune/gfe/filereader.hh b/dune/gfe/filereader.hh index e0958b9d..24efd6ca 100644 --- a/dune/gfe/filereader.hh +++ b/dune/gfe/filereader.hh @@ -12,32 +12,32 @@ namespace Dune { namespace GFE { // Convert the pairs {grid vertex, vector of dimension d} in the given file to a map template <int d> - static std::unordered_map<std::string, FieldVector<double,d>> transformFileToMap(std::string pathToFile) { + static std::unordered_map<std::string, FieldVector<double,d> > transformFileToMap(std::string pathToFile) { - std::unordered_map<std::string, FieldVector<double,d>> map; + std::unordered_map<std::string, FieldVector<double,d> > map; - std::string line, displacement, entry; + std::string line, displacement, entry; - std::ifstream file(pathToFile, std::ios::in); + std::ifstream file(pathToFile, std::ios::in); - if (file.is_open()) { - while (std::getline(file, line)) { - size_t j = 0; - size_t pos = line.find(":"); - displacement = line.substr(pos + 1); - line.erase(pos); - std::stringstream entries(displacement); - FieldVector<double,d> vector(0); - while(entries >> entry) - vector[j++] = std::stod(entry); - map.insert({line,vector}); - } - file.close(); - } else { - DUNE_THROW(Exception, "Error: Could not open the file " + pathToFile + " !"); + if (file.is_open()) { + while (std::getline(file, line)) { + size_t j = 0; + size_t pos = line.find(":"); + displacement = line.substr(pos + 1); + line.erase(pos); + std::stringstream entries(displacement); + FieldVector<double,d> vector(0); + while(entries >> entry) + vector[j++] = std::stod(entry); + map.insert({line,vector}); } + file.close(); + } else { + DUNE_THROW(Exception, "Error: Could not open the file " + pathToFile + " !"); + } return map; } } } -#endif \ No newline at end of file +#endif diff --git a/dune/gfe/geodesicdifference.hh b/dune/gfe/geodesicdifference.hh index a8542362..38788d1e 100644 --- a/dune/gfe/geodesicdifference.hh +++ b/dune/gfe/geodesicdifference.hh @@ -9,19 +9,19 @@ template <class TargetSpace> Dune::BlockVector<typename TargetSpace::TangentVector> computeGeodesicDifference(const std::vector<TargetSpace>& a, const std::vector<TargetSpace>& b) { - if (a.size() != b.size()) - DUNE_THROW(Dune::Exception, "a and b have to have the same length!"); + if (a.size() != b.size()) + DUNE_THROW(Dune::Exception, "a and b have to have the same length!"); - Dune::BlockVector<typename TargetSpace::TangentVector> result(a.size()); + Dune::BlockVector<typename TargetSpace::TangentVector> result(a.size()); - for (size_t i=0; i<result.size(); i++) { + for (size_t i=0; i<result.size(); i++) { - // Subtract orientations on the tangent space of 'a' - result[i] = TargetSpace::difference(a[i], b[i]); + // Subtract orientations on the tangent space of 'a' + result[i] = TargetSpace::difference(a[i], b[i]); - } + } - return result; + return result; } #endif diff --git a/dune/gfe/geodesicfefunctionadaptor.hh b/dune/gfe/geodesicfefunctionadaptor.hh index b80f325e..e070f84d 100644 --- a/dune/gfe/geodesicfefunctionadaptor.hh +++ b/dune/gfe/geodesicfefunctionadaptor.hh @@ -10,11 +10,11 @@ template <class Basis, class TargetSpace> class GeodesicFEFunctionAdaptor { public: -/** \brief Refine a grid globally and prolong a given geodesic finite element function - */ -template <class GridType> -static void geodesicFEFunctionAdaptor(GridType& grid, std::vector<TargetSpace>& x) -{ + /** \brief Refine a grid globally and prolong a given geodesic finite element function + */ + template <class GridType> + static void geodesicFEFunctionAdaptor(GridType& grid, std::vector<TargetSpace>& x) + { const int dim = GridType::dimension; assert(x.size() == grid.size(dim)); @@ -29,7 +29,7 @@ static void geodesicFEFunctionAdaptor(GridType& grid, std::vector<TargetSpace>& std::map<typename GridType::Traits::LocalIdSet::IdType, TargetSpace> dofMap; for (const auto& vertex : vertices(grid.leafGridView())) - dofMap.insert(std::make_pair(idSet.id(vertex), x[indexSet.index(vertex)])); + dofMap.insert(std::make_pair(idSet.id(vertex), x[indexSet.index(vertex)])); @@ -49,74 +49,74 @@ static void geodesicFEFunctionAdaptor(GridType& grid, std::vector<TargetSpace>& for (const auto& element : elements(grid.leafGridView())) { - // Set up a local gfe function on the father element - size_t nFatherDofs = element.father().subEntities(dim); - std::vector<TargetSpace> coefficients(nFatherDofs); - - for (int i=0; i<nFatherDofs; i++) - coefficients[i] = dofMap.find(idSet.subId(element.father(),i,dim))->second; + // Set up a local gfe function on the father element + size_t nFatherDofs = element.father().subEntities(dim); + std::vector<TargetSpace> coefficients(nFatherDofs); - typedef typename P1NodalBasis<typename GridType::LeafGridView>::LocalFiniteElement LocalFiniteElement; - LocalGeodesicFEFunction<dim,double,LocalFiniteElement,TargetSpace> fatherFunction(p1Basis.getLocalFiniteElement(element), - coefficients); + for (int i=0; i<nFatherDofs; i++) + coefficients[i] = dofMap.find(idSet.subId(element.father(),i,dim))->second; - // The embedding of this element into the father geometry - const auto& geometryInFather = element.geometryInFather(); + typedef typename P1NodalBasis<typename GridType::LeafGridView>::LocalFiniteElement LocalFiniteElement; + LocalGeodesicFEFunction<dim,double,LocalFiniteElement,TargetSpace> fatherFunction(p1Basis.getLocalFiniteElement(element), + coefficients); - size_t nDofs = element.subEntities(dim); - for (int i=0; i<nDofs; i++) { + // The embedding of this element into the father geometry + const auto& geometryInFather = element.geometryInFather(); - if (dofMap.find(idSet.subId(element,i,dim)) != dofMap.end()) { + size_t nDofs = element.subEntities(dim); + for (int i=0; i<nDofs; i++) { - // If the vertex exists on the coarser level we take the value from there. - // That should be faster and more accurate than interpolating - x[indexSet.subIndex(element,i,dim)] = dofMap[idSet.subId(element,i,dim)]; + if (dofMap.find(idSet.subId(element,i,dim)) != dofMap.end()) { - } else { + // If the vertex exists on the coarser level we take the value from there. + // That should be faster and more accurate than interpolating + x[indexSet.subIndex(element,i,dim)] = dofMap[idSet.subId(element,i,dim)]; - // Interpolate - x[indexSet.subIndex(element,i,dim)] = fatherFunction.evaluate(geometryInFather.corner(i)); + } else { - } + // Interpolate + x[indexSet.subIndex(element,i,dim)] = fatherFunction.evaluate(geometryInFather.corner(i)); } + } + } -} + } -/** \brief Coordinate function in one variable, constant in the others + /** \brief Coordinate function in one variable, constant in the others - This is used to extract the positions of the Lagrange nodes. - */ -template <int dim> -struct CoordinateFunction + This is used to extract the positions of the Lagrange nodes. + */ + template <int dim> + struct CoordinateFunction : public Dune::VirtualFunction<Dune::FieldVector<double,dim>, Dune::FieldVector<double,1> > -{ + { CoordinateFunction(int d) - : d_(d) + : d_(d) {} void evaluate(const Dune::FieldVector<double, dim>& x, Dune::FieldVector<double,1>& out) const { - out[0] = x[d_]; + out[0] = x[d_]; } // int d_; -}; - - -/** \brief Refine a grid globally and prolong a given geodesic finite element function - * - * \tparam order Interpolation order of the function space. Kinda stupid that I - * have to provide this by hand. Will change... - */ -template <int order> -static void higherOrderGFEFunctionAdaptor(Basis& basis, - typename Basis::GridView::Grid& grid, std::vector<TargetSpace>& x) -{ + }; + + + /** \brief Refine a grid globally and prolong a given geodesic finite element function + * + * \tparam order Interpolation order of the function space. Kinda stupid that I + * have to provide this by hand. Will change... + */ + template <int order> + static void higherOrderGFEFunctionAdaptor(Basis& basis, + typename Basis::GridView::Grid& grid, std::vector<TargetSpace>& x) + { typedef typename Basis::GridView::Grid GridType; const int dim = GridType::dimension; @@ -138,14 +138,14 @@ static void higherOrderGFEFunctionAdaptor(Basis& basis, for (; eIt!=eEndIt; ++eIt) { - const typename Basis::LocalFiniteElement& lfe = basis.getLocalFiniteElement(*eIt); - std::vector<TargetSpace> coefficients(lfe.localCoefficients().size()); + const typename Basis::LocalFiniteElement& lfe = basis.getLocalFiniteElement(*eIt); + std::vector<TargetSpace> coefficients(lfe.localCoefficients().size()); - for (size_t i=0; i<lfe.localCoefficients().size(); i++) - coefficients[i] = x[basis.index(*eIt, i)]; + for (size_t i=0; i<lfe.localCoefficients().size(); i++) + coefficients[i] = x[basis.index(*eIt, i)]; - IdType id = idSet.id(*eIt); - dofMap.insert(std::make_pair(id, coefficients)); + IdType id = idSet.id(*eIt); + dofMap.insert(std::make_pair(id, coefficients)); } @@ -167,44 +167,44 @@ static void higherOrderGFEFunctionAdaptor(Basis& basis, for (eIt=grid.template leafbegin<0>(); eIt!=eEndIt; ++eIt) { - const typename Basis::LocalFiniteElement& lfe = basis.getLocalFiniteElement(*eIt); + const typename Basis::LocalFiniteElement& lfe = basis.getLocalFiniteElement(*eIt); - typedef typename Dune::PQkLocalFiniteElementFactory<double,double,dim,order>::FiniteElementType FatherFiniteElementType; + typedef typename Dune::PQkLocalFiniteElementFactory<double,double,dim,order>::FiniteElementType FatherFiniteElementType; - auto fatherLFE = std::unique_ptr<FatherFiniteElementType>(Dune::PQkLocalFiniteElementFactory<double,double,dim,order>::create(eIt->father()->type())); + auto fatherLFE = std::unique_ptr<FatherFiniteElementType>(Dune::PQkLocalFiniteElementFactory<double,double,dim,order>::create(eIt->father()->type())); - // Set up a local gfe function on the father element - std::vector<TargetSpace> coefficients = dofMap[idSet.id(*eIt->father())]; + // Set up a local gfe function on the father element + std::vector<TargetSpace> coefficients = dofMap[idSet.id(*eIt->father())]; - LocalGeodesicFEFunction<dim,double,typename Basis::LocalFiniteElement,TargetSpace> fatherFunction(*fatherLFE, coefficients); + LocalGeodesicFEFunction<dim,double,typename Basis::LocalFiniteElement,TargetSpace> fatherFunction(*fatherLFE, coefficients); - // The embedding of this element into the father geometry - const typename GridType::template Codim<0>::LocalGeometry& geometryInFather = eIt->geometryInFather(); + // The embedding of this element into the father geometry + const typename GridType::template Codim<0>::LocalGeometry& geometryInFather = eIt->geometryInFather(); - // Generate position of the Lagrange nodes - std::vector<Dune::FieldVector<double,dim> > lagrangeNodes(lfe.localBasis().size()); + // Generate position of the Lagrange nodes + std::vector<Dune::FieldVector<double,dim> > lagrangeNodes(lfe.localBasis().size()); - for (int i=0; i<dim; i++) { - CoordinateFunction<dim> lFunction(i); - std::vector<Dune::FieldVector<double,1> > coordinates; - lfe.localInterpolation().interpolate(lFunction, coordinates); + for (int i=0; i<dim; i++) { + CoordinateFunction<dim> lFunction(i); + std::vector<Dune::FieldVector<double,1> > coordinates; + lfe.localInterpolation().interpolate(lFunction, coordinates); - for (size_t j=0; j<coordinates.size(); j++) - lagrangeNodes[j][i] = coordinates[j]; + for (size_t j=0; j<coordinates.size(); j++) + lagrangeNodes[j][i] = coordinates[j]; - } + } - for (int i=0; i<lfe.localCoefficients().size(); i++) { + for (int i=0; i<lfe.localCoefficients().size(); i++) { - unsigned int idx = basis.index(*eIt, i); + unsigned int idx = basis.index(*eIt, i); - x[idx] = fatherFunction.evaluate(geometryInFather.global(lagrangeNodes[i])); + x[idx] = fatherFunction.evaluate(geometryInFather.global(lagrangeNodes[i])); - } + } } -} + } }; diff --git a/dune/gfe/geometries/moebiusstrip.hh b/dune/gfe/geometries/moebiusstrip.hh index 793c2283..5d345c9d 100644 --- a/dune/gfe/geometries/moebiusstrip.hh +++ b/dune/gfe/geometries/moebiusstrip.hh @@ -9,97 +9,97 @@ namespace Dune { -/// \brief Functor representing a Möbius strip in 3D, where the base circle is the circle in the x-y-plane with radius_ around 0,0,0 -template <class T = double> -class MoebiusStripProjection -{ - static const int dim = 3; - using Domain = FieldVector<T,dim>; - using Jacobian = FieldMatrix<T,dim,dim>; - - T radius_; - -public: - MoebiusStripProjection (T radius) - : radius_(radius) - {} - - /// \brief Project the coordinate to the MS using closest point projection - Domain operator() (const Domain& x) const + /// \brief Functor representing a Möbius strip in 3D, where the base circle is the circle in the x-y-plane with radius_ around 0,0,0 + template <class T = double> + class MoebiusStripProjection { - double nrm = std::sqrt(x[0]*x[0] + x[1]*x[1]); - //center for the point x - it lies on the circle around (0,0,0) with radius radius_ in the x-y-plane - Domain center{x[0] * radius_/nrm, x[1] * radius_/nrm, 0}; - double cosu = x[0]/nrm; - double sinu = x[1]/nrm; - double cosuhalf = std::sqrt((1+cosu)/2); - cosuhalf *= (sinu < 0) ? (-1) : 1 ; // u goes from 0 to 2*pi, so cosuhalf is negative if sinu < 0, we multiply that here - double sinuhalf = std::sqrt((1-cosu)/2); //u goes from 0 to 2*pi, so sinuhalf is always >= 0 - - // now calculate line from center to the new point - // the direction is (cosuhalf*cosu,cosuhalf*sinu,sinuhalf), as can be seen from the formula to construct the MS, we normalize this vector - Domain centerToPointOnMS{cosuhalf*cosu,cosuhalf*sinu,sinuhalf}; - centerToPointOnMS /= centerToPointOnMS.two_norm(); - - Domain centerToX = center - x; - // We need the length, let theta be the angle between the vector centerToX and center to centerToPointOnMS - // then cos(theta) = centerToX*centerToPointOnMS/(len(centerToX)*len(centerToPointOnMS)) - // We want to project the point to MS, s.t. the angle between the MS and the projection is 90deg - // Then, length = cos(theta) * len(centerToX) = centerToX*centerToPointOnMS/len(centerToPointOnMS) = centerToX*centerToPointOnMS - double length = - centerToX * centerToPointOnMS; - centerToPointOnMS *= length; - return center + centerToPointOnMS; - } + static const int dim = 3; + using Domain = FieldVector<T,dim>; + using Jacobian = FieldMatrix<T,dim,dim>; + T radius_; - /// \brief derivative of the projection to the MS - friend auto derivative (const MoebiusStripProjection& moebiusStrip) - { - DUNE_THROW(NotImplemented,"The derivative of the projection to the Möbius strip is not implemented yet!"); - return [radius = moebiusStrip.radius_](const Domain& x) - { - Jacobian out; - return out; - }; - } - - /// \brief Normal Vector of the MS - Domain normal (const Domain& x) const - { - using std::sqrt; - Domain nVec = {x[0],x[1],0}; - double nrm = std::sqrt(x[0]*x[0] + x[1]*x[1]); - double cosu = x[0]/nrm; - double sinu = x[1]/nrm; - double cosuhalf = std::sqrt((1+cosu)/2); - cosuhalf *= (sinu < 0) ? (-1) : 1 ; // u goes from 0 to 2*pi, so cosuhalf is negative if sinu < 0, we multiply that here - double sinuhalf = std::sqrt((1-cosu)/2); - nVec[2] = (-1)*cosuhalf*(cosu*x[0]+sinu*x[1])/sinuhalf; - nVec /= nVec.two_norm(); - return nVec; - } + public: + MoebiusStripProjection (T radius) + : radius_(radius) + {} - /// \brief The mean curvature of the MS - T mean_curvature (const Domain& /*x*/) const - { - DUNE_THROW(NotImplemented,"The mean curvature of the Möbius strip is not implemented yet!"); - return 1; - } + /// \brief Project the coordinate to the MS using closest point projection + Domain operator() (const Domain& x) const + { + double nrm = std::sqrt(x[0]*x[0] + x[1]*x[1]); + //center for the point x - it lies on the circle around (0,0,0) with radius radius_ in the x-y-plane + Domain center{x[0] * radius_/nrm, x[1] * radius_/nrm, 0}; + double cosu = x[0]/nrm; + double sinu = x[1]/nrm; + double cosuhalf = std::sqrt((1+cosu)/2); + cosuhalf *= (sinu < 0) ? (-1) : 1 ; // u goes from 0 to 2*pi, so cosuhalf is negative if sinu < 0, we multiply that here + double sinuhalf = std::sqrt((1-cosu)/2); //u goes from 0 to 2*pi, so sinuhalf is always >= 0 + + // now calculate line from center to the new point + // the direction is (cosuhalf*cosu,cosuhalf*sinu,sinuhalf), as can be seen from the formula to construct the MS, we normalize this vector + Domain centerToPointOnMS{cosuhalf*cosu,cosuhalf*sinu,sinuhalf}; + centerToPointOnMS /= centerToPointOnMS.two_norm(); + + Domain centerToX = center - x; + // We need the length, let theta be the angle between the vector centerToX and center to centerToPointOnMS + // then cos(theta) = centerToX*centerToPointOnMS/(len(centerToX)*len(centerToPointOnMS)) + // We want to project the point to MS, s.t. the angle between the MS and the projection is 90deg + // Then, length = cos(theta) * len(centerToX) = centerToX*centerToPointOnMS/len(centerToPointOnMS) = centerToX*centerToPointOnMS + double length = - centerToX * centerToPointOnMS; + centerToPointOnMS *= length; + return center + centerToPointOnMS; + } + + + /// \brief derivative of the projection to the MS + friend auto derivative (const MoebiusStripProjection& moebiusStrip) + { + DUNE_THROW(NotImplemented,"The derivative of the projection to the Möbius strip is not implemented yet!"); + return [radius = moebiusStrip.radius_](const Domain& x) + { + Jacobian out; + return out; + }; + } + + /// \brief Normal Vector of the MS + Domain normal (const Domain& x) const + { + using std::sqrt; + Domain nVec = {x[0],x[1],0}; + double nrm = std::sqrt(x[0]*x[0] + x[1]*x[1]); + double cosu = x[0]/nrm; + double sinu = x[1]/nrm; + double cosuhalf = std::sqrt((1+cosu)/2); + cosuhalf *= (sinu < 0) ? (-1) : 1 ; // u goes from 0 to 2*pi, so cosuhalf is negative if sinu < 0, we multiply that here + double sinuhalf = std::sqrt((1-cosu)/2); + nVec[2] = (-1)*cosuhalf*(cosu*x[0]+sinu*x[1])/sinuhalf; + nVec /= nVec.two_norm(); + return nVec; + } + + /// \brief The mean curvature of the MS + T mean_curvature (const Domain& /*x*/) const + { + DUNE_THROW(NotImplemented,"The mean curvature of the Möbius strip is not implemented yet!"); + return 1; + } - /// \brief The area of the MS - T area () const + /// \brief The area of the MS + T area () const + { + DUNE_THROW(NotImplemented,"The area of the Möbius strip is not implemented yet!"); + return 1; + } + }; + + /// \brief construct a grid function representing the parametrization of a MS + template <class Grid, class T> + auto moebiusStripGridFunction (T radius) { - DUNE_THROW(NotImplemented,"The area of the Möbius strip is not implemented yet!"); - return 1; + return analyticGridFunction<Grid>(MoebiusStripProjection<T>{radius}); } -}; - -/// \brief construct a grid function representing the parametrization of a MS -template <class Grid, class T> -auto moebiusStripGridFunction (T radius) -{ - return analyticGridFunction<Grid>(MoebiusStripProjection<T>{radius}); -} } // end namespace Dune diff --git a/dune/gfe/geometries/twistedstrip.hh b/dune/gfe/geometries/twistedstrip.hh index d7af4843..9d502aeb 100644 --- a/dune/gfe/geometries/twistedstrip.hh +++ b/dune/gfe/geometries/twistedstrip.hh @@ -9,95 +9,95 @@ namespace Dune { -/// \brief Functor representing a twisted strip in 3D, with length length_ and nTwists twists -template <class T = double> -class TwistedStripProjection -{ - static const int dim = 3; - using Domain = FieldVector<T,dim>; - using Jacobian = FieldMatrix<T,dim,dim>; + /// \brief Functor representing a twisted strip in 3D, with length length_ and nTwists twists + template <class T = double> + class TwistedStripProjection + { + static const int dim = 3; + using Domain = FieldVector<T,dim>; + using Jacobian = FieldMatrix<T,dim,dim>; - T length_; - double nTwists_; + T length_; + double nTwists_; -public: - TwistedStripProjection (T length, double nTwists) - : length_(length), + public: + TwistedStripProjection (T length, double nTwists) + : length_(length), nTwists_(nTwists) - {} + {} - /// \brief Project the coordinate to the twisted strip using closest point projection - Domain operator() (const Domain& x) const - { - // Angle for the x - position of the point - double alpha = std::acos(-1)*nTwists_*x[0]/length_; - double cosalpha = std::cos(alpha); - double sinalpha = std::sin(alpha); + /// \brief Project the coordinate to the twisted strip using closest point projection + Domain operator() (const Domain& x) const + { + // Angle for the x - position of the point + double alpha = std::acos(-1)*nTwists_*x[0]/length_; + double cosalpha = std::cos(alpha); + double sinalpha = std::sin(alpha); - // Add the line from middle to the new point - the direction is (0,cosalpha,sinalpha) - // We need the distance, calculated by (y^2 + z^2)^0.5 - double dist = std::sqrt(x[1]*x[1] + x[2]*x[2]); - double y = std::abs(cosalpha*dist); - if (x[1] < 0 and std::abs(x[1]) > std::abs(x[2])) { // only trust the sign of x[1] if it is larger than x[2] - y *= (-1); - } else if (std::abs(x[1]) <= std::abs(x[2])) { - if (cosalpha * sinalpha >= 0 and x[2] < 0) // if cosalpha*sinalpha > 0, x[1] and x[2] must have the same sign + // Add the line from middle to the new point - the direction is (0,cosalpha,sinalpha) + // We need the distance, calculated by (y^2 + z^2)^0.5 + double dist = std::sqrt(x[1]*x[1] + x[2]*x[2]); + double y = std::abs(cosalpha*dist); + if (x[1] < 0 and std::abs(x[1]) > std::abs(x[2])) { // only trust the sign of x[1] if it is larger than x[2] y *= (-1); - if (cosalpha * sinalpha <= 0 and x[2] > 0) // if cosalpha*sinalpha < 0, x[1] and x[2] must have opposite signs - y *= (-1); - } - double z = std::abs(sinalpha*dist); - if (x[2] < 0 and std::abs(x[2]) > std::abs(x[1])) { - z *= (-1); - } else if (std::abs(x[2]) <= std::abs(x[1])) { - if (cosalpha * sinalpha >= 0 and x[1] < 0) - z *= (-1); - if (cosalpha * sinalpha <= 0 and x[1] > 0) + } else if (std::abs(x[1]) <= std::abs(x[2])) { + if (cosalpha * sinalpha >= 0 and x[2] < 0) // if cosalpha*sinalpha > 0, x[1] and x[2] must have the same sign + y *= (-1); + if (cosalpha * sinalpha <= 0 and x[2] > 0) // if cosalpha*sinalpha < 0, x[1] and x[2] must have opposite signs + y *= (-1); + } + double z = std::abs(sinalpha*dist); + if (x[2] < 0 and std::abs(x[2]) > std::abs(x[1])) { z *= (-1); + } else if (std::abs(x[2]) <= std::abs(x[1])) { + if (cosalpha * sinalpha >= 0 and x[1] < 0) + z *= (-1); + if (cosalpha * sinalpha <= 0 and x[1] > 0) + z *= (-1); + } + return {x[0], y , z}; } - return {x[0], y , z}; - } - /// \brief derivative of the projection to the twistedstrip - friend auto derivative (const TwistedStripProjection& twistedStrip) - { - DUNE_THROW(NotImplemented,"The derivative of the projection to the twisted strip is not implemented yet!"); - return [length = twistedStrip.length_, nTwists = twistedStrip.nTwists_](const Domain& x) + /// \brief derivative of the projection to the twistedstrip + friend auto derivative (const TwistedStripProjection& twistedStrip) { - Jacobian out; - return out; - }; - } + DUNE_THROW(NotImplemented,"The derivative of the projection to the twisted strip is not implemented yet!"); + return [length = twistedStrip.length_, nTwists = twistedStrip.nTwists_](const Domain& x) + { + Jacobian out; + return out; + }; + } - /// \brief Normal Vector of the twisted strip - Domain normal (const Domain& x) const - { - // Angle for the x - position of the point - double alpha = std::acos(-1)*nTwists_*x[0]/length_; - std::cout << "normal at " << x[0] << "is " << -std::sin(alpha) << ", " << std::cos(alpha) << std::endl; - return {0,-std::sin(alpha), std::cos(alpha)}; - } + /// \brief Normal Vector of the twisted strip + Domain normal (const Domain& x) const + { + // Angle for the x - position of the point + double alpha = std::acos(-1)*nTwists_*x[0]/length_; + std::cout << "normal at " << x[0] << "is " << -std::sin(alpha) << ", " << std::cos(alpha) << std::endl; + return {0,-std::sin(alpha), std::cos(alpha)}; + } - /// \brief The mean curvature of the twisted strip - T mean_curvature (const Domain& /*x*/) const - { - DUNE_THROW(NotImplemented,"The mean curvature of the twisted strip is not implemented yet!"); - return 1; - } + /// \brief The mean curvature of the twisted strip + T mean_curvature (const Domain& /*x*/) const + { + DUNE_THROW(NotImplemented,"The mean curvature of the twisted strip is not implemented yet!"); + return 1; + } - /// \brief The area of the twisted strip - T area (T width) const + /// \brief The area of the twisted strip + T area (T width) const + { + return length_*width; + } + }; + + /// \brief construct a grid function representing the parametrization of a twisted strip + template <class Grid, class T> + auto twistedStripGridFunction (T length, double nTwists) { - return length_*width; + return analyticGridFunction<Grid>(TwistedStripProjection<T>{length, nTwists}); } -}; - -/// \brief construct a grid function representing the parametrization of a twisted strip -template <class Grid, class T> -auto twistedStripGridFunction (T length, double nTwists) -{ - return analyticGridFunction<Grid>(TwistedStripProjection<T>{length, nTwists}); -} } // end namespace Dune diff --git a/dune/gfe/gfedifferencenormsquared.hh b/dune/gfe/gfedifferencenormsquared.hh index a6b53e61..a6d15123 100644 --- a/dune/gfe/gfedifferencenormsquared.hh +++ b/dune/gfe/gfedifferencenormsquared.hh @@ -4,7 +4,7 @@ /** \file \brief Helper method which computes the energy norm (squared) of the difference of the fe functions on two different refinements of the same base grid. -*/ + */ #include <dune/geometry/type.hh> #include <dune/common/fvector.hh> @@ -18,271 +18,271 @@ template <class Basis, class TargetSpace> class GFEDifferenceNormSquared { - typedef typename Basis::GridView GridView; - typedef typename Basis::GridView::Grid GridType; - - // Grid dimension - const static int dim = GridType::dimension; - - /** \brief Check whether given local coordinates are contained in the reference element - \todo This method exists in the Dune grid interface! But we need the eps. - */ - static bool checkInside(const Dune::GeometryType& type, - const Dune::FieldVector<double, dim> &loc, - double eps) - { - switch (type.dim()) { - - case 0: // vertex - return false; - case 1: // line - return -eps <= loc[0] && loc[0] <= 1+eps; - case 2: - - if (type.isSimplex()) { - return -eps <= loc[0] && -eps <= loc[1] && (loc[0]+loc[1])<=1+eps; - } else if (type.isCube()) { - return -eps <= loc[0] && loc[0] <= 1+eps - && -eps <= loc[1] && loc[1] <= 1+eps; - } else { - DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); - } - - case 3: - if (type.isSimplex()) { - return -eps <= loc[0] && -eps <= loc[1] && -eps <= loc[2] - && (loc[0]+loc[1]+loc[2]) <= 1+eps; - } else if (type.isPyramid()) { - return -eps <= loc[0] && -eps <= loc[1] && -eps <= loc[2] - && (loc[0]+loc[2]) <= 1+eps - && (loc[1]+loc[2]) <= 1+eps; - } else if (type.isPrism()) { - return -eps <= loc[0] && -eps <= loc[1] - && (loc[0]+loc[1])<= 1+eps - && -eps <= loc[2] && loc[2] <= 1+eps; - } else if (type.isCube()) { - return -eps <= loc[0] && loc[0] <= 1+eps - && -eps <= loc[1] && loc[1] <= 1+eps - && -eps <= loc[2] && loc[2] <= 1+eps; - }else { - DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); - } - default: - DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); - } - + typedef typename Basis::GridView GridView; + typedef typename Basis::GridView::Grid GridType; + + // Grid dimension + const static int dim = GridType::dimension; + + /** \brief Check whether given local coordinates are contained in the reference element + \todo This method exists in the Dune grid interface! But we need the eps. + */ + static bool checkInside(const Dune::GeometryType& type, + const Dune::FieldVector<double, dim> &loc, + double eps) + { + switch (type.dim()) { + + case 0 : // vertex + return false; + case 1 : // line + return -eps <= loc[0] && loc[0] <= 1+eps; + case 2 : + + if (type.isSimplex()) { + return -eps <= loc[0] && -eps <= loc[1] && (loc[0]+loc[1])<=1+eps; + } else if (type.isCube()) { + return -eps <= loc[0] && loc[0] <= 1+eps + && -eps <= loc[1] && loc[1] <= 1+eps; + } else { + DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); + } + + case 3 : + if (type.isSimplex()) { + return -eps <= loc[0] && -eps <= loc[1] && -eps <= loc[2] + && (loc[0]+loc[1]+loc[2]) <= 1+eps; + } else if (type.isPyramid()) { + return -eps <= loc[0] && -eps <= loc[1] && -eps <= loc[2] + && (loc[0]+loc[2]) <= 1+eps + && (loc[1]+loc[2]) <= 1+eps; + } else if (type.isPrism()) { + return -eps <= loc[0] && -eps <= loc[1] + && (loc[0]+loc[1])<= 1+eps + && -eps <= loc[2] && loc[2] <= 1+eps; + } else if (type.isCube()) { + return -eps <= loc[0] && loc[0] <= 1+eps + && -eps <= loc[1] && loc[1] <= 1+eps + && -eps <= loc[2] && loc[2] <= 1+eps; + }else { + DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); + } + default : + DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); } - - - /** \brief Coordinate function in one variable, constant in the others - - This is used to extract the positions of the Lagrange nodes. - */ - struct CoordinateFunction - : public Dune::VirtualFunction<Dune::FieldVector<double,dim>, Dune::FieldVector<double,1> > - { - CoordinateFunction(int d) - : d_(d) - {} - - void evaluate(const Dune::FieldVector<double, dim>& x, Dune::FieldVector<double,1>& out) const { - out[0] = x[d_]; - } - // - int d_; - }; + } + + + /** \brief Coordinate function in one variable, constant in the others + + This is used to extract the positions of the Lagrange nodes. + */ + struct CoordinateFunction + : public Dune::VirtualFunction<Dune::FieldVector<double,dim>, Dune::FieldVector<double,1> > + { + CoordinateFunction(int d) + : d_(d) + {} + + void evaluate(const Dune::FieldVector<double, dim>& x, Dune::FieldVector<double,1>& out) const { + out[0] = x[d_]; + } + + // + int d_; + }; public: - static void interpolate(const GridType& sourceGrid, - const std::vector<TargetSpace>& source, - const GridType& targetGrid, - std::vector<TargetSpace>& target) - { - // Create a leaf function, which we need to be able to call 'evalall()' - Basis sourceBasis(sourceGrid.leafGridView()); - GlobalGeodesicFEFunction<Basis,TargetSpace> sourceFunction(sourceBasis, source); - - Basis targetBasis(targetGrid.leafGridView()); - - // /////////////////////////////////////////////////////////////////////////////////////////// - // Prolong the adaptive solution onto the uniform grid in order to make it comparable - // /////////////////////////////////////////////////////////////////////////////////////////// - - target.resize(targetBasis.size()); - - // handle each dof only once - Dune::BitSetVector<1> handled(targetBasis.size(), false); - - typename GridView::template Codim<0>::Iterator eIt = targetGrid.template leafbegin<0>(); - typename GridView::template Codim<0>::Iterator eEndIt = targetGrid.template leafend<0>(); - - // Loop over all dofs by looping over all elements - for (; eIt!=eEndIt; ++eIt) { - - const typename Basis::LocalFiniteElement& targetLFE = targetBasis.getLocalFiniteElement(*eIt); - - // Generate position of the Lagrange nodes - std::vector<Dune::FieldVector<double,dim> > lagrangeNodes(targetLFE.localBasis().size()); - - for (int i=0; i<dim; i++) { - CoordinateFunction lFunction(i); - std::vector<Dune::FieldVector<double,1> > coordinates; - targetLFE.localInterpolation().interpolate(lFunction, coordinates); - - for (size_t j=0; j<coordinates.size(); j++) - lagrangeNodes[j][i] = coordinates[j]; - - } - - for (size_t i=0; i<targetLFE.localCoefficients().size(); i++) { - - typename GridType::template Codim<0>::EntityPointer element(eIt); - - Dune::FieldVector<double,dim> pos = lagrangeNodes[i]; - - if (handled[targetBasis.index(*eIt,i)][0]) - continue; - - handled[targetBasis.index(*eIt,i)] = true; - - assert(checkInside(element->type(), pos, 1e-7)); - - // //////////////////////////////////////////////////////////////////// - // Get an element on the coarsest grid which contains the vertex and - // its local coordinates there - // //////////////////////////////////////////////////////////////////// - while (element->level() != 0){ - - pos = element->geometryInFather().global(pos); - element = element->father(); - - assert(checkInside(element->type(), pos, 1e-7)); - } - - // //////////////////////////////////////////////////////////////////// - // Find the corresponding coarse grid element on the adaptive grid. - // This is a linear algorithm, but we expect the coarse grid to be small. - // //////////////////////////////////////////////////////////////////// - Dune::LevelMultipleCodimMultipleGeomTypeMapper<GridType,Dune::MCMGElementLayout> uniformP0Mapper (targetGrid, 0); - Dune::LevelMultipleCodimMultipleGeomTypeMapper<GridType,Dune::MCMGElementLayout> adaptiveP0Mapper(sourceGrid, 0); - int coarseIndex = uniformP0Mapper.map(*element); - - typename GridType::template Codim<0>::LevelIterator adaptEIt = sourceGrid.template lbegin<0>(0); - typename GridType::template Codim<0>::LevelIterator adaptEEndIt = sourceGrid.template lend<0>(0); - - for (; adaptEIt!=adaptEEndIt; ++adaptEIt) - if (adaptiveP0Mapper.map(*adaptEIt) == coarseIndex) - break; - - element = adaptEIt; - - // //////////////////////////////////////////////////////////////////////// - // Find a corresponding point (not necessarily vertex) on the leaf level - // of the adaptive grid. - // //////////////////////////////////////////////////////////////////////// - - while (!element->isLeaf()) { - - - typename GridType::template Codim<0>::Entity::HierarchicIterator hIt = element->hbegin(element->level()+1); - typename GridType::template Codim<0>::Entity::HierarchicIterator hEndIt = element->hend(element->level()+1); - - Dune::FieldVector<double,dim> childPos; - assert(checkInside(element->type(), pos, 1e-7)); - - for (; hIt!=hEndIt; ++hIt) { - - childPos = hIt->geometryInFather().local(pos); - if (checkInside(hIt->type(), childPos, 1e-7)) - break; - - } - - assert(hIt!=hEndIt); - element = hIt; - pos = childPos; - - } - - // //////////////////////////////////////////////////////////////////////// - // Sample adaptive function - // //////////////////////////////////////////////////////////////////////// - sourceFunction.evaluateLocal(*element, pos, - target[targetBasis.index(*eIt,i)]); - - } - + static void interpolate(const GridType& sourceGrid, + const std::vector<TargetSpace>& source, + const GridType& targetGrid, + std::vector<TargetSpace>& target) + { + // Create a leaf function, which we need to be able to call 'evalall()' + Basis sourceBasis(sourceGrid.leafGridView()); + GlobalGeodesicFEFunction<Basis,TargetSpace> sourceFunction(sourceBasis, source); + + Basis targetBasis(targetGrid.leafGridView()); + + // /////////////////////////////////////////////////////////////////////////////////////////// + // Prolong the adaptive solution onto the uniform grid in order to make it comparable + // /////////////////////////////////////////////////////////////////////////////////////////// + + target.resize(targetBasis.size()); + + // handle each dof only once + Dune::BitSetVector<1> handled(targetBasis.size(), false); + + typename GridView::template Codim<0>::Iterator eIt = targetGrid.template leafbegin<0>(); + typename GridView::template Codim<0>::Iterator eEndIt = targetGrid.template leafend<0>(); + + // Loop over all dofs by looping over all elements + for (; eIt!=eEndIt; ++eIt) { + + const typename Basis::LocalFiniteElement& targetLFE = targetBasis.getLocalFiniteElement(*eIt); + + // Generate position of the Lagrange nodes + std::vector<Dune::FieldVector<double,dim> > lagrangeNodes(targetLFE.localBasis().size()); + + for (int i=0; i<dim; i++) { + CoordinateFunction lFunction(i); + std::vector<Dune::FieldVector<double,1> > coordinates; + targetLFE.localInterpolation().interpolate(lFunction, coordinates); + + for (size_t j=0; j<coordinates.size(); j++) + lagrangeNodes[j][i] = coordinates[j]; + + } + + for (size_t i=0; i<targetLFE.localCoefficients().size(); i++) { + + typename GridType::template Codim<0>::EntityPointer element(eIt); + + Dune::FieldVector<double,dim> pos = lagrangeNodes[i]; + + if (handled[targetBasis.index(*eIt,i)][0]) + continue; + + handled[targetBasis.index(*eIt,i)] = true; + + assert(checkInside(element->type(), pos, 1e-7)); + + // //////////////////////////////////////////////////////////////////// + // Get an element on the coarsest grid which contains the vertex and + // its local coordinates there + // //////////////////////////////////////////////////////////////////// + while (element->level() != 0) { + + pos = element->geometryInFather().global(pos); + element = element->father(); + + assert(checkInside(element->type(), pos, 1e-7)); } - - } + // //////////////////////////////////////////////////////////////////// + // Find the corresponding coarse grid element on the adaptive grid. + // This is a linear algorithm, but we expect the coarse grid to be small. + // //////////////////////////////////////////////////////////////////// + Dune::LevelMultipleCodimMultipleGeomTypeMapper<GridType,Dune::MCMGElementLayout> uniformP0Mapper (targetGrid, 0); + Dune::LevelMultipleCodimMultipleGeomTypeMapper<GridType,Dune::MCMGElementLayout> adaptiveP0Mapper(sourceGrid, 0); + int coarseIndex = uniformP0Mapper.map(*element); + + typename GridType::template Codim<0>::LevelIterator adaptEIt = sourceGrid.template lbegin<0>(0); + typename GridType::template Codim<0>::LevelIterator adaptEEndIt = sourceGrid.template lend<0>(0); + + for (; adaptEIt!=adaptEEndIt; ++adaptEIt) + if (adaptiveP0Mapper.map(*adaptEIt) == coarseIndex) + break; + + element = adaptEIt; + + // //////////////////////////////////////////////////////////////////////// + // Find a corresponding point (not necessarily vertex) on the leaf level + // of the adaptive grid. + // //////////////////////////////////////////////////////////////////////// + + while (!element->isLeaf()) { + + + typename GridType::template Codim<0>::Entity::HierarchicIterator hIt = element->hbegin(element->level()+1); + typename GridType::template Codim<0>::Entity::HierarchicIterator hEndIt = element->hend(element->level()+1); + + Dune::FieldVector<double,dim> childPos; + assert(checkInside(element->type(), pos, 1e-7)); + + for (; hIt!=hEndIt; ++hIt) { + + childPos = hIt->geometryInFather().local(pos); + if (checkInside(hIt->type(), childPos, 1e-7)) + break; + + } + + assert(hIt!=hEndIt); + element = hIt; + pos = childPos; - template <int blocksize> - static double computeNormSquared(const GridType& grid, - const Dune::BlockVector<Dune::FieldVector<double,blocksize> >& x, - const LocalOperatorAssembler<GridType, - typename Basis::LocalFiniteElement, - typename Basis::LocalFiniteElement, - Dune::FieldMatrix<double,1,1> >* localStiffness) - { - // /////////////////////////////////////////////////////////////////////////////////////////// - // Compute the energy norm - // /////////////////////////////////////////////////////////////////////////////////////////// - - double energyNormSquared = 0; - Basis basis(grid.leafGridView()); - Dune::Matrix<Dune::FieldMatrix<double,1,1> > localMatrix; - - typename GridType::template Codim<0>::LeafIterator eIt = grid.template leafbegin<0>(); - typename GridType::template Codim<0>::LeafIterator eEndIt = grid.template leafend<0>(); - - for (; eIt!=eEndIt; ++eIt) { - - size_t nLocalDofs = basis.getLocalFiniteElement(*eIt).localCoefficients().size(); - - localMatrix.setSize(nLocalDofs,nLocalDofs); - - localStiffness->assemble(*eIt, localMatrix, - basis.getLocalFiniteElement(*eIt), - basis.getLocalFiniteElement(*eIt)); - - for (size_t i=0; i<nLocalDofs; i++) - for (size_t j=0; j<nLocalDofs; j++) - energyNormSquared += localMatrix[i][j][0][0] * (x[basis.index(*eIt,i)] * x[basis.index(*eIt,j)]); - } - - return energyNormSquared; + + // //////////////////////////////////////////////////////////////////////// + // Sample adaptive function + // //////////////////////////////////////////////////////////////////////// + sourceFunction.evaluateLocal(*element, pos, + target[targetBasis.index(*eIt,i)]); + + } + } - static double compute(const GridType& uniformGrid, - const std::vector<TargetSpace>& uniformSolution, - const GridType& adaptiveGrid, - const std::vector<TargetSpace>& adaptiveSolution, - const LocalOperatorAssembler<GridType, - typename Basis::LocalFiniteElement, - typename Basis::LocalFiniteElement, - Dune::FieldMatrix<double,1,1> >* localStiffness) - { - std::vector<TargetSpace> uniformAdaptiveSolution; - - interpolate(adaptiveGrid, adaptiveSolution, uniformGrid, uniformAdaptiveSolution); - - // Compute difference in the embedding space - Dune::BlockVector<typename TargetSpace::CoordinateType> difference(uniformSolution.size()); - - for (size_t i=0; i<difference.size(); i++) - difference[i] = uniformAdaptiveSolution[i].globalCoordinates() - uniformSolution[i].globalCoordinates(); - - //std::cout << "new difference:\n" << difference << std::endl; -// std::cout << "new projected:\n" << std::endl; -// for (size_t i=0; i<difference.size(); i++) -// std::cout << uniformAdaptiveSolution[i].globalCoordinates() << std::endl; - - return computeNormSquared(uniformGrid, difference, localStiffness); + } + + + template <int blocksize> + static double computeNormSquared(const GridType& grid, + const Dune::BlockVector<Dune::FieldVector<double,blocksize> >& x, + const LocalOperatorAssembler<GridType, + typename Basis::LocalFiniteElement, + typename Basis::LocalFiniteElement, + Dune::FieldMatrix<double,1,1> >* localStiffness) + { + // /////////////////////////////////////////////////////////////////////////////////////////// + // Compute the energy norm + // /////////////////////////////////////////////////////////////////////////////////////////// + + double energyNormSquared = 0; + Basis basis(grid.leafGridView()); + Dune::Matrix<Dune::FieldMatrix<double,1,1> > localMatrix; + + typename GridType::template Codim<0>::LeafIterator eIt = grid.template leafbegin<0>(); + typename GridType::template Codim<0>::LeafIterator eEndIt = grid.template leafend<0>(); + + for (; eIt!=eEndIt; ++eIt) { + + size_t nLocalDofs = basis.getLocalFiniteElement(*eIt).localCoefficients().size(); + + localMatrix.setSize(nLocalDofs,nLocalDofs); + + localStiffness->assemble(*eIt, localMatrix, + basis.getLocalFiniteElement(*eIt), + basis.getLocalFiniteElement(*eIt)); + + for (size_t i=0; i<nLocalDofs; i++) + for (size_t j=0; j<nLocalDofs; j++) + energyNormSquared += localMatrix[i][j][0][0] * (x[basis.index(*eIt,i)] * x[basis.index(*eIt,j)]); + } + + return energyNormSquared; + } + + static double compute(const GridType& uniformGrid, + const std::vector<TargetSpace>& uniformSolution, + const GridType& adaptiveGrid, + const std::vector<TargetSpace>& adaptiveSolution, + const LocalOperatorAssembler<GridType, + typename Basis::LocalFiniteElement, + typename Basis::LocalFiniteElement, + Dune::FieldMatrix<double,1,1> >* localStiffness) + { + std::vector<TargetSpace> uniformAdaptiveSolution; + + interpolate(adaptiveGrid, adaptiveSolution, uniformGrid, uniformAdaptiveSolution); + + // Compute difference in the embedding space + Dune::BlockVector<typename TargetSpace::CoordinateType> difference(uniformSolution.size()); + + for (size_t i=0; i<difference.size(); i++) + difference[i] = uniformAdaptiveSolution[i].globalCoordinates() - uniformSolution[i].globalCoordinates(); + + //std::cout << "new difference:\n" << difference << std::endl; + // std::cout << "new projected:\n" << std::endl; + // for (size_t i=0; i<difference.size(); i++) + // std::cout << uniformAdaptiveSolution[i].globalCoordinates() << std::endl; + + return computeNormSquared(uniformGrid, difference, localStiffness); + } }; #endif diff --git a/dune/gfe/globalgfefunction.hh b/dune/gfe/globalgfefunction.hh index 9cc1f814..cc219ca0 100644 --- a/dune/gfe/globalgfefunction.hh +++ b/dune/gfe/globalgfefunction.hh @@ -22,525 +22,525 @@ namespace Dune::GFE { -namespace Impl { + namespace Impl { #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - // This collects all data that is shared by all related - // global and local functions. - template <typename Basis, typename Vector> - struct Data - { - using GridView = typename Basis::GridView; - using EntitySet = Functions::GridViewEntitySet<GridView, 0>; - EntitySet entitySet; - std::shared_ptr<const Basis> basis; - std::shared_ptr<const Vector> coefficients; - }; + // This collects all data that is shared by all related + // global and local functions. + template <typename Basis, typename Vector> + struct Data + { + using GridView = typename Basis::GridView; + using EntitySet = Functions::GridViewEntitySet<GridView, 0>; + EntitySet entitySet; + std::shared_ptr<const Basis> basis; + std::shared_ptr<const Vector> coefficients; + }; #endif -/** \brief Common base class for GlobalGFEFunction and its derivative - * - * \tparam B Scalar(!) function-space basis - * \tparam V Container of coefficients - * \tparam LocalInterpolationRule How to interpolate manifold-valued data - */ + /** \brief Common base class for GlobalGFEFunction and its derivative + * + * \tparam B Scalar(!) function-space basis + * \tparam V Container of coefficients + * \tparam LocalInterpolationRule How to interpolate manifold-valued data + */ #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) -template<typename B, typename V, typename LocalInterpolationRule, typename Range> -class GlobalGFEFunctionBase -: public VirtualGridViewFunction<typename B::GridView, Range> + template<typename B, typename V, typename LocalInterpolationRule, typename Range> + class GlobalGFEFunctionBase + : public VirtualGridViewFunction<typename B::GridView, Range> #else -template<typename B, typename V, typename LocalInterpolationRule> -class GlobalGFEFunctionBase + template<typename B, typename V, typename LocalInterpolationRule> + class GlobalGFEFunctionBase #endif -{ -public: - using Basis = B; - using Vector = V; + { + public: + using Basis = B; + using Vector = V; - // In order to make the cache work for proxy-references - // we have to use AutonomousValue<T> instead of std::decay_t<T> - using Coefficient = Dune::AutonomousValue<decltype(std::declval<Vector>()[std::declval<typename Basis::MultiIndex>()])>; + // In order to make the cache work for proxy-references + // we have to use AutonomousValue<T> instead of std::decay_t<T> + using Coefficient = Dune::AutonomousValue<decltype(std::declval<Vector>()[std::declval<typename Basis::MultiIndex>()])>; - using GridView = typename Basis::GridView; - using EntitySet = Functions::GridViewEntitySet<GridView, 0>; - using Tree = typename Basis::LocalView::Tree; + using GridView = typename Basis::GridView; + using EntitySet = Functions::GridViewEntitySet<GridView, 0>; + using Tree = typename Basis::LocalView::Tree; - using Domain = typename EntitySet::GlobalCoordinate; + using Domain = typename EntitySet::GlobalCoordinate; - using LocalDomain = typename EntitySet::LocalCoordinate; - using Element = typename EntitySet::Element; + using LocalDomain = typename EntitySet::LocalCoordinate; + using Element = typename EntitySet::Element; -protected: + protected: #if DUNE_VERSION_GT(DUNE_FUFEM, 2, 9) - // This collects all data that is shared by all related - // global and local functions. This way we don't need to - // keep track of it individually. - struct Data - { - EntitySet entitySet; - std::shared_ptr<const Basis> basis; - std::shared_ptr<const Vector> coefficients; - }; + // This collects all data that is shared by all related + // global and local functions. This way we don't need to + // keep track of it individually. + struct Data + { + EntitySet entitySet; + std::shared_ptr<const Basis> basis; + std::shared_ptr<const Vector> coefficients; + }; #endif -public: - class LocalFunctionBase - { - using LocalView = typename Basis::LocalView; - using size_type = typename Tree::size_type; + public: + class LocalFunctionBase + { + using LocalView = typename Basis::LocalView; + using size_type = typename Tree::size_type; - public: - using Domain = LocalDomain; - using Element = typename EntitySet::Element; + public: + using Domain = LocalDomain; + using Element = typename EntitySet::Element; - protected: + protected: #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - LocalFunctionBase(const std::shared_ptr<const Data<Basis,Vector>>& data) + LocalFunctionBase(const std::shared_ptr<const Data<Basis,Vector> >& data) #else - LocalFunctionBase(const std::shared_ptr<const Data>& data) + LocalFunctionBase(const std::shared_ptr<const Data>& data) #endif - : data_(data) - , localView_(data_->basis->localView()) - { - localDoFs_.reserve(localView_.maxSize()); - } - - /** - * \brief Copy-construct the local-function. - * - * This copy-constructor copies the cached local DOFs only - * if the `other` local-function is bound to an element. - **/ - LocalFunctionBase(const LocalFunctionBase& other) - : data_(other.data_) - , localView_(other.localView_) - { - localDoFs_.reserve(localView_.maxSize()); - if (bound()) - localDoFs_ = other.localDoFs_; - } - - /** - * \brief Copy-assignment of the local-function. - * - * Assign all members from `other` to `this`, except the - * local DOFs. Those are copied only if the `other` - * local-function is bound to an element. - **/ - LocalFunctionBase& operator=(const LocalFunctionBase& other) - { - data_ = other.data_; - localView_ = other.localView_; - if (bound()) - localDoFs_ = other.localDoFs_; - return *this; - } - - public: - /** - * \brief Bind LocalFunction to grid element. - * - * You must call this method before `operator()` - * and after changes to the coefficient vector. - */ - void bind(const Element& element) - { - localView_.bind(element); - - localDoFs_.resize(localView_.size()); - const auto& dofs = *data_->coefficients; - for (size_type i = 0; i < localView_.tree().size(); ++i) - { - // For a subspace basis the index-within-tree i - // is not the same as the localIndex within the - // full local view. - size_t localIndex = localView_.tree().localIndex(i); - localDoFs_[localIndex] = dofs[localView_.index(localIndex)]; - } - - // create local GFE function - // TODO Store this object by value - localInterpolationRule_ = std::make_unique<LocalInterpolationRule>(this->localView_.tree().finiteElement(),localDoFs_); - } - - //! Unbind the local-function. - void unbind() - { - localView_.unbind(); - } - - //! Check if LocalFunction is already bound to an element. - bool bound() const - { - return localView_.bound(); - } - - //! Return the element the local-function is bound to. - const Element& localContext() const - { - return localView_.element(); - } - - protected: + : data_(data) + , localView_(data_->basis->localView()) + { + localDoFs_.reserve(localView_.maxSize()); + } + + /** + * \brief Copy-construct the local-function. + * + * This copy-constructor copies the cached local DOFs only + * if the `other` local-function is bound to an element. + **/ + LocalFunctionBase(const LocalFunctionBase& other) + : data_(other.data_) + , localView_(other.localView_) + { + localDoFs_.reserve(localView_.maxSize()); + if (bound()) + localDoFs_ = other.localDoFs_; + } + + /** + * \brief Copy-assignment of the local-function. + * + * Assign all members from `other` to `this`, except the + * local DOFs. Those are copied only if the `other` + * local-function is bound to an element. + **/ + LocalFunctionBase& operator=(const LocalFunctionBase& other) + { + data_ = other.data_; + localView_ = other.localView_; + if (bound()) + localDoFs_ = other.localDoFs_; + return *this; + } + + public: + /** + * \brief Bind LocalFunction to grid element. + * + * You must call this method before `operator()` + * and after changes to the coefficient vector. + */ + void bind(const Element& element) + { + localView_.bind(element); + + localDoFs_.resize(localView_.size()); + const auto& dofs = *data_->coefficients; + for (size_type i = 0; i < localView_.tree().size(); ++i) + { + // For a subspace basis the index-within-tree i + // is not the same as the localIndex within the + // full local view. + size_t localIndex = localView_.tree().localIndex(i); + localDoFs_[localIndex] = dofs[localView_.index(localIndex)]; + } + + // create local GFE function + // TODO Store this object by value + localInterpolationRule_ = std::make_unique<LocalInterpolationRule>(this->localView_.tree().finiteElement(),localDoFs_); + } + + //! Unbind the local-function. + void unbind() + { + localView_.unbind(); + } + + //! Check if LocalFunction is already bound to an element. + bool bound() const + { + return localView_.bound(); + } + + //! Return the element the local-function is bound to. + const Element& localContext() const + { + return localView_.element(); + } + + protected: #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - std::shared_ptr<const Data<Basis,Vector> > data_; + std::shared_ptr<const Data<Basis,Vector> > data_; #else - std::shared_ptr<const Data> data_; + std::shared_ptr<const Data> data_; #endif - LocalView localView_; - std::vector<Coefficient> localDoFs_; - std::unique_ptr<LocalInterpolationRule> localInterpolationRule_; - }; + LocalView localView_; + std::vector<Coefficient> localDoFs_; + std::unique_ptr<LocalInterpolationRule> localInterpolationRule_; + }; -protected: + protected: #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - GlobalGFEFunctionBase(const std::shared_ptr<const Data<Basis,Vector>>& data) - : VirtualGridViewFunction<typename B::GridView, Range>(data->basis->gridView()) - , data_(data) + GlobalGFEFunctionBase(const std::shared_ptr<const Data<Basis,Vector> >& data) + : VirtualGridViewFunction<typename B::GridView, Range>(data->basis->gridView()) + , data_(data) #else - GlobalGFEFunctionBase(const std::shared_ptr<const Data>& data) - : data_(data) + GlobalGFEFunctionBase(const std::shared_ptr<const Data>& data) + : data_(data) #endif - { - /* Nothing. */ - } + { + /* Nothing. */ + } -public: + public: - //! Return a const reference to the stored basis. - const Basis& basis() const - { - return *data_->basis; - } + //! Return a const reference to the stored basis. + const Basis& basis() const + { + return *data_->basis; + } - //! Return the coefficients of this discrete function by reference. - const Vector& dofs() const - { - return *data_->coefficients; - } + //! Return the coefficients of this discrete function by reference. + const Vector& dofs() const + { + return *data_->coefficients; + } - //! Get associated set of entities the local-function can be bound to. - const EntitySet& entitySet() const - { - return data_->entitySet; - } + //! Get associated set of entities the local-function can be bound to. + const EntitySet& entitySet() const + { + return data_->entitySet; + } -protected: + protected: #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - std::shared_ptr<const Data<Basis, Vector> > data_; + std::shared_ptr<const Data<Basis, Vector> > data_; #else - std::shared_ptr<const Data> data_; + std::shared_ptr<const Data> data_; #endif -}; + }; -} // namespace Impl + } // namespace Impl -template<typename GGF> -class GlobalGFEFunctionDerivative; + template<typename GGF> + class GlobalGFEFunctionDerivative; -/** - * \brief A global geometric finite element function - * - * \tparam B Type of global basis - * \tparam LIR Local interpolation rule for manifold-valued data - * \tparam TargetSpace Range type of this function - */ -template<typename B, typename LIR, typename TargetSpace> -class GlobalGFEFunction + /** + * \brief A global geometric finite element function + * + * \tparam B Type of global basis + * \tparam LIR Local interpolation rule for manifold-valued data + * \tparam TargetSpace Range type of this function + */ + template<typename B, typename LIR, typename TargetSpace> + class GlobalGFEFunction #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - : public Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR, typename TargetSpace::CoordinateType> + : public Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR, typename TargetSpace::CoordinateType> #else - : public Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR> + : public Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR> #endif -{ + { #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Base = Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR, typename TargetSpace::CoordinateType>; + using Base = Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR, typename TargetSpace::CoordinateType>; #else - using Base = Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR>; - using Data = typename Base::Data; + using Base = Impl::GlobalGFEFunctionBase<B, std::vector<TargetSpace>, LIR>; + using Data = typename Base::Data; #endif -public: - using Basis = typename Base::Basis; - using Vector = typename Base::Vector; + public: + using Basis = typename Base::Basis; + using Vector = typename Base::Vector; #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Data = typename Impl::Data<Basis,Vector>; + using Data = typename Impl::Data<Basis,Vector>; #endif - using LocalInterpolationRule = LIR; + using LocalInterpolationRule = LIR; - using Domain = typename Base::Domain; - using Range = typename TargetSpace::CoordinateType; + using Domain = typename Base::Domain; + using Range = typename TargetSpace::CoordinateType; - using Traits = Functions::Imp::GridFunctionTraits<Range(Domain), typename Base::EntitySet, Functions::DefaultDerivativeTraits, 16>; + using Traits = Functions::Imp::GridFunctionTraits<Range(Domain), typename Base::EntitySet, Functions::DefaultDerivativeTraits, 16>; - class LocalFunction - : public Base::LocalFunctionBase - { - using LocalBase = typename Base::LocalFunctionBase; - using size_type = typename Base::Tree::size_type; + class LocalFunction + : public Base::LocalFunctionBase + { + using LocalBase = typename Base::LocalFunctionBase; + using size_type = typename Base::Tree::size_type; - public: + public: - using GlobalFunction = GlobalGFEFunction; - using Domain = typename LocalBase::Domain; - using Range = GlobalFunction::Range; - using Element = typename LocalBase::Element; + using GlobalFunction = GlobalGFEFunction; + using Domain = typename LocalBase::Domain; + using Range = GlobalFunction::Range; + using Element = typename LocalBase::Element; + + //! Create a local-function from the associated grid-function + LocalFunction(const GlobalGFEFunction& globalFunction) + : LocalBase(globalFunction.data_) + { + /* Nothing. */ + } - //! Create a local-function from the associated grid-function - LocalFunction(const GlobalGFEFunction& globalFunction) - : LocalBase(globalFunction.data_) + /** + * \brief Evaluate this local-function in coordinates `x` in the bound element. + * + * The result of this method is undefined if you did + * not call bind() beforehand or changed the coefficient + * vector after the last call to bind(). In the latter case + * you have to call bind() again in order to make operator() + * usable. + */ + Range operator()(const Domain& x) const + { + return this->localInterpolationRule_->evaluate(x).globalCoordinates(); + } + + //! Local function of the derivative + friend typename GlobalGFEFunctionDerivative<GlobalGFEFunction>::LocalFunction derivative(const LocalFunction& lf) + { + auto dlf = localFunction(GlobalGFEFunctionDerivative<GlobalGFEFunction>(lf.data_)); + if (lf.bound()) + dlf.bind(lf.localContext()); + return dlf; + } + }; + + //! Create a grid-function, by wrapping the arguments in `std::shared_ptr`. + template<class B_T, class V_T> + GlobalGFEFunction(B_T && basis, V_T && coefficients) + : Base(std::make_shared<Data>(Data{{basis.gridView()}, wrap_or_move(std::forward<B_T>(basis)), wrap_or_move(std::forward<V_T>(coefficients))})) + {} + + //! Create a grid-function, by moving the arguments in `std::shared_ptr`. + GlobalGFEFunction(std::shared_ptr<const Basis> basis, std::shared_ptr<const Vector> coefficients) + : Base(std::make_shared<Data>(Data{{basis->gridView()}, basis, coefficients})) + {} + + /** \brief Evaluate at a point given in world coordinates + * + * \warning This has to find the element that the evaluation point is in. + * It is therefore very slow. + */ + Range operator() (const Domain& x) const { - /* Nothing. */ + HierarchicSearch search(this->data_->basis->gridView().grid(), this->data_->basis->gridView().indexSet()); + + const auto e = search.findEntity(x); + auto localThis = localFunction(*this); + localThis.bind(e); + return localThis(e.geometry().local(x)); + } + + //! Derivative of the `GlobalGFEFunction` + friend GlobalGFEFunctionDerivative<GlobalGFEFunction> derivative(const GlobalGFEFunction& f) + { + return GlobalGFEFunctionDerivative<GlobalGFEFunction>(f.data_); } /** - * \brief Evaluate this local-function in coordinates `x` in the bound element. + * \brief Construct local function from a GlobalGFEFunction. * - * The result of this method is undefined if you did - * not call bind() beforehand or changed the coefficient - * vector after the last call to bind(). In the latter case - * you have to call bind() again in order to make operator() - * usable. + * The obtained local function satisfies the concept + * `Dune::Functions::Concept::LocalFunction`. It must be bound + * to an entity from the entity set of the GlobalGFEFunction + * before it can be used. */ - Range operator()(const Domain& x) const + friend LocalFunction localFunction(const GlobalGFEFunction& t) { - return this->localInterpolationRule_->evaluate(x).globalCoordinates(); + return LocalFunction(t); } - //! Local function of the derivative - friend typename GlobalGFEFunctionDerivative<GlobalGFEFunction>::LocalFunction derivative(const LocalFunction& lf) +#if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) + using Element = typename Basis::GridView::template Codim<0>::Entity; + /** \brief Evaluate the function at local coordinates. */ + void evaluateLocal(const Element& element, const Domain& local, typename TargetSpace::CoordinateType& out) const { - auto dlf = localFunction(GlobalGFEFunctionDerivative<GlobalGFEFunction>(lf.data_)); - if (lf.bound()) - dlf.bind(lf.localContext()); - return dlf; + DUNE_THROW(NotImplemented, "!"); } +#endif }; - //! Create a grid-function, by wrapping the arguments in `std::shared_ptr`. - template<class B_T, class V_T> - GlobalGFEFunction(B_T && basis, V_T && coefficients) - : Base(std::make_shared<Data>(Data{{basis.gridView()}, wrap_or_move(std::forward<B_T>(basis)), wrap_or_move(std::forward<V_T>(coefficients))})) - {} - - //! Create a grid-function, by moving the arguments in `std::shared_ptr`. - GlobalGFEFunction(std::shared_ptr<const Basis> basis, std::shared_ptr<const Vector> coefficients) - : Base(std::make_shared<Data>(Data{{basis->gridView()}, basis, coefficients})) - {} - - /** \brief Evaluate at a point given in world coordinates - * - * \warning This has to find the element that the evaluation point is in. - * It is therefore very slow. - */ - Range operator() (const Domain& x) const - { - HierarchicSearch search(this->data_->basis->gridView().grid(), this->data_->basis->gridView().indexSet()); - - const auto e = search.findEntity(x); - auto localThis = localFunction(*this); - localThis.bind(e); - return localThis(e.geometry().local(x)); - } - - //! Derivative of the `GlobalGFEFunction` - friend GlobalGFEFunctionDerivative<GlobalGFEFunction> derivative(const GlobalGFEFunction& f) - { - return GlobalGFEFunctionDerivative<GlobalGFEFunction>(f.data_); - } /** - * \brief Construct local function from a GlobalGFEFunction. + * \brief Derivative of a `GlobalGFEFunction` * - * The obtained local function satisfies the concept - * `Dune::Functions::Concept::LocalFunction`. It must be bound - * to an entity from the entity set of the GlobalGFEFunction - * before it can be used. + * Function returning the derivative of the given `GlobalGFEFunction` + * with respect to global coordinates. + * + * \tparam GGF instance of the `GlobalGFEFunction` this is a derivative of */ - friend LocalFunction localFunction(const GlobalGFEFunction& t) - { - return LocalFunction(t); - } - -#if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Element = typename Basis::GridView::template Codim<0>::Entity; - /** \brief Evaluate the function at local coordinates. */ - void evaluateLocal(const Element& element, const Domain& local, typename TargetSpace::CoordinateType& out) const - { - DUNE_THROW(NotImplemented, "!"); - } -#endif -}; - - -/** - * \brief Derivative of a `GlobalGFEFunction` - * - * Function returning the derivative of the given `GlobalGFEFunction` - * with respect to global coordinates. - * - * \tparam GGF instance of the `GlobalGFEFunction` this is a derivative of - */ -template<typename GGF> -class GlobalGFEFunctionDerivative + template<typename GGF> + class GlobalGFEFunctionDerivative #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - : public Impl::GlobalGFEFunctionBase<typename GGF::Basis, typename GGF::Vector, typename GGF::LocalInterpolationRule, - Dune::FieldMatrix<double, GGF::Vector::value_type::EmbeddedTangentVector::dimension, GGF::Basis::GridView::dimensionworld> > + : public Impl::GlobalGFEFunctionBase<typename GGF::Basis, typename GGF::Vector, typename GGF::LocalInterpolationRule, + Dune::FieldMatrix<double, GGF::Vector::value_type::EmbeddedTangentVector::dimension, GGF::Basis::GridView::dimensionworld> > #else - : public Impl::GlobalGFEFunctionBase<typename GGF::Basis, typename GGF::Vector, typename GGF::LocalInterpolationRule> + : public Impl::GlobalGFEFunctionBase<typename GGF::Basis, typename GGF::Vector, typename GGF::LocalInterpolationRule> #endif -{ + { #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Base = Impl::GlobalGFEFunctionBase<typename GGF::Basis, typename GGF::Vector, typename GGF::LocalInterpolationRule, - Dune::FieldMatrix<double, GGF::Vector::value_type::EmbeddedTangentVector::dimension, GGF::Basis::GridView::dimensionworld> >; + using Base = Impl::GlobalGFEFunctionBase<typename GGF::Basis, typename GGF::Vector, typename GGF::LocalInterpolationRule, + Dune::FieldMatrix<double, GGF::Vector::value_type::EmbeddedTangentVector::dimension, GGF::Basis::GridView::dimensionworld> >; #else - using Base = Impl::GlobalGFEFunctionBase<typename GGF::Basis, typename GGF::Vector, typename GGF::LocalInterpolationRule>; - using Data = typename Base::Data; + using Base = Impl::GlobalGFEFunctionBase<typename GGF::Basis, typename GGF::Vector, typename GGF::LocalInterpolationRule>; + using Data = typename Base::Data; #endif -public: - using GlobalGFEFunction = GGF; + public: + using GlobalGFEFunction = GGF; - using Basis = typename Base::Basis; - using Vector = typename Base::Vector; + using Basis = typename Base::Basis; + using Vector = typename Base::Vector; #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Data = typename Impl::Data<Basis,Vector>; + using Data = typename Impl::Data<Basis,Vector>; #endif - using Domain = typename Base::Domain; - using Range = typename Functions::SignatureTraits<typename GlobalGFEFunction::Traits::DerivativeInterface>::Range; - - using Traits = Functions::Imp::GridFunctionTraits<Range(Domain), typename Base::EntitySet, Functions::DefaultDerivativeTraits, 16>; - - /** - * \brief local function evaluating the derivative in reference coordinates - * - * Note that the function returns the derivative with respect to global - * coordinates even when the point is given in reference coordinates on - * an element. - */ - class LocalFunction - : public Base::LocalFunctionBase - { - using LocalBase = typename Base::LocalFunctionBase; - using size_type = typename Base::Tree::size_type; + using Domain = typename Base::Domain; + using Range = typename Functions::SignatureTraits<typename GlobalGFEFunction::Traits::DerivativeInterface>::Range; - public: - using GlobalFunction = GlobalGFEFunctionDerivative; - using Domain = typename LocalBase::Domain; - using Range = GlobalFunction::Range; - using Element = typename LocalBase::Element; - - //! Create a local function from the associated grid function - LocalFunction(const GlobalFunction& globalFunction) - : LocalBase(globalFunction.data_) - { - /* Nothing. */ - } + using Traits = Functions::Imp::GridFunctionTraits<Range(Domain), typename Base::EntitySet, Functions::DefaultDerivativeTraits, 16>; /** - * \brief Bind LocalFunction to grid element. + * \brief local function evaluating the derivative in reference coordinates * - * You must call this method before `operator()` - * and after changes to the coefficient vector. + * Note that the function returns the derivative with respect to global + * coordinates even when the point is given in reference coordinates on + * an element. */ - void bind(const Element& element) + class LocalFunction + : public Base::LocalFunctionBase { - LocalBase::bind(element); - geometry_.emplace(element.geometry()); - } + using LocalBase = typename Base::LocalFunctionBase; + using size_type = typename Base::Tree::size_type; + + public: + using GlobalFunction = GlobalGFEFunctionDerivative; + using Domain = typename LocalBase::Domain; + using Range = GlobalFunction::Range; + using Element = typename LocalBase::Element; + + //! Create a local function from the associated grid function + LocalFunction(const GlobalFunction& globalFunction) + : LocalBase(globalFunction.data_) + { + /* Nothing. */ + } - //! Unbind the local-function. - void unbind() - { - geometry_.reset(); - LocalBase::unbind(); - } + /** + * \brief Bind LocalFunction to grid element. + * + * You must call this method before `operator()` + * and after changes to the coefficient vector. + */ + void bind(const Element& element) + { + LocalBase::bind(element); + geometry_.emplace(element.geometry()); + } + + //! Unbind the local-function. + void unbind() + { + geometry_.reset(); + LocalBase::unbind(); + } + + /** + * \brief Evaluate this local-function in coordinates `x` in the bound element. + * + * The result of this method is undefined if you did + * not call bind() beforehand or changed the coefficient + * vector after the last call to bind(). In the latter case + * you have to call bind() again in order to make operator() + * usable. + * + * Note that the function returns the derivative with respect to global + * coordinates even though the evaluation point is given in reference coordinates + * on the current element. + */ + Range operator()(const Domain& x) const + { + // Jacobian with respect to local coordinates + auto refJac = this->localInterpolationRule_->evaluateDerivative(x); + + // Transform to world coordinates + return refJac * geometry_->jacobianInverse(x); + } + + //! Not implemented + friend typename Traits::LocalFunctionTraits::DerivativeInterface derivative(const LocalFunction&) + { + DUNE_THROW(NotImplemented, "derivative of derivative is not implemented"); + } + + private: + std::optional<typename Element::Geometry> geometry_; + }; /** - * \brief Evaluate this local-function in coordinates `x` in the bound element. + * \brief create object from `GlobalGFEFunction` data * - * The result of this method is undefined if you did - * not call bind() beforehand or changed the coefficient - * vector after the last call to bind(). In the latter case - * you have to call bind() again in order to make operator() - * usable. + * Please call `derivative(globalGFEFunction)` to create an instance + * of this class. + */ + GlobalGFEFunctionDerivative(const std::shared_ptr<const Data>& data) + : Base(data) + { + /* Nothing. */ + } + + /** \brief Evaluate the discrete grid-function derivative in global coordinates * - * Note that the function returns the derivative with respect to global - * coordinates even though the evaluation point is given in reference coordinates - * on the current element. + * \warning This has to find the element that the evaluation point is in. + * It is therefore very slow. */ Range operator()(const Domain& x) const { - // Jacobian with respect to local coordinates - auto refJac = this->localInterpolationRule_->evaluateDerivative(x); + HierarchicSearch search(this->data_->basis->gridView().grid(), this->data_->basis->gridView().indexSet()); - // Transform to world coordinates - return refJac * geometry_->jacobianInverse(x); + const auto e = search.findEntity(x); + auto localThis = localFunction(*this); + localThis.bind(e); + return localThis(e.geometry().local(x)); } - //! Not implemented - friend typename Traits::LocalFunctionTraits::DerivativeInterface derivative(const LocalFunction&) + friend typename Traits::DerivativeInterface derivative(const GlobalGFEFunctionDerivative& f) { DUNE_THROW(NotImplemented, "derivative of derivative is not implemented"); } - private: - std::optional<typename Element::Geometry> geometry_; - }; - - /** - * \brief create object from `GlobalGFEFunction` data - * - * Please call `derivative(globalGFEFunction)` to create an instance - * of this class. - */ - GlobalGFEFunctionDerivative(const std::shared_ptr<const Data>& data) - : Base(data) - { - /* Nothing. */ - } - - /** \brief Evaluate the discrete grid-function derivative in global coordinates - * - * \warning This has to find the element that the evaluation point is in. - * It is therefore very slow. - */ - Range operator()(const Domain& x) const - { - HierarchicSearch search(this->data_->basis->gridView().grid(), this->data_->basis->gridView().indexSet()); - - const auto e = search.findEntity(x); - auto localThis = localFunction(*this); - localThis.bind(e); - return localThis(e.geometry().local(x)); - } - - friend typename Traits::DerivativeInterface derivative(const GlobalGFEFunctionDerivative& f) - { - DUNE_THROW(NotImplemented, "derivative of derivative is not implemented"); - } - - //! Construct local function from a `GlobalGFEFunctionDerivative` - friend LocalFunction localFunction(const GlobalGFEFunctionDerivative& f) - { - return LocalFunction(f); - } + //! Construct local function from a `GlobalGFEFunctionDerivative` + friend LocalFunction localFunction(const GlobalGFEFunctionDerivative& f) + { + return LocalFunction(f); + } #if DUNE_VERSION_LTE(DUNE_FUFEM, 2, 9) - using Element = typename Basis::GridView::template Codim<0>::Entity; - /** \brief Evaluate the function at local coordinates. */ - void evaluateLocal(const Element& element, const Domain& local, Range& out) const override - { - // This method will never be called. - } + using Element = typename Basis::GridView::template Codim<0>::Entity; + /** \brief Evaluate the function at local coordinates. */ + void evaluateLocal(const Element& element, const Domain& local, Range& out) const override + { + // This method will never be called. + } #endif -}; + }; } // namespace Dune::GFE diff --git a/dune/gfe/gramschmidtsolver.hh b/dune/gfe/gramschmidtsolver.hh index 202fd21b..69104265 100644 --- a/dune/gfe/gramschmidtsolver.hh +++ b/dune/gfe/gramschmidtsolver.hh @@ -61,7 +61,7 @@ public: */ GramSchmidtSolver(const Dune::SymmetricMatrix<field_type,embeddedDim>& matrix, const Dune::FieldMatrix<field_type,rank,embeddedDim>& basis) - : orthonormalBasis_(basis) + : orthonormalBasis_(basis) { // Use the Gram-Schmidt algorithm to compute a basis that is orthonormal // with respect to the given matrix. diff --git a/dune/gfe/linearalgebra.hh b/dune/gfe/linearalgebra.hh index 2d6f7433..a7a6ea7e 100644 --- a/dune/gfe/linearalgebra.hh +++ b/dune/gfe/linearalgebra.hh @@ -17,13 +17,13 @@ namespace Dune { namespace GFE { #if ADOLC_ADOUBLE_H - /** \brief Calculates ret = s*A, where A has as field_type of adouble. - * - * The function template is disabled if s isn't a scalar or adolc type. - */ - template<typename T1,int m, int n,class = typename std::enable_if< std::is_scalar_v<T1> || std::is_base_of_v<badouble,T1> >::type > - auto operator* ( const T1& s, const Dune::FieldMatrix<adouble, m, n> &A) - { + /** \brief Calculates ret = s*A, where A has as field_type of adouble. + * + * The function template is disabled if s isn't a scalar or adolc type. + */ + template<typename T1,int m, int n,class = typename std::enable_if< std::is_scalar_v<T1> || std::is_base_of_v<badouble,T1> >::type > + auto operator* ( const T1& s, const Dune::FieldMatrix<adouble, m, n> &A) + { typedef typename Dune::FieldMatrix<adouble,m,n> :: size_type size_type; Dune::FieldMatrix<adouble,m,n> ret; @@ -32,43 +32,43 @@ namespace Dune { ret[i][j] = s * A[i][j]; return ret; - } + } - /** \brief Calculates ret = A*v, where A has as field_type of adouble. - * - * The function template is disabled if the field_type of v is no an adolc type - */ - template<typename T1,int m, int n,class = typename std::enable_if< std::is_base_of_v<badouble,T1> >::type > - auto operator* (const Dune::FieldMatrix<double, m, n> &A, const Dune::FieldVector<T1,n>& v ) - { - typedef typename Dune::FieldMatrix<adouble,m,n> :: size_type size_type; - Dune::FieldVector<adouble,m> ret(0.0); - - for( size_type i = 0; i < m; ++i ) - for( size_type j = 0; j < n; ++j ) - ret[i] += A[i][j]*v[j]; - - return ret; - } + /** \brief Calculates ret = A*v, where A has as field_type of adouble. + * + * The function template is disabled if the field_type of v is no an adolc type + */ + template<typename T1,int m, int n,class = typename std::enable_if< std::is_base_of_v<badouble,T1> >::type > + auto operator* (const Dune::FieldMatrix<double, m, n> &A, const Dune::FieldVector<T1,n>& v ) + { + typedef typename Dune::FieldMatrix<adouble,m,n> :: size_type size_type; + Dune::FieldVector<adouble,m> ret(0.0); + + for( size_type i = 0; i < m; ++i ) + for( size_type j = 0; j < n; ++j ) + ret[i] += A[i][j]*v[j]; + return ret; + } - /** \brief Calculates ret = A*s, where A has as field_type of adouble. - * - * The function template is disabled if s isn't a scalar or adolc type. - */ - template<typename T1,int m, int n,class = typename std::enable_if< std::is_scalar_v<T1> || std::is_base_of_v<badouble,T1> >::type > - auto operator* (const Dune::FieldMatrix<adouble, m, n> &A, const T1& s ) - { - return s*A; - } + + /** \brief Calculates ret = A*s, where A has as field_type of adouble. + * + * The function template is disabled if s isn't a scalar or adolc type. + */ + template<typename T1,int m, int n,class = typename std::enable_if< std::is_scalar_v<T1> || std::is_base_of_v<badouble,T1> >::type > + auto operator* (const Dune::FieldMatrix<adouble, m, n> &A, const T1& s ) + { + return s*A; + } #endif #if !DUNE_VERSION_NEWER(DUNE_COMMON, 2, 8) - /** \brief Multiplication of a ScaledIdentityMatrix with another FieldMatrix */ - template <class T, int N, int otherCols> - Dune::FieldMatrix<T,N,otherCols> operator* ( const Dune::ScaledIdentityMatrix<T, N>& diagonalMatrix, - const Dune::FieldMatrix<T, N, otherCols>& matrix) - { + /** \brief Multiplication of a ScaledIdentityMatrix with another FieldMatrix */ + template <class T, int N, int otherCols> + Dune::FieldMatrix<T,N,otherCols> operator* ( const Dune::ScaledIdentityMatrix<T, N>& diagonalMatrix, + const Dune::FieldMatrix<T, N, otherCols>& matrix) + { Dune::FieldMatrix<T,N,otherCols> result(0); for (size_t i = 0; i < N; ++i) @@ -76,114 +76,114 @@ namespace Dune { result[i][j] = diagonalMatrix[i][i]*matrix[i][j]; return result; - } + } #endif - /** \brief Return the trace of a matrix */ - template <class T, int n> - static T trace(const FieldMatrix<T,n,n>& A) - { - T trace = 0; - for (int i=0; i<n; i++) - trace += A[i][i]; - return trace; - } + /** \brief Return the trace of a matrix */ + template <class T, int n> + static T trace(const FieldMatrix<T,n,n>& A) + { + T trace = 0; + for (int i=0; i<n; i++) + trace += A[i][i]; + return trace; + } - /** \brief Return the square of the trace of a matrix */ - template <class T, int n> - static T traceSquared(const FieldMatrix<T,n,n>& A) - { - T trace = 0; - for (int i=0; i<n; i++) - trace += A[i][i]; - return trace*trace; - } + /** \brief Return the square of the trace of a matrix */ + template <class T, int n> + static T traceSquared(const FieldMatrix<T,n,n>& A) + { + T trace = 0; + for (int i=0; i<n; i++) + trace += A[i][i]; + return trace*trace; + } - /** \brief Compute the symmetric part of a matrix A, i.e. \f$ \frac 12 (A + A^T) \f$ */ - template <class T, int n> - static FieldMatrix<T,n,n> sym(const FieldMatrix<T,n,n>& A) - { - FieldMatrix<T,n,n> result; - for (int i=0; i<n; i++) - for (int j=0; j<n; j++) - result[i][j] = 0.5 * (A[i][j] + A[j][i]); - return result; - } + /** \brief Compute the symmetric part of a matrix A, i.e. \f$ \frac 12 (A + A^T) \f$ */ + template <class T, int n> + static FieldMatrix<T,n,n> sym(const FieldMatrix<T,n,n>& A) + { + FieldMatrix<T,n,n> result; + for (int i=0; i<n; i++) + for (int j=0; j<n; j++) + result[i][j] = 0.5 * (A[i][j] + A[j][i]); + return result; + } - /** \brief Compute the antisymmetric part of a matrix A, i.e. \f$ \frac 12 (A - A^T) \f$ */ - template <class T, int n> - static FieldMatrix<T,n,n> skew(const FieldMatrix<T,n,n>& A) - { - FieldMatrix<T,n,n> result; - for (int i=0; i<n; i++) - for (int j=0; j<n; j++) - result[i][j] = 0.5 * (A[i][j] - A[j][i]); - return result; - } + /** \brief Compute the antisymmetric part of a matrix A, i.e. \f$ \frac 12 (A - A^T) \f$ */ + template <class T, int n> + static FieldMatrix<T,n,n> skew(const FieldMatrix<T,n,n>& A) + { + FieldMatrix<T,n,n> result; + for (int i=0; i<n; i++) + for (int j=0; j<n; j++) + result[i][j] = 0.5 * (A[i][j] - A[j][i]); + return result; + } - /** \brief Compute the deviator of a matrix A */ - template <class T, int n> - static FieldMatrix<T,n,n> dev(const FieldMatrix<T,n,n>& A) - { - FieldMatrix<T,n,n> result = A; - auto t = trace(A); - for (int i=0; i<n; i++) - result[i][i] -= t / n; - return result; - } + /** \brief Compute the deviator of a matrix A */ + template <class T, int n> + static FieldMatrix<T,n,n> dev(const FieldMatrix<T,n,n>& A) + { + FieldMatrix<T,n,n> result = A; + auto t = trace(A); + for (int i=0; i<n; i++) + result[i][i] -= t / n; + return result; + } - /** \brief Return the transposed matrix */ - template <class T, int n, int m> - static FieldMatrix<T,m,n> transpose(const FieldMatrix<T,n,m>& A) - { - FieldMatrix<T,m,n> result; + /** \brief Return the transposed matrix */ + template <class T, int n, int m> + static FieldMatrix<T,m,n> transpose(const FieldMatrix<T,n,m>& A) + { + FieldMatrix<T,m,n> result; - for (int i=0; i<m; i++) - for (int j=0; j<n; j++) - result[i][j] = A[j][i]; + for (int i=0; i<m; i++) + for (int j=0; j<n; j++) + result[i][j] = A[j][i]; - return result; - } + return result; + } - /** \brief The Frobenius (i.e., componentwise) product of two matrices */ - template <class T, int n> - static T frobeniusProduct(const FieldMatrix<T,n,n>& A, const FieldMatrix<T,n,n>& B) - { - T result(0.0); + /** \brief The Frobenius (i.e., componentwise) product of two matrices */ + template <class T, int n> + static T frobeniusProduct(const FieldMatrix<T,n,n>& A, const FieldMatrix<T,n,n>& B) + { + T result(0.0); - for (int i=0; i<n; i++) - for (int j=0; j<n; j++) - result += A[i][j] * B[i][j]; + for (int i=0; i<n; i++) + for (int j=0; j<n; j++) + result += A[i][j] * B[i][j]; - return result; - } + return result; + } - /** \brief Return a*b^T */ - template <class T1,class T2, int n, int m> - static auto dyadicProduct(const FieldVector<T1,n>& a, const FieldVector<T2,m>& b) - { - using ScalarResultType = typename Dune::PromotionTraits<T1,T2>::PromotedType; - FieldMatrix<ScalarResultType,n,m> result; - for (int i=0; i<n; i++) - for (int j=0; j<m; j++) - result[i][j] = a[i]*b[j]; + /** \brief Return a*b^T */ + template <class T1,class T2, int n, int m> + static auto dyadicProduct(const FieldVector<T1,n>& a, const FieldVector<T2,m>& b) + { + using ScalarResultType = typename Dune::PromotionTraits<T1,T2>::PromotedType; + FieldMatrix<ScalarResultType,n,m> result; + for (int i=0; i<n; i++) + for (int j=0; j<m; j++) + result[i][j] = a[i]*b[j]; - return result; - } + return result; + } - /** \brief Get the requested column of fieldmatrix */ - template<typename field_type, int cols, int rows> - auto col(const Dune::FieldMatrix<field_type, rows, cols> &mat, const int requestedCol) - { - Dune::FieldVector<field_type, rows> col; + /** \brief Get the requested column of fieldmatrix */ + template<typename field_type, int cols, int rows> + auto col(const Dune::FieldMatrix<field_type, rows, cols> &mat, const int requestedCol) + { + Dune::FieldVector<field_type, rows> col; - for (int i = 0; i < rows; ++i) - col[i] = mat[i][requestedCol]; + for (int i = 0; i < rows; ++i) + col[i] = mat[i][requestedCol]; - return col; - } + return col; + } /** \brief Return a segment of a FieldVector from lower up to lower+size-1 */ template< int lower, int size,typename field_type,int n> @@ -217,19 +217,19 @@ namespace Dune { return res; } - /** \brief Return a block of a FieldMatrix (lower1...lower1+size1-1,lower2...lower2+size2-1 - * * lower1 and lower2 are unknown at compile time*/ - template< int size1,int size2,typename field_type,int n,int m> - static auto blockAt(const FieldMatrix<field_type,n,m>& v, const size_t& lower1, const size_t& lower2) - { - assert(lower1+size1<=n && lower2+size2<=m); - FieldMatrix<field_type,size1,size2> res; + /** \brief Return a block of a FieldMatrix (lower1...lower1+size1-1,lower2...lower2+size2-1 + * * lower1 and lower2 are unknown at compile time*/ + template< int size1,int size2,typename field_type,int n,int m> + static auto blockAt(const FieldMatrix<field_type,n,m>& v, const size_t& lower1, const size_t& lower2) + { + assert(lower1+size1<=n && lower2+size2<=m); + FieldMatrix<field_type,size1,size2> res; - for(size_t i=lower1; i<lower1+size1; ++i) - for(size_t j=lower2; j<lower2+size2; ++j) - res[i-lower1][j-lower2] = v[i][j]; - return res; - } + for(size_t i=lower1; i<lower1+size1; ++i) + for(size_t j=lower2; j<lower2+size2; ++j) + res[i-lower1][j-lower2] = v[i][j]; + return res; + } /** \brief Generates FieldVector with random entries in the range -1..1 */ template<typename field_type,int n> diff --git a/dune/gfe/localgeodesicfefunction.hh b/dune/gfe/localgeodesicfefunction.hh index 8318f9d6..76e0d864 100644 --- a/dune/gfe/localgeodesicfefunction.hh +++ b/dune/gfe/localgeodesicfefunction.hh @@ -23,161 +23,161 @@ class LocalGfeTestFunctionBasis; /** \brief A function defined by simplicial geodesic interpolation from the reference element to a Riemannian manifold. -\tparam dim Dimension of the reference element -\tparam ctype Type used for coordinates on the reference element -\tparam LocalFiniteElement A Lagrangian finite element whose shape functions define the interpolation weights -\tparam TargetSpace The manifold that the function takes its values in -*/ + \tparam dim Dimension of the reference element + \tparam ctype Type used for coordinates on the reference element + \tparam LocalFiniteElement A Lagrangian finite element whose shape functions define the interpolation weights + \tparam TargetSpace The manifold that the function takes its values in + */ template <int dim, class ctype, class LocalFiniteElement, class TargetSpace> class LocalGeodesicFEFunction { - typedef typename TargetSpace::ctype RT; + typedef typename TargetSpace::ctype RT; - typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector; - static const int embeddedDim = EmbeddedTangentVector::dimension; + typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector; + static const int embeddedDim = EmbeddedTangentVector::dimension; - static const int spaceDim = TargetSpace::TangentVector::dimension; + static const int spaceDim = TargetSpace::TangentVector::dimension; - friend class LocalGfeTestFunctionBasis<LocalFiniteElement,TargetSpace>; + friend class LocalGfeTestFunctionBasis<LocalFiniteElement,TargetSpace>; public: - /** \brief The type used for derivatives */ - typedef Dune::FieldMatrix<RT, embeddedDim, dim> DerivativeType; - - /** \brief The type used for derivatives of the gradient with respect to coefficients */ - typedef Tensor3<RT,embeddedDim,embeddedDim,dim> DerivativeOfGradientWRTCoefficientType; - - /** \brief Constructor - * \param localFiniteElement A Lagrangian finite element that provides the interpolation points - * \param coefficients Values of the function at the Lagrange points - */ - LocalGeodesicFEFunction(const LocalFiniteElement& localFiniteElement, - const std::vector<TargetSpace>& coefficients) - : localFiniteElement_(localFiniteElement), - coefficients_(coefficients) - { - assert(localFiniteElement_.localBasis().size() == coefficients_.size()); - } - - /** \brief Rebind the FEFunction to another TargetSpace */ - template<class U> - struct rebind - { - using other = LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,U>; - }; - - /** \brief The number of Lagrange points */ - unsigned int size() const - { - return localFiniteElement_.localBasis().size(); - } - - /** \brief The type of the reference element */ - Dune::GeometryType type() const - { - return localFiniteElement_.type(); - } - - /** \brief Evaluate the function */ - TargetSpace evaluate(const Dune::FieldVector<ctype, dim>& local) const; - - /** \brief Evaluate the derivative of the function */ - DerivativeType evaluateDerivative(const Dune::FieldVector<ctype, dim>& local) const; + /** \brief The type used for derivatives */ + typedef Dune::FieldMatrix<RT, embeddedDim, dim> DerivativeType; - /** \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, dim>& local, - const TargetSpace& q) const; + /** \brief The type used for derivatives of the gradient with respect to coefficients */ + typedef Tensor3<RT,embeddedDim,embeddedDim,dim> DerivativeOfGradientWRTCoefficientType; - /** \brief Evaluate the derivative of the function value with respect to a coefficient */ - void evaluateDerivativeOfValueWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, + /** \brief Constructor + * \param localFiniteElement A Lagrangian finite element that provides the interpolation points + * \param coefficients Values of the function at the Lagrange points + */ + LocalGeodesicFEFunction(const LocalFiniteElement& localFiniteElement, + const std::vector<TargetSpace>& coefficients) + : localFiniteElement_(localFiniteElement), + coefficients_(coefficients) + { + assert(localFiniteElement_.localBasis().size() == coefficients_.size()); + } + + /** \brief Rebind the FEFunction to another TargetSpace */ + template<class U> + struct rebind + { + using other = LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,U>; + }; + + /** \brief The number of Lagrange points */ + unsigned int size() const + { + return localFiniteElement_.localBasis().size(); + } + + /** \brief The type of the reference element */ + Dune::GeometryType type() const + { + return localFiniteElement_.type(); + } + + /** \brief Evaluate the function */ + TargetSpace evaluate(const Dune::FieldVector<ctype, dim>& local) const; + + /** \brief Evaluate the derivative of the function */ + DerivativeType evaluateDerivative(const Dune::FieldVector<ctype, dim>& local) const; + + /** \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, dim>& local, + const TargetSpace& q) const; + + /** \brief Evaluate the derivative of the function value with respect to a coefficient */ + void evaluateDerivativeOfValueWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, + int coefficient, + Dune::FieldMatrix<RT,embeddedDim,embeddedDim>& derivative) const; + + /** \brief Evaluate the derivative of the function value with respect to a coefficient */ + void evaluateFDDerivativeOfValueWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, int coefficient, Dune::FieldMatrix<RT,embeddedDim,embeddedDim>& derivative) const; - /** \brief Evaluate the derivative of the function value with respect to a coefficient */ - void evaluateFDDerivativeOfValueWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, - int coefficient, - Dune::FieldMatrix<RT,embeddedDim,embeddedDim>& derivative) const; + /** \brief Evaluate the derivative of the gradient of the function with respect to a coefficient */ + void evaluateDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, + int coefficient, + DerivativeOfGradientWRTCoefficientType& result) const; - /** \brief Evaluate the derivative of the gradient of the function with respect to a coefficient */ - void evaluateDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, + /** \brief Evaluate the derivative of the gradient of the function with respect to a coefficient */ + void evaluateFDDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, int coefficient, DerivativeOfGradientWRTCoefficientType& result) const; - /** \brief Evaluate the derivative of the gradient of the function with respect to a coefficient */ - void evaluateFDDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, - int coefficient, - DerivativeOfGradientWRTCoefficientType& result) const; - - /** \brief Get the i'th base coefficient. */ - TargetSpace coefficient(int i) const - { - return coefficients_[i]; - } + /** \brief Get the i'th base coefficient. */ + TargetSpace coefficient(int i) const + { + return coefficients_[i]; + } private: - static Dune::SymmetricMatrix<RT,embeddedDim> pseudoInverse(const Dune::SymmetricMatrix<RT,embeddedDim>& dFdq, - const TargetSpace& q) - { - const int shortDim = TargetSpace::TangentVector::dimension; - - // the orthonormal frame - Dune::FieldMatrix<RT,shortDim,embeddedDim> O = q.orthonormalFrame(); - - // compute A = O dFDq O^T - // TODO A really is symmetric, too - Dune::FieldMatrix<RT,shortDim,shortDim> A; - for (int i=0; i<shortDim; i++) - for (int j=0; j<shortDim; j++) { - A[i][j] = 0; - for (int k=0; k<embeddedDim; k++) - for (int l=0; l<embeddedDim; l++) - A[i][j] += O[i][k] * ((k>=l) ? dFdq(k,l) : dFdq(l,k)) *O[j][l]; - } - - A.invert(); - - Dune::SymmetricMatrix<RT,embeddedDim> result; - result = 0.0; - for (int i=0; i<embeddedDim; i++) - for (int j=0; j<=i; j++) - for (int k=0; k<shortDim; k++) - for (int l=0; l<shortDim; l++) - result(i,j) += O[k][i]*A[k][l]*O[l][j]; - - return result; - } + static Dune::SymmetricMatrix<RT,embeddedDim> pseudoInverse(const Dune::SymmetricMatrix<RT,embeddedDim>& dFdq, + const TargetSpace& q) + { + const int shortDim = TargetSpace::TangentVector::dimension; - /** \brief Compute derivate of F(w,q) (the derivative of the weighted distance fctl) wrt to w */ - Dune::Matrix<RT> computeDFdw(const TargetSpace& q) const - { - Dune::Matrix<RT> dFdw(embeddedDim,localFiniteElement_.localBasis().size()); - for (size_t i=0; i<localFiniteElement_.localBasis().size(); i++) { - Dune::FieldVector<RT,embeddedDim> tmp = TargetSpace::derivativeOfDistanceSquaredWRTSecondArgument(coefficients_[i], q); - for (int j=0; j<embeddedDim; j++) - dFdw[j][i] = tmp[j]; - } - return dFdw; - } + // the orthonormal frame + Dune::FieldMatrix<RT,shortDim,embeddedDim> O = q.orthonormalFrame(); + + // compute A = O dFDq O^T + // TODO A really is symmetric, too + Dune::FieldMatrix<RT,shortDim,shortDim> A; + for (int i=0; i<shortDim; i++) + for (int j=0; j<shortDim; j++) { + A[i][j] = 0; + for (int k=0; k<embeddedDim; k++) + for (int l=0; l<embeddedDim; l++) + A[i][j] += O[i][k] * ((k>=l) ? dFdq(k,l) : dFdq(l,k)) *O[j][l]; + } + + A.invert(); - Tensor3<RT,embeddedDim,embeddedDim,embeddedDim> computeDqDqF(const std::vector<Dune::FieldVector<ctype,1> >& w, const TargetSpace& q) const - { - Tensor3<RT,embeddedDim,embeddedDim,embeddedDim> result; - result = Tensor3<RT,embeddedDim,embeddedDim,embeddedDim>(RT(0)); - for (size_t i=0; i<w.size(); i++) - result.axpy(w[i][0], TargetSpace::thirdDerivativeOfDistanceSquaredWRTSecondArgument(coefficients_[i],q)); - return result; + Dune::SymmetricMatrix<RT,embeddedDim> result; + result = 0.0; + for (int i=0; i<embeddedDim; i++) + for (int j=0; j<=i; j++) + for (int k=0; k<shortDim; k++) + for (int l=0; l<shortDim; l++) + result(i,j) += O[k][i]*A[k][l]*O[l][j]; + + return result; + } + + /** \brief Compute derivate of F(w,q) (the derivative of the weighted distance fctl) wrt to w */ + Dune::Matrix<RT> computeDFdw(const TargetSpace& q) const + { + Dune::Matrix<RT> dFdw(embeddedDim,localFiniteElement_.localBasis().size()); + for (size_t i=0; i<localFiniteElement_.localBasis().size(); i++) { + Dune::FieldVector<RT,embeddedDim> tmp = TargetSpace::derivativeOfDistanceSquaredWRTSecondArgument(coefficients_[i], q); + for (int j=0; j<embeddedDim; j++) + dFdw[j][i] = tmp[j]; } + return dFdw; + } + + Tensor3<RT,embeddedDim,embeddedDim,embeddedDim> computeDqDqF(const std::vector<Dune::FieldVector<ctype,1> >& w, const TargetSpace& q) const + { + Tensor3<RT,embeddedDim,embeddedDim,embeddedDim> result; + result = Tensor3<RT,embeddedDim,embeddedDim,embeddedDim>(RT(0)); + for (size_t i=0; i<w.size(); i++) + result.axpy(w[i][0], TargetSpace::thirdDerivativeOfDistanceSquaredWRTSecondArgument(coefficients_[i],q)); + return result; + } - /** \brief The scalar local finite element, which provides the weighting factors - \todo We really only need the local basis - */ - const LocalFiniteElement& localFiniteElement_; + /** \brief The scalar local finite element, which provides the weighting factors + \todo We really only need the local basis + */ + const LocalFiniteElement& localFiniteElement_; - /** \brief The coefficient vector */ - std::vector<TargetSpace> coefficients_; + /** \brief The coefficient vector */ + std::vector<TargetSpace> coefficients_; }; @@ -185,29 +185,29 @@ template <int dim, class ctype, class LocalFiniteElement, class TargetSpace> TargetSpace LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace>:: evaluate(const Dune::FieldVector<ctype, dim>& local) const { - // Evaluate the weighting factors---these are the Lagrangian shape function values at 'local' - std::vector<Dune::FieldVector<ctype,1> > w; - localFiniteElement_.localBasis().evaluateFunction(local,w); + // Evaluate the weighting factors---these are the Lagrangian shape function values at 'local' + std::vector<Dune::FieldVector<ctype,1> > w; + localFiniteElement_.localBasis().evaluateFunction(local,w); - // The energy functional whose mimimizer is the value of the geodesic interpolation - AverageDistanceAssembler<TargetSpace> assembler(coefficients_, w); + // The energy functional whose mimimizer is the value of the geodesic interpolation + AverageDistanceAssembler<TargetSpace> assembler(coefficients_, w); - // Create a reasonable initial iterate for the iterative solver - Dune::GFE::LocalQuickAndDirtyFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> localProjectedFEFunction(localFiniteElement_, coefficients_); - TargetSpace initialIterate = localProjectedFEFunction.evaluate(local); + // Create a reasonable initial iterate for the iterative solver + Dune::GFE::LocalQuickAndDirtyFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> localProjectedFEFunction(localFiniteElement_, coefficients_); + TargetSpace initialIterate = localProjectedFEFunction.evaluate(local); - // Iteratively solve the GFE minimization problem - TargetSpaceRiemannianTRSolver<TargetSpace> solver; + // Iteratively solve the GFE minimization problem + TargetSpaceRiemannianTRSolver<TargetSpace> solver; - solver.setup(&assembler, - initialIterate, - 1e-14, // tolerance - 100 // maxNewtonSteps - ); + solver.setup(&assembler, + initialIterate, + 1e-14, // tolerance + 100 // maxNewtonSteps + ); - solver.solve(); + solver.solve(); - return solver.getSol(); + return solver.getSol(); } template <int dim, class ctype, class LocalFiniteElement, class TargetSpace> @@ -215,11 +215,11 @@ typename LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace>::Deri LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace>:: evaluateDerivative(const Dune::FieldVector<ctype, dim>& local) const { - // the function value at the point where we are evaluating the derivative - TargetSpace q = evaluate(local); + // the function value at the point where we are evaluating the derivative + TargetSpace q = evaluate(local); - // Actually compute the derivative - return evaluateDerivative(local,q); + // Actually compute the derivative + return evaluateDerivative(local,q); } template <int dim, class ctype, class LocalFiniteElement, class TargetSpace> @@ -227,79 +227,79 @@ typename LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace>::Deri LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace>:: evaluateDerivative(const Dune::FieldVector<ctype, dim>& local, const TargetSpace& q) const { - Dune::FieldMatrix<RT, embeddedDim, dim> result; + Dune::FieldMatrix<RT, embeddedDim, dim> result; #if 0 // this is probably faster than the general implementation, but we leave it out for testing purposes - if (dim==1) { + if (dim==1) { - EmbeddedTangentVector tmp = TargetSpace::interpolateDerivative(coefficients_[0], coefficients_[1], local[0]); + EmbeddedTangentVector tmp = TargetSpace::interpolateDerivative(coefficients_[0], coefficients_[1], local[0]); - for (int i=0; i<embeddedDim; i++) - result[i][0] = tmp[i]; + for (int i=0; i<embeddedDim; i++) + result[i][0] = tmp[i]; - } + } #endif - // //////////////////////////////////////////////////////////////////////// - // The derivative is evaluated using the implicit function theorem. - // Hence we need to solve a small system of linear equations. - // //////////////////////////////////////////////////////////////////////// - - // the matrix that turns coordinates on the reference simplex into coordinates on the standard simplex - std::vector<Dune::FieldMatrix<ctype,1,dim> > B(coefficients_.size()); - localFiniteElement_.localBasis().evaluateJacobian(local, B); - - // compute negative derivative of F(w,q) (the derivative of the weighted distance fctl) wrt to w - Dune::Matrix<RT> dFdw = computeDFdw(q); - dFdw *= -1; - - // multiply the two previous matrices: the result is the right hand side - // RHS = dFdw * B; - // We need to write this multiplication by hand, because B is actually an (#coefficients) times 1 times dim matrix, - // and we need to ignore the middle index. - Dune::Matrix<RT> RHS(dFdw.N(), dim); - - for (size_t i=0; i<RHS.N(); i++) - for (size_t j=0; j<RHS.M(); j++) { - RHS[i][j] = 0; - for (size_t k=0; k<dFdw.M(); k++) - RHS[i][j] += dFdw[i][k]*B[k][0][j]; - } + // //////////////////////////////////////////////////////////////////////// + // The derivative is evaluated using the implicit function theorem. + // Hence we need to solve a small system of linear equations. + // //////////////////////////////////////////////////////////////////////// + + // the matrix that turns coordinates on the reference simplex into coordinates on the standard simplex + std::vector<Dune::FieldMatrix<ctype,1,dim> > B(coefficients_.size()); + localFiniteElement_.localBasis().evaluateJacobian(local, B); + + // compute negative derivative of F(w,q) (the derivative of the weighted distance fctl) wrt to w + Dune::Matrix<RT> dFdw = computeDFdw(q); + dFdw *= -1; + + // multiply the two previous matrices: the result is the right hand side + // RHS = dFdw * B; + // We need to write this multiplication by hand, because B is actually an (#coefficients) times 1 times dim matrix, + // and we need to ignore the middle index. + Dune::Matrix<RT> RHS(dFdw.N(), dim); + + for (size_t i=0; i<RHS.N(); i++) + for (size_t j=0; j<RHS.M(); j++) { + RHS[i][j] = 0; + for (size_t k=0; k<dFdw.M(); k++) + RHS[i][j] += dFdw[i][k]*B[k][0][j]; + } - // the actual system matrix - std::vector<Dune::FieldVector<ctype,1> > w; - localFiniteElement_.localBasis().evaluateFunction(local, w); + // the actual system matrix + std::vector<Dune::FieldVector<ctype,1> > w; + localFiniteElement_.localBasis().evaluateFunction(local, w); - AverageDistanceAssembler<TargetSpace> assembler(coefficients_, w); + AverageDistanceAssembler<TargetSpace> assembler(coefficients_, w); - Dune::SymmetricMatrix<RT,embeddedDim> dFdq; - assembler.assembleEmbeddedHessian(q,dFdq); + Dune::SymmetricMatrix<RT,embeddedDim> dFdq; + assembler.assembleEmbeddedHessian(q,dFdq); - // We want to solve - // dFdq * x = rhs - // We use the Gram-Schmidt solver because we know that dFdq is rank-deficient. + // We want to solve + // dFdq * x = rhs + // We use the Gram-Schmidt solver because we know that dFdq is rank-deficient. - const int shortDim = TargetSpace::TangentVector::dimension; + const int shortDim = TargetSpace::TangentVector::dimension; - // the orthonormal frame - Dune::FieldMatrix<RT,shortDim,embeddedDim> basis = q.orthonormalFrame(); - GramSchmidtSolver<RT,shortDim,embeddedDim> gramSchmidtSolver(dFdq, basis); + // the orthonormal frame + Dune::FieldMatrix<RT,shortDim,embeddedDim> basis = q.orthonormalFrame(); + GramSchmidtSolver<RT,shortDim,embeddedDim> gramSchmidtSolver(dFdq, basis); - for (int i=0; i<dim; i++) { + for (int i=0; i<dim; i++) { - Dune::FieldVector<RT,embeddedDim> rhs; - for (int j=0; j<embeddedDim; j++) - rhs[j] = RHS[j][i]; + Dune::FieldVector<RT,embeddedDim> rhs; + for (int j=0; j<embeddedDim; j++) + rhs[j] = RHS[j][i]; - Dune::FieldVector<RT,embeddedDim> x; - gramSchmidtSolver.solve(x, rhs); + Dune::FieldVector<RT,embeddedDim> x; + gramSchmidtSolver.solve(x, rhs); - for (int j=0; j<embeddedDim; j++) - result[j][i] = x[j]; + for (int j=0; j<embeddedDim; j++) + result[j][i] = x[j]; - } + } - return result; + return result; } template <int dim, class ctype, class LocalFiniteElement, class TargetSpace> @@ -308,48 +308,48 @@ evaluateDerivativeOfValueWRTCoefficient(const Dune::FieldVector<ctype, dim>& loc int coefficient, Dune::FieldMatrix<RT,embeddedDim,embeddedDim>& result) const { - // the function value at the point where we are evaluating the derivative - TargetSpace q = evaluate(local); + // the function value at the point where we are evaluating the derivative + TargetSpace q = evaluate(local); - // dFdq - std::vector<Dune::FieldVector<ctype,1> > w; - localFiniteElement_.localBasis().evaluateFunction(local,w); + // dFdq + std::vector<Dune::FieldVector<ctype,1> > w; + localFiniteElement_.localBasis().evaluateFunction(local,w); - AverageDistanceAssembler<TargetSpace> assembler(coefficients_, w); + AverageDistanceAssembler<TargetSpace> assembler(coefficients_, w); - Dune::SymmetricMatrix<RT,embeddedDim> dFdq; - assembler.assembleEmbeddedHessian(q,dFdq); + Dune::SymmetricMatrix<RT,embeddedDim> dFdq; + assembler.assembleEmbeddedHessian(q,dFdq); - const int shortDim = TargetSpace::TangentVector::dimension; + const int shortDim = TargetSpace::TangentVector::dimension; - // the orthonormal frame - Dune::FieldMatrix<RT,shortDim,embeddedDim> O = q.orthonormalFrame(); + // the orthonormal frame + Dune::FieldMatrix<RT,shortDim,embeddedDim> O = q.orthonormalFrame(); - // compute A = O dFDq O^T - Dune::FieldMatrix<RT,shortDim,shortDim> A; - for (int i=0; i<shortDim; i++) - for (int j=0; j<shortDim; j++) { - A[i][j] = 0; - for (int k=0; k<embeddedDim; k++) - for (int l=0; l<embeddedDim; l++) - A[i][j] += O[i][k] * ((k>=l) ? dFdq(k,l) : dFdq(l,k)) * O[j][l]; - } + // compute A = O dFDq O^T + Dune::FieldMatrix<RT,shortDim,shortDim> A; + for (int i=0; i<shortDim; i++) + for (int j=0; j<shortDim; j++) { + A[i][j] = 0; + for (int k=0; k<embeddedDim; k++) + for (int l=0; l<embeddedDim; l++) + A[i][j] += O[i][k] * ((k>=l) ? dFdq(k,l) : dFdq(l,k)) * O[j][l]; + } - // - Dune::FieldMatrix<RT,embeddedDim,embeddedDim> rhs = TargetSpace::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(coefficients_[coefficient], q); - rhs *= -w[coefficient]; + // + Dune::FieldMatrix<RT,embeddedDim,embeddedDim> rhs = TargetSpace::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(coefficients_[coefficient], q); + rhs *= -w[coefficient]; - for (int i=0; i<embeddedDim; i++) { + for (int i=0; i<embeddedDim; i++) { - Dune::FieldVector<RT,shortDim> shortRhs; - O.mv(rhs[i],shortRhs); + Dune::FieldVector<RT,shortDim> shortRhs; + O.mv(rhs[i],shortRhs); - Dune::FieldVector<RT,shortDim> shortX; - A.solve(shortX,shortRhs); + Dune::FieldVector<RT,shortDim> shortX; + A.solve(shortX,shortRhs); - O.mtv(shortX,result[i]); + O.mtv(shortX,result[i]); - } + } } @@ -359,44 +359,44 @@ evaluateFDDerivativeOfValueWRTCoefficient(const Dune::FieldVector<ctype, dim>& l int coefficient, Dune::FieldMatrix<RT,embeddedDim,embeddedDim>& result) const { - double eps = 1e-6; + double eps = 1e-6; - // the function value at the point where we are evaluating the derivative - const Dune::FieldMatrix<RT,spaceDim,embeddedDim> B = coefficients_[coefficient].orthonormalFrame(); + // the function value at the point where we are evaluating the derivative + const Dune::FieldMatrix<RT,spaceDim,embeddedDim> B = coefficients_[coefficient].orthonormalFrame(); - Dune::FieldMatrix<RT,spaceDim,embeddedDim> interimResult; + Dune::FieldMatrix<RT,spaceDim,embeddedDim> interimResult; - std::vector<TargetSpace> cornersPlus = coefficients_; - std::vector<TargetSpace> cornersMinus = coefficients_; + std::vector<TargetSpace> cornersPlus = coefficients_; + std::vector<TargetSpace> cornersMinus = coefficients_; - for (int j=0; j<spaceDim; j++) { + for (int j=0; j<spaceDim; j++) { - typename TargetSpace::EmbeddedTangentVector forwardVariation = B[j]; - forwardVariation *= eps; - typename TargetSpace::EmbeddedTangentVector backwardVariation = B[j]; - backwardVariation *= -eps; + typename TargetSpace::EmbeddedTangentVector forwardVariation = B[j]; + forwardVariation *= eps; + typename TargetSpace::EmbeddedTangentVector backwardVariation = B[j]; + backwardVariation *= -eps; - cornersPlus [coefficient] = TargetSpace::exp(coefficients_[coefficient], forwardVariation); - cornersMinus[coefficient] = TargetSpace::exp(coefficients_[coefficient], backwardVariation); + cornersPlus [coefficient] = TargetSpace::exp(coefficients_[coefficient], forwardVariation); + cornersMinus[coefficient] = TargetSpace::exp(coefficients_[coefficient], backwardVariation); - LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> fPlus(localFiniteElement_,cornersPlus); - LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> fMinus(localFiniteElement_,cornersMinus); + LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> fPlus(localFiniteElement_,cornersPlus); + LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> fMinus(localFiniteElement_,cornersMinus); - TargetSpace hPlus = fPlus.evaluate(local); - TargetSpace hMinus = fMinus.evaluate(local); + TargetSpace hPlus = fPlus.evaluate(local); + TargetSpace hMinus = fMinus.evaluate(local); - interimResult[j] = hPlus.globalCoordinates(); - interimResult[j] -= hMinus.globalCoordinates(); - interimResult[j] /= 2*eps; + interimResult[j] = hPlus.globalCoordinates(); + interimResult[j] -= hMinus.globalCoordinates(); + interimResult[j] /= 2*eps; - } + } - for (int i=0; i<embeddedDim; i++) - for (int j=0; j<embeddedDim; j++) { - result[i][j] = 0; - for (int k=0; k<spaceDim; k++) - result[i][j] += B[k][i]*interimResult[k][j]; - } + for (int i=0; i<embeddedDim; i++) + for (int j=0; j<embeddedDim; j++) { + result[i][j] = 0; + for (int k=0; k<spaceDim; k++) + result[i][j] += B[k][i]*interimResult[k][j]; + } } @@ -407,93 +407,93 @@ evaluateDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& int coefficient, DerivativeOfGradientWRTCoefficientType& result) const { - // the function value at the point where we are evaluating the derivative - TargetSpace q = evaluate(local); - - // the matrix that turns coordinates on the reference simplex into coordinates on the standard simplex - std::vector<Dune::FieldMatrix<ctype,1,dim> > BNested(coefficients_.size()); - localFiniteElement_.localBasis().evaluateJacobian(local, BNested); - Dune::Matrix<RT> B(coefficients_.size(), dim); - for (size_t i=0; i<coefficients_.size(); i++) - for (size_t j=0; j<dim; j++) - B[i][j] = BNested[i][0][j]; - - // the actual system matrix - std::vector<Dune::FieldVector<ctype,1> > w; - localFiniteElement_.localBasis().evaluateFunction(local,w); + // the function value at the point where we are evaluating the derivative + TargetSpace q = evaluate(local); - AverageDistanceAssembler<TargetSpace> assembler(coefficients_, w); + // the matrix that turns coordinates on the reference simplex into coordinates on the standard simplex + std::vector<Dune::FieldMatrix<ctype,1,dim> > BNested(coefficients_.size()); + localFiniteElement_.localBasis().evaluateJacobian(local, BNested); + Dune::Matrix<RT> B(coefficients_.size(), dim); + for (size_t i=0; i<coefficients_.size(); i++) + for (size_t j=0; j<dim; j++) + B[i][j] = BNested[i][0][j]; - /** \todo Use a symmetric matrix here */ - Dune::SymmetricMatrix<RT,embeddedDim> dFdq; - assembler.assembleEmbeddedHessian(q,dFdq); + // the actual system matrix + std::vector<Dune::FieldVector<ctype,1> > w; + localFiniteElement_.localBasis().evaluateFunction(local,w); + AverageDistanceAssembler<TargetSpace> assembler(coefficients_, w); - Dune::FieldMatrix<RT,embeddedDim,embeddedDim> mixedDerivative = TargetSpace::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(coefficients_[coefficient], q); - TensorSSD<RT,embeddedDim,embeddedDim> dvDwF(coefficients_.size()); - dvDwF = 0; - for (int i=0; i<embeddedDim; i++) - for (int j=0; j<embeddedDim; j++) - dvDwF(i, j, coefficient) = mixedDerivative[i][j]; + /** \todo Use a symmetric matrix here */ + Dune::SymmetricMatrix<RT,embeddedDim> dFdq; + assembler.assembleEmbeddedHessian(q,dFdq); - // dFDq is not invertible, if the target space is embedded into a higher-dimensional - // Euclidean space. Therefore we use its pseudo inverse. I don't think that is the - // best way, though. - Dune::SymmetricMatrix<RT,embeddedDim> dFdqPseudoInv = pseudoInverse(dFdq,q); + Dune::FieldMatrix<RT,embeddedDim,embeddedDim> mixedDerivative = TargetSpace::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(coefficients_[coefficient], q); + TensorSSD<RT,embeddedDim,embeddedDim> dvDwF(coefficients_.size()); + dvDwF = 0; + for (int i=0; i<embeddedDim; i++) + for (int j=0; j<embeddedDim; j++) + dvDwF(i, j, coefficient) = mixedDerivative[i][j]; - // - Tensor3<RT,embeddedDim,embeddedDim,embeddedDim> dvDqF - = TargetSpace::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(coefficients_[coefficient], q); - dvDqF = RT(w[coefficient]) * dvDqF; + // dFDq is not invertible, if the target space is embedded into a higher-dimensional + // Euclidean space. Therefore we use its pseudo inverse. I don't think that is the + // best way, though. + Dune::SymmetricMatrix<RT,embeddedDim> dFdqPseudoInv = pseudoInverse(dFdq,q); - // Put it all together - // dvq[i][j] = \partial q_j / \partial v_i - Dune::FieldMatrix<RT,embeddedDim,embeddedDim> dvq; - evaluateDerivativeOfValueWRTCoefficient(local,coefficient,dvq); + // + Tensor3<RT,embeddedDim,embeddedDim,embeddedDim> dvDqF + = TargetSpace::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(coefficients_[coefficient], q); - Dune::FieldMatrix<RT, embeddedDim, dim> derivative = evaluateDerivative(local); + dvDqF = RT(w[coefficient]) * dvDqF; - Tensor3<RT, embeddedDim,embeddedDim,embeddedDim> dqdqF; - dqdqF = computeDqDqF(w,q); + // Put it all together + // dvq[i][j] = \partial q_j / \partial v_i + Dune::FieldMatrix<RT,embeddedDim,embeddedDim> dvq; + evaluateDerivativeOfValueWRTCoefficient(local,coefficient,dvq); - TensorSSD<RT, embeddedDim,embeddedDim> dqdwF(coefficients_.size()); + Dune::FieldMatrix<RT, embeddedDim, dim> derivative = evaluateDerivative(local); - for (size_t k=0; k<coefficients_.size(); k++) { - Dune::SymmetricMatrix<RT,embeddedDim> hesse = TargetSpace::secondDerivativeOfDistanceSquaredWRTSecondArgument(coefficients_[k], q); - for (int i=0; i<embeddedDim; i++) - for (int j=0; j<=i; j++) - dqdwF(i, j, k) = dqdwF(j, i, k) = hesse(i,j); + Tensor3<RT, embeddedDim,embeddedDim,embeddedDim> dqdqF; + dqdqF = computeDqDqF(w,q); - } + TensorSSD<RT, embeddedDim,embeddedDim> dqdwF(coefficients_.size()); - TensorSSD<RT, embeddedDim,embeddedDim> dqdwF_times_dvq(coefficients_.size()); + for (size_t k=0; k<coefficients_.size(); k++) { + Dune::SymmetricMatrix<RT,embeddedDim> hesse = TargetSpace::secondDerivativeOfDistanceSquaredWRTSecondArgument(coefficients_[k], q); for (int i=0; i<embeddedDim; i++) - for (int j=0; j<embeddedDim; j++) - for (size_t k=0; k<coefficients_.size(); k++) { - dqdwF_times_dvq(i, j, k) = 0; - for (int l=0; l<embeddedDim; l++) - dqdwF_times_dvq(i, j, k) += dqdwF(l, j, k) * dvq[i][l]; - } - - Tensor3<RT,embeddedDim,embeddedDim,dim> foo; - foo = -1 * dvDqF*derivative - (dvq*dqdqF)*derivative; - TensorSSD<RT,embeddedDim,embeddedDim> bar(dim); - bar = dvDwF * B + dqdwF_times_dvq*B; + for (int j=0; j<=i; j++) + dqdwF(i, j, k) = dqdwF(j, i, k) = hesse(i,j); + + } + + TensorSSD<RT, embeddedDim,embeddedDim> dqdwF_times_dvq(coefficients_.size()); + for (int i=0; i<embeddedDim; i++) + for (int j=0; j<embeddedDim; j++) + for (size_t k=0; k<coefficients_.size(); k++) { + dqdwF_times_dvq(i, j, k) = 0; + for (int l=0; l<embeddedDim; l++) + dqdwF_times_dvq(i, j, k) += dqdwF(l, j, k) * dvq[i][l]; + } - for (int i=0; i<embeddedDim; i++) - for (int j=0; j<embeddedDim; j++) - for (int k=0; k<dim; k++) - foo[i][j][k] -= bar(i, j, k); + Tensor3<RT,embeddedDim,embeddedDim,dim> foo; + foo = -1 * dvDqF*derivative - (dvq*dqdqF)*derivative; + TensorSSD<RT,embeddedDim,embeddedDim> bar(dim); + bar = dvDwF * B + dqdwF_times_dvq*B; - result = RT(0); - for (int i=0; i<embeddedDim; i++) - for (int j=0; j<embeddedDim; j++) - for (int k=0; k<dim; k++) - for (int l=0; l<embeddedDim; l++) - // TODO Smarter implementation of the product with a symmetric matrix - result[i][j][k] += ((j>=l) ? dFdqPseudoInv(j,l) : dFdqPseudoInv(l,j)) * foo[i][l][k]; + for (int i=0; i<embeddedDim; i++) + for (int j=0; j<embeddedDim; j++) + for (int k=0; k<dim; k++) + foo[i][j][k] -= bar(i, j, k); + + result = RT(0); + for (int i=0; i<embeddedDim; i++) + for (int j=0; j<embeddedDim; j++) + for (int k=0; k<dim; k++) + for (int l=0; l<embeddedDim; l++) + // TODO Smarter implementation of the product with a symmetric matrix + result[i][j][k] += ((j>=l) ? dFdqPseudoInv(j,l) : dFdqPseudoInv(l,j)) * foo[i][l][k]; } @@ -501,52 +501,52 @@ evaluateDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& template <int dim, class ctype, class LocalFiniteElement, class TargetSpace> void LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace>:: evaluateFDDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, - int coefficient, - DerivativeOfGradientWRTCoefficientType& result) const + int coefficient, + DerivativeOfGradientWRTCoefficientType& result) const { - double eps = 1e-6; + double eps = 1e-6; - // loop over the different partial derivatives - for (int j=0; j<embeddedDim; j++) { + // loop over the different partial derivatives + for (int j=0; j<embeddedDim; j++) { - std::vector<TargetSpace> cornersPlus = coefficients_; - std::vector<TargetSpace> cornersMinus = coefficients_; - typename TargetSpace::CoordinateType aPlus = coefficients_[coefficient].globalCoordinates(); - typename TargetSpace::CoordinateType aMinus = coefficients_[coefficient].globalCoordinates(); - aPlus[j] += eps; - aMinus[j] -= eps; - cornersPlus[coefficient] = TargetSpace(aPlus); - cornersMinus[coefficient] = TargetSpace(aMinus); - LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> fPlus(localFiniteElement_,cornersPlus); - LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> fMinus(localFiniteElement_,cornersMinus); + std::vector<TargetSpace> cornersPlus = coefficients_; + std::vector<TargetSpace> cornersMinus = coefficients_; + typename TargetSpace::CoordinateType aPlus = coefficients_[coefficient].globalCoordinates(); + typename TargetSpace::CoordinateType aMinus = coefficients_[coefficient].globalCoordinates(); + aPlus[j] += eps; + aMinus[j] -= eps; + cornersPlus[coefficient] = TargetSpace(aPlus); + cornersMinus[coefficient] = TargetSpace(aMinus); + LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> fPlus(localFiniteElement_,cornersPlus); + LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> fMinus(localFiniteElement_,cornersMinus); - Dune::FieldMatrix<RT,embeddedDim,dim> hPlus = fPlus.evaluateDerivative(local); - Dune::FieldMatrix<RT,embeddedDim,dim> hMinus = fMinus.evaluateDerivative(local); + Dune::FieldMatrix<RT,embeddedDim,dim> hPlus = fPlus.evaluateDerivative(local); + Dune::FieldMatrix<RT,embeddedDim,dim> hMinus = fMinus.evaluateDerivative(local); - result[j] = hPlus; - result[j] -= hMinus; - result[j] /= 2*eps; + result[j] = hPlus; + result[j] -= hMinus; + result[j] /= 2*eps; - } + } - for (int j=0; j<embeddedDim; j++) { - - TargetSpace q = evaluate(local); - Dune::FieldVector<RT,embeddedDim> foo; - for (int l=0; l<dim; l++) { + for (int j=0; j<embeddedDim; j++) { - for (int k=0; k<embeddedDim; k++) - foo[k] = result[j][k][l]; + TargetSpace q = evaluate(local); + Dune::FieldVector<RT,embeddedDim> foo; + for (int l=0; l<dim; l++) { - foo = q.projectOntoTangentSpace(foo); + for (int k=0; k<embeddedDim; k++) + foo[k] = result[j][k][l]; - for (int k=0; k<embeddedDim; k++) - result[j][k][l] = foo[k]; + foo = q.projectOntoTangentSpace(foo); - } + for (int k=0; k<embeddedDim; k++) + result[j][k][l] = foo[k]; } + } + } @@ -556,233 +556,233 @@ evaluateFDDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim> This is a specialization for speeding up the code. We use that a RigidBodyMotion is a product manifold. -\tparam dim Dimension of the reference element -\tparam ctype Type used for coordinates on the reference element -*/ + \tparam dim Dimension of the reference element + \tparam ctype Type used for coordinates on the reference element + */ template <int dim, class ctype, class LocalFiniteElement, class field_type> class LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,RigidBodyMotion<field_type,3> > { - typedef RigidBodyMotion<field_type,3> TargetSpace; + typedef RigidBodyMotion<field_type,3> TargetSpace; - typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector; - static const int embeddedDim = EmbeddedTangentVector::dimension; + typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector; + static const int embeddedDim = EmbeddedTangentVector::dimension; - static const int spaceDim = TargetSpace::TangentVector::dimension; + static const int spaceDim = TargetSpace::TangentVector::dimension; public: - /** \brief The type used for derivatives */ - typedef Dune::FieldMatrix<field_type, embeddedDim, dim> DerivativeType; + /** \brief The type used for derivatives */ + typedef Dune::FieldMatrix<field_type, embeddedDim, dim> DerivativeType; - /** \brief The type used for derivatives of the gradient with respect to coefficients */ - typedef Tensor3<field_type,embeddedDim,embeddedDim,dim> DerivativeOfGradientWRTCoefficientType; + /** \brief The type used for derivatives of the gradient with respect to coefficients */ + typedef Tensor3<field_type,embeddedDim,embeddedDim,dim> DerivativeOfGradientWRTCoefficientType; - /** \brief Constructor */ - LocalGeodesicFEFunction(const LocalFiniteElement& localFiniteElement, - const std::vector<TargetSpace>& coefficients) + /** \brief Constructor */ + LocalGeodesicFEFunction(const LocalFiniteElement& localFiniteElement, + const std::vector<TargetSpace>& coefficients) : localFiniteElement_(localFiniteElement), - translationCoefficients_(coefficients.size()) - { - assert(localFiniteElement.localBasis().size() == coefficients.size()); + translationCoefficients_(coefficients.size()) + { + assert(localFiniteElement.localBasis().size() == coefficients.size()); - for (size_t i=0; i<coefficients.size(); i++) - translationCoefficients_[i] = coefficients[i].r; + for (size_t i=0; i<coefficients.size(); i++) + translationCoefficients_[i] = coefficients[i].r; - std::vector<Rotation<field_type,3> > orientationCoefficients(coefficients.size()); - for (size_t i=0; i<coefficients.size(); i++) - orientationCoefficients[i] = coefficients[i].q; + std::vector<Rotation<field_type,3> > orientationCoefficients(coefficients.size()); + for (size_t i=0; i<coefficients.size(); i++) + orientationCoefficients[i] = coefficients[i].q; - orientationFEFunction_ = std::unique_ptr<LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,Rotation<field_type,3> > > (new LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,Rotation<field_type,3> >(localFiniteElement,orientationCoefficients)); + orientationFEFunction_ = std::unique_ptr<LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,Rotation<field_type,3> > > (new LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,Rotation<field_type,3> >(localFiniteElement,orientationCoefficients)); - } + } - /** \brief Rebind the FEFunction to another TargetSpace */ - template<class U> - struct rebind - { - using other = LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,U>; - }; - - /** \brief The number of Lagrange points */ - unsigned int size() const - { - return localFiniteElement_.localBasis().size(); - } + /** \brief Rebind the FEFunction to another TargetSpace */ + template<class U> + struct rebind + { + using other = LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,U>; + }; - /** \brief The type of the reference element */ - Dune::GeometryType type() const - { - return localFiniteElement_.type(); - } + /** \brief The number of Lagrange points */ + unsigned int size() const + { + return localFiniteElement_.localBasis().size(); + } - /** \brief Evaluate the function */ - TargetSpace evaluate(const Dune::FieldVector<ctype, dim>& local) const - { - TargetSpace result; + /** \brief The type of the reference element */ + Dune::GeometryType type() const + { + return localFiniteElement_.type(); + } - // Evaluate the weighting factors---these are the Lagrangian shape function values at 'local' - std::vector<Dune::FieldVector<ctype,1> > w; - localFiniteElement_.localBasis().evaluateFunction(local,w); + /** \brief Evaluate the function */ + TargetSpace evaluate(const Dune::FieldVector<ctype, dim>& local) const + { + TargetSpace result; - result.r = 0; - for (size_t i=0; i<w.size(); i++) - result.r.axpy(w[i][0], translationCoefficients_[i]); + // Evaluate the weighting factors---these are the Lagrangian shape function values at 'local' + std::vector<Dune::FieldVector<ctype,1> > w; + localFiniteElement_.localBasis().evaluateFunction(local,w); - result.q = orientationFEFunction_->evaluate(local); - return result; - } + result.r = 0; + for (size_t i=0; i<w.size(); i++) + result.r.axpy(w[i][0], translationCoefficients_[i]); - /** \brief Evaluate the derivative of the function */ - DerivativeType evaluateDerivative(const Dune::FieldVector<ctype, dim>& local) const - { - DerivativeType result(0); + result.q = orientationFEFunction_->evaluate(local); + return result; + } - // get translation part - std::vector<Dune::FieldMatrix<ctype,1,dim> > sfDer(translationCoefficients_.size()); - localFiniteElement_.localBasis().evaluateJacobian(local, sfDer); + /** \brief Evaluate the derivative of the function */ + DerivativeType evaluateDerivative(const Dune::FieldVector<ctype, dim>& local) const + { + DerivativeType result(0); - for (size_t i=0; i<translationCoefficients_.size(); i++) - for (int j=0; j<3; j++) - result[j].axpy(translationCoefficients_[i][j], sfDer[i][0]); + // get translation part + std::vector<Dune::FieldMatrix<ctype,1,dim> > sfDer(translationCoefficients_.size()); + localFiniteElement_.localBasis().evaluateJacobian(local, sfDer); - // get orientation part - Dune::FieldMatrix<field_type,4,dim> qResult = orientationFEFunction_->evaluateDerivative(local); - for (int i=0; i<4; i++) - for (int j=0; j<dim; j++) - result[3+i][j] = qResult[i][j]; + for (size_t i=0; i<translationCoefficients_.size(); i++) + for (int j=0; j<3; j++) + result[j].axpy(translationCoefficients_[i][j], sfDer[i][0]); - return result; - } + // get orientation part + Dune::FieldMatrix<field_type,4,dim> qResult = orientationFEFunction_->evaluateDerivative(local); + for (int i=0; i<4; i++) + for (int j=0; j<dim; j++) + result[3+i][j] = qResult[i][j]; - /** \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, dim>& local, - const TargetSpace& q) const - { - DerivativeType result(0); - - // get translation part - std::vector<Dune::FieldMatrix<ctype,1,dim> > sfDer(translationCoefficients_.size()); - localFiniteElement_.localBasis().evaluateJacobian(local, sfDer); - - for (size_t i=0; i<translationCoefficients_.size(); i++) - for (int j=0; j<3; j++) - result[j].axpy(translationCoefficients_[i][j], sfDer[i][0]); - - // get orientation part - Dune::FieldMatrix<field_type,4,dim> qResult = orientationFEFunction_->evaluateDerivative(local,q.q); - for (int i=0; i<4; i++) - for (int j=0; j<dim; j++) - result[3+i][j] = qResult[i][j]; - - return result; - } + 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, dim>& local, + const TargetSpace& q) const + { + DerivativeType result(0); + + // get translation part + std::vector<Dune::FieldMatrix<ctype,1,dim> > sfDer(translationCoefficients_.size()); + localFiniteElement_.localBasis().evaluateJacobian(local, sfDer); + + for (size_t i=0; i<translationCoefficients_.size(); i++) + for (int j=0; j<3; j++) + result[j].axpy(translationCoefficients_[i][j], sfDer[i][0]); + + // get orientation part + Dune::FieldMatrix<field_type,4,dim> qResult = orientationFEFunction_->evaluateDerivative(local,q.q); + for (int i=0; i<4; i++) + for (int j=0; j<dim; j++) + result[3+i][j] = qResult[i][j]; - /** \brief Evaluate the derivative of the function value with respect to a coefficient */ - void evaluateDerivativeOfValueWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, - int coefficient, - Dune::FieldMatrix<field_type,embeddedDim,embeddedDim>& derivative) const - { - derivative = 0; - - // Translation part - std::vector<Dune::FieldVector<ctype,1> > w; - localFiniteElement_.localBasis().evaluateFunction(local,w); - for (int i=0; i<3; i++) - derivative[i][i] = w[coefficient]; - - // Rotation part - Dune::FieldMatrix<field_type,4,4> qDerivative; - orientationFEFunction_->evaluateDerivativeOfValueWRTCoefficient(local,coefficient,qDerivative); - for (int i=0; i<4; i++) - for (int j=0; j<4; j++) - derivative[3+i][3+j] = qDerivative[i][j]; - } + return result; + } + + /** \brief Evaluate the derivative of the function value with respect to a coefficient */ + void evaluateDerivativeOfValueWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, + int coefficient, + Dune::FieldMatrix<field_type,embeddedDim,embeddedDim>& derivative) const + { + derivative = 0; - /** \brief Evaluate the derivative of the function value with respect to a coefficient */ - void evaluateFDDerivativeOfValueWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, + // Translation part + std::vector<Dune::FieldVector<ctype,1> > w; + localFiniteElement_.localBasis().evaluateFunction(local,w); + for (int i=0; i<3; i++) + derivative[i][i] = w[coefficient]; + + // Rotation part + Dune::FieldMatrix<field_type,4,4> qDerivative; + orientationFEFunction_->evaluateDerivativeOfValueWRTCoefficient(local,coefficient,qDerivative); + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) + derivative[3+i][3+j] = qDerivative[i][j]; + } + + /** \brief Evaluate the derivative of the function value with respect to a coefficient */ + void evaluateFDDerivativeOfValueWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, int coefficient, Dune::FieldMatrix<field_type,embeddedDim,embeddedDim>& derivative) const - { - derivative = 0; - - // Translation part - std::vector<Dune::FieldVector<ctype,1> > w; - localFiniteElement_.localBasis().evaluateFunction(local,w); - for (int i=0; i<3; i++) - derivative[i][i] = w[coefficient]; - - // Rotation part - Dune::FieldMatrix<ctype,4,4> qDerivative; - orientationFEFunction_->evaluateFDDerivativeOfValueWRTCoefficient(local,coefficient,qDerivative); - for (int i=0; i<4; i++) - for (int j=0; j<4; j++) - derivative[3+i][3+j] = qDerivative[i][j]; - } - - /** \brief Evaluate the derivative of the gradient of the function with respect to a coefficient */ - void evaluateDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, - int coefficient, - DerivativeOfGradientWRTCoefficientType& derivative) const - { - derivative = field_type(0); - - // Translation part - std::vector<Dune::FieldMatrix<ctype,1,dim> > w; - localFiniteElement_.localBasis().evaluateJacobian(local,w); - for (int i=0; i<3; i++) - derivative[i][i] = w[coefficient][0]; - - // Rotation part - Tensor3<field_type,4,4,dim> qDerivative; - orientationFEFunction_->evaluateDerivativeOfGradientWRTCoefficient(local,coefficient,qDerivative); - for (int i=0; i<4; i++) - for (int j=0; j<4; j++) - for (int k=0; k<dim; k++) - derivative[3+i][3+j][k] = qDerivative[i][j][k]; - } + { + derivative = 0; - /** \brief Evaluate the derivative of the gradient of the function with respect to a coefficient */ - void evaluateFDDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, + // Translation part + std::vector<Dune::FieldVector<ctype,1> > w; + localFiniteElement_.localBasis().evaluateFunction(local,w); + for (int i=0; i<3; i++) + derivative[i][i] = w[coefficient]; + + // Rotation part + Dune::FieldMatrix<ctype,4,4> qDerivative; + orientationFEFunction_->evaluateFDDerivativeOfValueWRTCoefficient(local,coefficient,qDerivative); + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) + derivative[3+i][3+j] = qDerivative[i][j]; + } + + /** \brief Evaluate the derivative of the gradient of the function with respect to a coefficient */ + void evaluateDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, + int coefficient, + DerivativeOfGradientWRTCoefficientType& derivative) const + { + derivative = field_type(0); + + // Translation part + std::vector<Dune::FieldMatrix<ctype,1,dim> > w; + localFiniteElement_.localBasis().evaluateJacobian(local,w); + for (int i=0; i<3; i++) + derivative[i][i] = w[coefficient][0]; + + // Rotation part + Tensor3<field_type,4,4,dim> qDerivative; + orientationFEFunction_->evaluateDerivativeOfGradientWRTCoefficient(local,coefficient,qDerivative); + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) + for (int k=0; k<dim; k++) + derivative[3+i][3+j][k] = qDerivative[i][j][k]; + } + + /** \brief Evaluate the derivative of the gradient of the function with respect to a coefficient */ + void evaluateFDDerivativeOfGradientWRTCoefficient(const Dune::FieldVector<ctype, dim>& local, int coefficient, DerivativeOfGradientWRTCoefficientType& derivative) const - { - derivative = 0; - - // Translation part - std::vector<Dune::FieldMatrix<ctype,1,dim> > w; - localFiniteElement_.localBasis().evaluateJacobian(local,w); - for (int i=0; i<3; i++) - derivative[i][i] = w[coefficient][0]; - - // Rotation part - Tensor3<ctype,4,4,dim> qDerivative; - orientationFEFunction_->evaluateFDDerivativeOfGradientWRTCoefficient(local,coefficient,qDerivative); - for (int i=0; i<4; i++) - for (int j=0; j<4; j++) - for (int k=0; k<dim; k++) - derivative[3+i][3+j][k] = qDerivative[i][j][k]; - } - - TargetSpace coefficient(int i) const { - return TargetSpace(translationCoefficients_[i],orientationFEFunction_->coefficient(i)); - - } + { + derivative = 0; + + // Translation part + std::vector<Dune::FieldMatrix<ctype,1,dim> > w; + localFiniteElement_.localBasis().evaluateJacobian(local,w); + for (int i=0; i<3; i++) + derivative[i][i] = w[coefficient][0]; + + // Rotation part + Tensor3<ctype,4,4,dim> qDerivative; + orientationFEFunction_->evaluateFDDerivativeOfGradientWRTCoefficient(local,coefficient,qDerivative); + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) + for (int k=0; k<dim; k++) + derivative[3+i][3+j][k] = qDerivative[i][j][k]; + } + + TargetSpace coefficient(int i) const { + return TargetSpace(translationCoefficients_[i],orientationFEFunction_->coefficient(i)); + + } private: - /** \brief The scalar local finite element, which provides the weighting factors - \todo We really only need the local basis - */ - const LocalFiniteElement& localFiniteElement_; + /** \brief The scalar local finite element, which provides the weighting factors + \todo We really only need the local basis + */ + const LocalFiniteElement& localFiniteElement_; - // The two factors of a RigidBodyMotion - //LocalGeodesicFEFunction<dim,ctype,RealTuple<3> > translationFEFunction_; + // The two factors of a RigidBodyMotion + //LocalGeodesicFEFunction<dim,ctype,RealTuple<3> > translationFEFunction_; - std::vector<Dune::FieldVector<field_type,3> > translationCoefficients_; + std::vector<Dune::FieldVector<field_type,3> > translationCoefficients_; - std::unique_ptr<LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,Rotation<field_type,3> > > orientationFEFunction_; + std::unique_ptr<LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,Rotation<field_type,3> > > orientationFEFunction_; }; diff --git a/dune/gfe/localgfetestfunctionbasis.hh b/dune/gfe/localgfetestfunctionbasis.hh index b1968e0e..947f445e 100644 --- a/dune/gfe/localgfetestfunctionbasis.hh +++ b/dune/gfe/localgfetestfunctionbasis.hh @@ -29,64 +29,64 @@ class LocalGfeTestFunctionInterpolation; template <class LagrangeLfe, class TargetSpace> class LocalGfeTestFunctionFiniteElement { - typedef typename LagrangeLfe::Traits::LocalBasisType::Traits LagrangeBasisTraits; - typedef LocalGfeTestFunctionBasis<LagrangeLfe, TargetSpace> LocalBasis; - typedef LocalGfeTestFunctionInterpolation<LagrangeLfe, TargetSpace> LocalInterpolation; + typedef typename LagrangeLfe::Traits::LocalBasisType::Traits LagrangeBasisTraits; + typedef LocalGfeTestFunctionBasis<LagrangeLfe, TargetSpace> LocalBasis; + typedef LocalGfeTestFunctionInterpolation<LagrangeLfe, TargetSpace> LocalInterpolation; public: - //! Traits - typedef Dune::LocalFiniteElementTraits<LocalBasis,typename LagrangeLfe::Traits::LocalCoefficientsType, LocalInterpolation> Traits; - - /** Construct local finite element from the base coefficients and Lagrange local finite element. - * - * \param lfe - The Lagrange local finite element. - * \param baseCoeff - The coefficients of the base points the tangent spaces live at. - */ - LocalGfeTestFunctionFiniteElement(const LagrangeLfe& lfe, const std::vector<TargetSpace> baseCoeff) : - baseCoeff_(baseCoeff), - basis_(lfe, baseCoeff_), - coefficients_(lfe.localCoefficients()), - gt_(lfe.type()) - {} - - /** \brief Get reference to the local basis.*/ - const typename Traits::LocalBasisType& localBasis () const - { - return basis_; - } - - /** \brief Get reference to the local coefficients. */ - const typename Traits::LocalCoefficientsType& localCoefficients () const - { - return coefficients_; - } - - /** \brief Get reference to the local interpolation handler. */ - const typename Traits::LocalInterpolationType& localInterpolation () const - { - return interpolation_; - } - - /** \brief Get the element type this finite element lives on. */ - Dune::GeometryType type () const - { - return gt_; - } - - /** \brief Get base coefficients. */ - const std::vector<TargetSpace>& getBaseCoefficients() const {return baseCoeff_;} + //! Traits + typedef Dune::LocalFiniteElementTraits<LocalBasis,typename LagrangeLfe::Traits::LocalCoefficientsType, LocalInterpolation> Traits; + + /** Construct local finite element from the base coefficients and Lagrange local finite element. + * + * \param lfe - The Lagrange local finite element. + * \param baseCoeff - The coefficients of the base points the tangent spaces live at. + */ + LocalGfeTestFunctionFiniteElement(const LagrangeLfe& lfe, const std::vector<TargetSpace> baseCoeff) : + baseCoeff_(baseCoeff), + basis_(lfe, baseCoeff_), + coefficients_(lfe.localCoefficients()), + gt_(lfe.type()) + {} + + /** \brief Get reference to the local basis.*/ + const typename Traits::LocalBasisType& localBasis () const + { + return basis_; + } + + /** \brief Get reference to the local coefficients. */ + const typename Traits::LocalCoefficientsType& localCoefficients () const + { + return coefficients_; + } + + /** \brief Get reference to the local interpolation handler. */ + const typename Traits::LocalInterpolationType& localInterpolation () const + { + return interpolation_; + } + + /** \brief Get the element type this finite element lives on. */ + Dune::GeometryType type () const + { + return gt_; + } + + /** \brief Get base coefficients. */ + const std::vector<TargetSpace>& getBaseCoefficients() const {return baseCoeff_;} private: - const std::vector<TargetSpace> baseCoeff_; - LocalBasis basis_; - const typename Traits::LocalCoefficientsType coefficients_; - LocalInterpolation interpolation_; - Dune::GeometryType gt_; + const std::vector<TargetSpace> baseCoeff_; + LocalBasis basis_; + const typename Traits::LocalCoefficientsType coefficients_; + LocalInterpolation interpolation_; + Dune::GeometryType gt_; }; -/** \brief A local basis of the first variations of a given geodesic finite element function. +/** \brief A local basis of the first variations of a given geodesic finite element function. * * \tparam LocalFiniteElement A Lagrangian finite element whose shape functions define the interpolation weights * \tparam TargetSpace The manifold that the function takes its values in @@ -97,109 +97,109 @@ private: template <class LocalFiniteElement, class TargetSpace> class LocalGfeTestFunctionBasis { - typedef typename LocalFiniteElement::Traits::LocalBasisType::Traits LagrangeBasisTraits; - static const int dim = LagrangeBasisTraits::dimDomain; - typedef typename LagrangeBasisTraits::DomainFieldType ctype; - typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector; - static const int embeddedDim = EmbeddedTangentVector::dimension; - - static const int spaceDim = TargetSpace::TangentVector::dimension; - -public : - //! The local basis traits - typedef Dune::LocalBasisTraits<ctype, dim, Dune::FieldVector<ctype,dim>, - typename EmbeddedTangentVector::value_type, embeddedDim, std::array<EmbeddedTangentVector,spaceDim>, - std::array<Dune::FieldMatrix<ctype, embeddedDim, dim>,spaceDim>> Traits; - - /** \brief Constructor - */ - LocalGfeTestFunctionBasis(const LocalFiniteElement& localFiniteElement, - const std::vector<TargetSpace>& baseCoefficients) - : localGFEFunction_(localFiniteElement, baseCoefficients) - {} - - /** \brief The number of Lagrange points, NOT the number of basis functions */ - unsigned int size() const - { - return localGFEFunction_.size(); - } - - /** \brief Evaluate all shape functions at the given point */ - void evaluateFunction(const typename Traits::DomainType& local, - std::vector<typename Traits::RangeType>& out) const; + typedef typename LocalFiniteElement::Traits::LocalBasisType::Traits LagrangeBasisTraits; + static const int dim = LagrangeBasisTraits::dimDomain; + typedef typename LagrangeBasisTraits::DomainFieldType ctype; + typedef typename TargetSpace::EmbeddedTangentVector EmbeddedTangentVector; + static const int embeddedDim = EmbeddedTangentVector::dimension; - /** \brief Evaluate the derivatives of all shape functions function */ - void evaluateJacobian(const typename Traits::DomainType& local, - std::vector<typename Traits::JacobianType>& out) const; + static const int spaceDim = TargetSpace::TangentVector::dimension; - /** \brief Polynomial order */ - unsigned int order() const - { - return localGFEFunction_.localFiniteElement_.order(); - } +public: + //! The local basis traits + typedef Dune::LocalBasisTraits<ctype, dim, Dune::FieldVector<ctype,dim>, + typename EmbeddedTangentVector::value_type, embeddedDim, std::array<EmbeddedTangentVector,spaceDim>, + std::array<Dune::FieldMatrix<ctype, embeddedDim, dim>,spaceDim> > Traits; + + /** \brief Constructor + */ + LocalGfeTestFunctionBasis(const LocalFiniteElement& localFiniteElement, + const std::vector<TargetSpace>& baseCoefficients) + : localGFEFunction_(localFiniteElement, baseCoefficients) + {} + + /** \brief The number of Lagrange points, NOT the number of basis functions */ + unsigned int size() const + { + return localGFEFunction_.size(); + } + + /** \brief Evaluate all shape functions at the given point */ + void evaluateFunction(const typename Traits::DomainType& local, + std::vector<typename Traits::RangeType>& out) const; + + /** \brief Evaluate the derivatives of all shape functions function */ + void evaluateJacobian(const typename Traits::DomainType& local, + std::vector<typename Traits::JacobianType>& out) const; + + /** \brief Polynomial order */ + unsigned int order() const + { + return localGFEFunction_.localFiniteElement_.order(); + } private: - /** \brief The scalar local finite element, which provides the weighting factors - */ - const LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> localGFEFunction_; + /** \brief The scalar local finite element, which provides the weighting factors + */ + const LocalGeodesicFEFunction<dim,ctype,LocalFiniteElement,TargetSpace> localGFEFunction_; }; template <class LocalFiniteElement, class TargetSpace> void LocalGfeTestFunctionBasis<LocalFiniteElement,TargetSpace>::evaluateFunction(const typename Traits::DomainType& local, - std::vector<typename Traits::RangeType>& out) const + std::vector<typename Traits::RangeType>& out) const { - out.resize(size()); - - for (size_t i=0; i<size(); i++) { - - Dune::FieldMatrix< double, embeddedDim, embeddedDim > derivative; - - /** \todo This call internally keeps computing the value of the gfe function at 'local'. - * This is expensive. Eventually we should precompute it once and reused the result. */ - localGFEFunction_.evaluateDerivativeOfValueWRTCoefficient (local, i, derivative); - Dune::FieldMatrix<ctype,spaceDim,embeddedDim> basisVectors = localGFEFunction_.coefficient(i).orthonormalFrame(); - - for (int j=0; j<spaceDim; j++) - derivative.mv(basisVectors[j], out[i][j]); - - } - + out.resize(size()); + + for (size_t i=0; i<size(); i++) { + + Dune::FieldMatrix< double, embeddedDim, embeddedDim > derivative; + + /** \todo This call internally keeps computing the value of the gfe function at 'local'. + * This is expensive. Eventually we should precompute it once and reused the result. */ + localGFEFunction_.evaluateDerivativeOfValueWRTCoefficient (local, i, derivative); + Dune::FieldMatrix<ctype,spaceDim,embeddedDim> basisVectors = localGFEFunction_.coefficient(i).orthonormalFrame(); + + for (int j=0; j<spaceDim; j++) + derivative.mv(basisVectors[j], out[i][j]); + + } + } template <class LocalFiniteElement, class TargetSpace> void LocalGfeTestFunctionBasis<LocalFiniteElement,TargetSpace>::evaluateJacobian(const typename Traits::DomainType& local, - std::vector<typename Traits::JacobianType>& out) const + std::vector<typename Traits::JacobianType>& out) const { - out.resize(size()); - - for (size_t i=0; i<size(); i++) { - - /** \todo This call internally keeps computing the value of the gfe function at 'local'. - * This is expensive. Eventually we should precompute it once and reused the result. */ - Tensor3< double, embeddedDim, embeddedDim, dim > derivative; - localGFEFunction_.evaluateDerivativeOfGradientWRTCoefficient (local, i, derivative); - - Dune::FieldMatrix<ctype,spaceDim,embeddedDim> basisVectors = localGFEFunction_.coefficient(i).orthonormalFrame(); - - for (int j=0; j<spaceDim; j++) { - - out[i][j] = 0; - - // Contract the second index of the derivative with the tangent vector at the i-th Lagrange point. - // Add that to the result. - for (int k=0; k<embeddedDim; k++) - for (int l=0; l<embeddedDim; l++) - for (int m=0; m<dim; m++) - out[i][j][k][m] += derivative[k][l][m] * basisVectors[j][l]; - } + out.resize(size()); + + for (size_t i=0; i<size(); i++) { + + /** \todo This call internally keeps computing the value of the gfe function at 'local'. + * This is expensive. Eventually we should precompute it once and reused the result. */ + Tensor3< double, embeddedDim, embeddedDim, dim > derivative; + localGFEFunction_.evaluateDerivativeOfGradientWRTCoefficient (local, i, derivative); + + Dune::FieldMatrix<ctype,spaceDim,embeddedDim> basisVectors = localGFEFunction_.coefficient(i).orthonormalFrame(); + + for (int j=0; j<spaceDim; j++) { + + out[i][j] = 0; + + // Contract the second index of the derivative with the tangent vector at the i-th Lagrange point. + // Add that to the result. + for (int k=0; k<embeddedDim; k++) + for (int l=0; l<embeddedDim; l++) + for (int m=0; m<dim; m++) + out[i][j][k][m] += derivative[k][l][m] * basisVectors[j][l]; } - + } + } template <class LocalFiniteElement, class TargetSpace> -class LocalGfeTestFunctionInterpolation +class LocalGfeTestFunctionInterpolation {}; #endif diff --git a/dune/gfe/localprojectedfefunction.hh b/dune/gfe/localprojectedfefunction.hh index 7551fbb9..46acb95a 100644 --- a/dune/gfe/localprojectedfefunction.hh +++ b/dune/gfe/localprojectedfefunction.hh @@ -52,8 +52,8 @@ namespace Dune { */ LocalProjectedFEFunction(const LocalFiniteElement& localFiniteElement, const std::vector<TargetSpace>& coefficients) - : localFiniteElement_(localFiniteElement), - coefficients_(coefficients) + : localFiniteElement_(localFiniteElement), + coefficients_(coefficients) { assert(localFiniteElement_.localBasis().size() == coefficients_.size()); } @@ -132,7 +132,7 @@ namespace Dune { evaluateDerivative(const Dune::FieldVector<ctype, dim>& local) const { if constexpr(conformingFlag) - { + { // the function value at the point where we are evaluating the derivative TargetSpace q = evaluate(local); @@ -140,7 +140,7 @@ namespace Dune { return evaluateDerivative(local, q); } else { - std::vector<Dune::FieldMatrix<ctype, 1, dim>> wDer; + std::vector<Dune::FieldMatrix<ctype, 1, dim> > wDer; localFiniteElement_.localBasis().evaluateJacobian(local, wDer); Dune::FieldMatrix<RT, embeddedDim, dim> derivative(0); @@ -209,7 +209,7 @@ namespace Dune { * \param polar The image of the projection, i.e., the polar factor of A */ static std::array<std::array<FieldMatrix<field_type,3,3>, 3>, 3> derivativeOfProjection(const FieldMatrix<field_type,3,3>& A, - FieldMatrix<field_type,3,3>& polar) + FieldMatrix<field_type,3,3>& polar) { std::array<std::array<FieldMatrix<field_type,3,3>, 3>, 3> result; @@ -279,8 +279,8 @@ namespace Dune { */ LocalProjectedFEFunction(const LocalFiniteElement& localFiniteElement, const std::vector<TargetSpace>& coefficients) - : localFiniteElement_(localFiniteElement), - coefficients_(coefficients) + : localFiniteElement_(localFiniteElement), + coefficients_(coefficients) { assert(localFiniteElement_.localBasis().size() == coefficients_.size()); } @@ -437,17 +437,17 @@ namespace Dune { */ LocalProjectedFEFunction(const LocalFiniteElement& localFiniteElement, const std::vector<TargetSpace>& coefficients) - : localFiniteElement_(localFiniteElement), + : localFiniteElement_(localFiniteElement), translationCoefficients_(coefficients.size()) { assert(localFiniteElement.localBasis().size() == coefficients.size()); for (size_t i=0; i<coefficients.size(); i++) - translationCoefficients_[i] = coefficients[i].r; + translationCoefficients_[i] = coefficients[i].r; std::vector<Rotation<field_type,3> > orientationCoefficients(coefficients.size()); for (size_t i=0; i<coefficients.size(); i++) - orientationCoefficients[i] = coefficients[i].q; + orientationCoefficients[i] = coefficients[i].q; orientationFunction_ = std::make_unique<LocalProjectedFEFunction<dim,ctype,LocalFiniteElement,Rotation<field_type,3> > > (localFiniteElement,orientationCoefficients); } @@ -482,7 +482,7 @@ namespace Dune { result.r = 0; for (size_t i=0; i<w.size(); i++) - result.r.axpy(w[i][0], translationCoefficients_[i]); + result.r.axpy(w[i][0], translationCoefficients_[i]); result.q = orientationFunction_->evaluate(local); @@ -513,15 +513,15 @@ namespace Dune { localFiniteElement_.localBasis().evaluateJacobian(local, sfDer); for (size_t i=0; i<translationCoefficients_.size(); i++) - for (int j=0; j<3; j++) - result[j].axpy(translationCoefficients_[i][j], sfDer[i][0]); + for (int j=0; j<3; j++) + result[j].axpy(translationCoefficients_[i][j], sfDer[i][0]); // get orientation part Dune::FieldMatrix<field_type,4,dim> qResult = orientationFunction_->evaluateDerivative(local,q.q); for (int i=0; i<4; i++) - for (int j=0; j<dim; j++) - result[3+i][j] = qResult[i][j]; + for (int j=0; j<dim; j++) + result[3+i][j] = qResult[i][j]; return result; } diff --git a/dune/gfe/localquickanddirtyfefunction.hh b/dune/gfe/localquickanddirtyfefunction.hh index 34603132..76a8f105 100644 --- a/dune/gfe/localquickanddirtyfefunction.hh +++ b/dune/gfe/localquickanddirtyfefunction.hh @@ -53,9 +53,9 @@ namespace Dune { * \param coefficients Values of the function at the Lagrange points */ LocalQuickAndDirtyFEFunction(const LocalFiniteElement& localFiniteElement, - const std::vector<TargetSpace>& coefficients) - : localFiniteElement_(localFiniteElement), - coefficients_(coefficients) + const std::vector<TargetSpace>& coefficients) + : localFiniteElement_(localFiniteElement), + coefficients_(coefficients) { assert(localFiniteElement_.localBasis().size() == coefficients_.size()); } diff --git a/dune/gfe/localtangentfefunction.hh b/dune/gfe/localtangentfefunction.hh index 349a4644..ea8a86d4 100644 --- a/dune/gfe/localtangentfefunction.hh +++ b/dune/gfe/localtangentfefunction.hh @@ -38,9 +38,9 @@ namespace Dune { * \param coefficients Values of the function at the Lagrange points */ LocalTangentFEFunction(const LocalFiniteElement& localFiniteElement, - const std::vector<TargetSpace>& coefficients) - : localFiniteElement_(localFiniteElement), - coefficients_(coefficients) + const std::vector<TargetSpace>& coefficients) + : localFiniteElement_(localFiniteElement), + coefficients_(coefficients) { assert(localFiniteElement_.localBasis().size() == coefficients_.size()); } diff --git a/dune/gfe/maxnormtrustregion.hh b/dune/gfe/maxnormtrustregion.hh index 9bb4a301..50d649da 100644 --- a/dune/gfe/maxnormtrustregion.hh +++ b/dune/gfe/maxnormtrustregion.hh @@ -10,89 +10,89 @@ class MaxNormTrustRegion { public: - MaxNormTrustRegion(size_t size, field_type initialRadius) - : obstacles_(size) - { - set(initialRadius); - } - - void set(field_type radius) { + MaxNormTrustRegion(size_t size, field_type initialRadius) + : obstacles_(size) + { + set(initialRadius); + } - radius_ = radius; + void set(field_type radius) { - for (size_t i=0; i<obstacles_.size(); i++) { + radius_ = radius; - for (int k=0; k<blocksize; k++) { + for (size_t i=0; i<obstacles_.size(); i++) { - obstacles_[i].lower(k) = -radius; - obstacles_[i].upper(k) = radius; + for (int k=0; k<blocksize; k++) { - } + obstacles_[i].lower(k) = -radius; + obstacles_[i].upper(k) = radius; - } + } } - /** \brief Set trust region radius with a separate scaling for each vector block component - */ - void set(field_type radius, const Dune::FieldVector<field_type, blocksize>& scaling) { + } - radius_ = radius; + /** \brief Set trust region radius with a separate scaling for each vector block component + */ + void set(field_type radius, const Dune::FieldVector<field_type, blocksize>& scaling) { - for (size_t i=0; i<obstacles_.size(); i++) { + radius_ = radius; - for (int k=0; k<blocksize; k++) { + for (size_t i=0; i<obstacles_.size(); i++) { - obstacles_[i].lower(k) = -radius*scaling[k]; - obstacles_[i].upper(k) = radius*scaling[k]; + for (int k=0; k<blocksize; k++) { - } - } + obstacles_[i].lower(k) = -radius*scaling[k]; + obstacles_[i].upper(k) = radius*scaling[k]; + } } - /** \brief Return true if the given vector is not contained in the trust region */ - bool violates(const Dune::BlockVector<Dune::FieldVector<double,blocksize> >& v) const - { - assert(v.size() == obstacles_.size()); - for (size_t i=0; i<v.size(); i++) - for (size_t j=0; j<blocksize; j++) - if (v[i][j] < obstacles_[i].lower(j) or v[i][j] > obstacles_[i].upper(j)) - return true; + } - return false; - } + /** \brief Return true if the given vector is not contained in the trust region */ + bool violates(const Dune::BlockVector<Dune::FieldVector<double,blocksize> >& v) const + { + assert(v.size() == obstacles_.size()); + for (size_t i=0; i<v.size(); i++) + for (size_t j=0; j<blocksize; j++) + if (v[i][j] < obstacles_[i].lower(j) or v[i][j] > obstacles_[i].upper(j)) + return true; - field_type radius() const { - return radius_; - } + return false; + } - void scale(field_type factor) { + field_type radius() const { + return radius_; + } - radius_ *= factor; + void scale(field_type factor) { - for (size_t i=0; i<obstacles_.size(); i++) { + radius_ *= factor; - for (int k=0; k<blocksize; k++) { + for (size_t i=0; i<obstacles_.size(); i++) { - obstacles_[i].lower(k) *= factor; - obstacles_[i].upper(k) *= factor; + for (int k=0; k<blocksize; k++) { - } + obstacles_[i].lower(k) *= factor; + obstacles_[i].upper(k) *= factor; - } + } } - const std::vector<BoxConstraint<field_type,blocksize> >& obstacles() const { - return obstacles_; - } + } + + const std::vector<BoxConstraint<field_type,blocksize> >& obstacles() const { + return obstacles_; + } private: - std::vector<BoxConstraint<field_type,blocksize> > obstacles_; + std::vector<BoxConstraint<field_type,blocksize> > obstacles_; - field_type radius_; + field_type radius_; }; diff --git a/dune/gfe/mixedriemanniantrsolver.cc b/dune/gfe/mixedriemanniantrsolver.cc index ea340161..b93229e7 100644 --- a/dune/gfe/mixedriemanniantrsolver.cc +++ b/dune/gfe/mixedriemanniantrsolver.cc @@ -24,556 +24,556 @@ #include <dune/gfe/cosseratvtkwriter.hh> template <class GridType, - class Basis, - class Basis0, class TargetSpace0, - class Basis1, class TargetSpace1> + class Basis, + class Basis0, class TargetSpace0, + class Basis1, class TargetSpace1> void MixedRiemannianTrustRegionSolver<GridType,Basis,Basis0,TargetSpace0,Basis1,TargetSpace1>:: setup(const GridType& grid, const MixedGFEAssembler<Basis, TargetSpace0, TargetSpace1>* assembler, const Basis0& tmpBasis0, const Basis1& tmpBasis1, - const SolutionType& x, - const Dune::BitSetVector<blocksize0>& dirichletNodes0, - const Dune::BitSetVector<blocksize1>& dirichletNodes1, - double tolerance, - int maxTrustRegionSteps, - double initialTrustRegionRadius, - int multigridIterations, - double mgTolerance, - int mu, - int nu1, - int nu2, - int baseIterations, - double baseTolerance, - bool instrumented) + const SolutionType& x, + const Dune::BitSetVector<blocksize0>& dirichletNodes0, + const Dune::BitSetVector<blocksize1>& dirichletNodes1, + double tolerance, + int maxTrustRegionSteps, + double initialTrustRegionRadius, + int multigridIterations, + double mgTolerance, + int mu, + int nu1, + int nu2, + int baseIterations, + double baseTolerance, + bool instrumented) { - int rank = grid.comm().rank(); - - grid_ = &grid; - assembler_ = assembler; - basis0_ = std::unique_ptr<Basis0>(new Basis0(tmpBasis0)); - basis1_ = std::unique_ptr<Basis1>(new Basis1(tmpBasis1)); - x_ = x; - tolerance_ = tolerance; - maxTrustRegionSteps_ = maxTrustRegionSteps; - initialTrustRegionRadius_ = initialTrustRegionRadius; - innerIterations_ = multigridIterations; - innerTolerance_ = mgTolerance; - ignoreNodes0_ = &dirichletNodes0; - ignoreNodes1_ = &dirichletNodes1; - - int numLevels = grid_->maxLevel()+1; - - ////////////////////////////////////////////////////////////////// - // Create global numbering for matrix and vector transfer - ////////////////////////////////////////////////////////////////// + int rank = grid.comm().rank(); + + grid_ = &grid; + assembler_ = assembler; + basis0_ = std::unique_ptr<Basis0>(new Basis0(tmpBasis0)); + basis1_ = std::unique_ptr<Basis1>(new Basis1(tmpBasis1)); + x_ = x; + tolerance_ = tolerance; + maxTrustRegionSteps_ = maxTrustRegionSteps; + initialTrustRegionRadius_ = initialTrustRegionRadius; + innerIterations_ = multigridIterations; + innerTolerance_ = mgTolerance; + ignoreNodes0_ = &dirichletNodes0; + ignoreNodes1_ = &dirichletNodes1; + + int numLevels = grid_->maxLevel()+1; + + ////////////////////////////////////////////////////////////////// + // Create global numbering for matrix and vector transfer + ////////////////////////////////////////////////////////////////// #if 0 - guIndex_ = std::unique_ptr<GUIndex>(new GUIndex(grid_->leafGridView())); + guIndex_ = std::unique_ptr<GUIndex>(new GUIndex(grid_->leafGridView())); #endif - // //////////////////////////////// - // Create a multigrid solver - // //////////////////////////////// + // //////////////////////////////// + // Create a multigrid solver + // //////////////////////////////// #ifdef HAVE_IPOPT - // First create an IPOpt base solver - auto baseSolver0 = std::make_shared<QuadraticIPOptSolver<MatrixType00,CorrectionType0>>(); - baseSolver0->setVerbosity(NumProc::QUIET); - baseSolver0->setTolerance(baseTolerance); - auto baseSolver1 = std::make_shared<QuadraticIPOptSolver<MatrixType11,CorrectionType1>>(); - baseSolver1->setVerbosity(NumProc::QUIET); - baseSolver1->setTolerance(baseTolerance); + // First create an IPOpt base solver + auto baseSolver0 = std::make_shared<QuadraticIPOptSolver<MatrixType00,CorrectionType0> >(); + baseSolver0->setVerbosity(NumProc::QUIET); + baseSolver0->setTolerance(baseTolerance); + auto baseSolver1 = std::make_shared<QuadraticIPOptSolver<MatrixType11,CorrectionType1> >(); + baseSolver1->setVerbosity(NumProc::QUIET); + baseSolver1->setTolerance(baseTolerance); #else - // First create a Gauss-seidel base solver - auto baseSolverStep0 = std::make_shared< TrustRegionGSStep<MatrixType00, CorrectionType0> >(); - auto baseSolverStep1 = std::make_shared< TrustRegionGSStep<MatrixType11, CorrectionType1> >(); - - // Hack: the two-norm may not scale all that well, but it is fast! - auto baseNorm0 = std::make_shared< TwoNorm<CorrectionType0> >(); - auto baseNorm1 = std::make_shared< TwoNorm<CorrectionType1> >(); - - auto baseSolver0 = std::make_shared< ::LoopSolver<CorrectionType0> >(baseSolverStep0, - baseIterations, - baseTolerance, - baseNorm0, - Solver::QUIET); - - auto baseSolver1 = std::make_shared< ::LoopSolver<CorrectionType1> >(baseSolverStep1, - baseIterations, - baseTolerance, - baseNorm1, - Solver::QUIET); + // First create a Gauss-seidel base solver + auto baseSolverStep0 = std::make_shared< TrustRegionGSStep<MatrixType00, CorrectionType0> >(); + auto baseSolverStep1 = std::make_shared< TrustRegionGSStep<MatrixType11, CorrectionType1> >(); + + // Hack: the two-norm may not scale all that well, but it is fast! + auto baseNorm0 = std::make_shared< TwoNorm<CorrectionType0> >(); + auto baseNorm1 = std::make_shared< TwoNorm<CorrectionType1> >(); + + auto baseSolver0 = std::make_shared< ::LoopSolver<CorrectionType0> >(baseSolverStep0, + baseIterations, + baseTolerance, + baseNorm0, + Solver::QUIET); + + auto baseSolver1 = std::make_shared< ::LoopSolver<CorrectionType1> >(baseSolverStep1, + baseIterations, + baseTolerance, + baseNorm1, + Solver::QUIET); #endif - // Transfer all Dirichlet data to the master processor + // Transfer all Dirichlet data to the master processor #if 0 - VectorCommunicator<GUIndex, Dune::BitSetVector<blocksize> > vectorComm(*guIndex_, 0); - Dune::BitSetVector<blocksize>* globalDirichletNodes = NULL; - globalDirichletNodes = new Dune::BitSetVector<blocksize>(vectorComm.reduceCopy(dirichletNodes)); + VectorCommunicator<GUIndex, Dune::BitSetVector<blocksize> > vectorComm(*guIndex_, 0); + Dune::BitSetVector<blocksize>* globalDirichletNodes = NULL; + globalDirichletNodes = new Dune::BitSetVector<blocksize>(vectorComm.reduceCopy(dirichletNodes)); #endif - Dune::BitSetVector<blocksize0>* globalDirichletNodes0 = NULL; - globalDirichletNodes0 = new Dune::BitSetVector<blocksize0>(dirichletNodes0); - Dune::BitSetVector<blocksize1>* globalDirichletNodes1 = NULL; - globalDirichletNodes1 = new Dune::BitSetVector<blocksize1>(dirichletNodes1); + Dune::BitSetVector<blocksize0>* globalDirichletNodes0 = NULL; + globalDirichletNodes0 = new Dune::BitSetVector<blocksize0>(dirichletNodes0); + Dune::BitSetVector<blocksize1>* globalDirichletNodes1 = NULL; + globalDirichletNodes1 = new Dune::BitSetVector<blocksize1>(dirichletNodes1); - // Make pre and postsmoothers - auto presmoother0 = std::make_shared<TrustRegionGSStep<MatrixType00, CorrectionType0> >(); - auto postsmoother0 = std::make_shared<TrustRegionGSStep<MatrixType00, CorrectionType0> >(); + // Make pre and postsmoothers + auto presmoother0 = std::make_shared<TrustRegionGSStep<MatrixType00, CorrectionType0> >(); + auto postsmoother0 = std::make_shared<TrustRegionGSStep<MatrixType00, CorrectionType0> >(); - mmgStep0 = new MonotoneMGStep<MatrixType00, CorrectionType0>; + mmgStep0 = new MonotoneMGStep<MatrixType00, CorrectionType0>; - mmgStep0->setMGType(mu, nu1, nu2); - mmgStep0->ignoreNodes_ = globalDirichletNodes0; - mmgStep0->setBaseSolver(baseSolver0); - mmgStep0->setSmoother(presmoother0, postsmoother0); - mmgStep0->setObstacleRestrictor(std::make_shared<MandelObstacleRestrictor<CorrectionType0>>()); - mmgStep0->setVerbosity(Solver::QUIET); + mmgStep0->setMGType(mu, nu1, nu2); + mmgStep0->ignoreNodes_ = globalDirichletNodes0; + mmgStep0->setBaseSolver(baseSolver0); + mmgStep0->setSmoother(presmoother0, postsmoother0); + mmgStep0->setObstacleRestrictor(std::make_shared<MandelObstacleRestrictor<CorrectionType0> >()); + mmgStep0->setVerbosity(Solver::QUIET); - auto presmoother1 = std::make_shared<TrustRegionGSStep<MatrixType11, CorrectionType1> >(); - auto postsmoother1 = std::make_shared<TrustRegionGSStep<MatrixType11, CorrectionType1> >(); + auto presmoother1 = std::make_shared<TrustRegionGSStep<MatrixType11, CorrectionType1> >(); + auto postsmoother1 = std::make_shared<TrustRegionGSStep<MatrixType11, CorrectionType1> >(); - mmgStep1 = new MonotoneMGStep<MatrixType11, CorrectionType1>; + mmgStep1 = new MonotoneMGStep<MatrixType11, CorrectionType1>; - mmgStep1->setMGType(mu, nu1, nu2); - mmgStep1->ignoreNodes_ = globalDirichletNodes1; - mmgStep1->setBaseSolver(baseSolver1); - mmgStep1->setSmoother(presmoother1, postsmoother1); - mmgStep1->setObstacleRestrictor(std::make_shared<MandelObstacleRestrictor<CorrectionType1>>()); - mmgStep1->setVerbosity(Solver::QUIET); + mmgStep1->setMGType(mu, nu1, nu2); + mmgStep1->ignoreNodes_ = globalDirichletNodes1; + mmgStep1->setBaseSolver(baseSolver1); + mmgStep1->setSmoother(presmoother1, postsmoother1); + mmgStep1->setObstacleRestrictor(std::make_shared<MandelObstacleRestrictor<CorrectionType1> >()); + mmgStep1->setVerbosity(Solver::QUIET); - // ////////////////////////////////////////////////////////////////////////////////////// - // Assemble a Laplace matrix to create a norm that's equivalent to the H1-norm - // ////////////////////////////////////////////////////////////////////////////////////// - Basis0 basis0(grid.leafGridView()); - Basis1 basis1(grid.leafGridView()); + // ////////////////////////////////////////////////////////////////////////////////////// + // Assemble a Laplace matrix to create a norm that's equivalent to the H1-norm + // ////////////////////////////////////////////////////////////////////////////////////// + Basis0 basis0(grid.leafGridView()); + Basis1 basis1(grid.leafGridView()); - // //////////////////////////////////////////////////////////// - // Create Hessian matrix and its occupation structure - // //////////////////////////////////////////////////////////// + // //////////////////////////////////////////////////////////// + // Create Hessian matrix and its occupation structure + // //////////////////////////////////////////////////////////// - // \todo Why are the hessianMatrix objects class members at all, and not local to 'solve'? + // \todo Why are the hessianMatrix objects class members at all, and not local to 'solve'? - hessianMatrix_ = std::make_unique<MatrixType>(); + hessianMatrix_ = std::make_unique<MatrixType>(); - // //////////////////////////////////// - // Create the transfer operators - // //////////////////////////////////// + // //////////////////////////////////// + // Create the transfer operators + // //////////////////////////////////// - mmgStep0->mgTransfer_.resize(numLevels-1); - mmgStep1->mgTransfer_.resize(numLevels-1); + mmgStep0->mgTransfer_.resize(numLevels-1); + mmgStep1->mgTransfer_.resize(numLevels-1); - // Get bind to some element to be able to query the FE space order - auto localView0 = basis0.localView(); - localView0.bind(*grid.leafGridView().template begin<0>()); + // Get bind to some element to be able to query the FE space order + auto localView0 = basis0.localView(); + localView0.bind(*grid.leafGridView().template begin<0>()); - if (localView0.tree().finiteElement().localBasis().order() > 1) - { - if (numLevels>1) { - typedef typename TruncatedCompressedMGTransfer<CorrectionType0>::TransferOperatorType TransferOperatorType; - Dune::Functions::LagrangeBasis<typename GridType::LeafGridView,1> p1Basis(grid_->leafGridView()); - - TransferOperatorType pkToP1TransferMatrix; - assembleGlobalBasisTransferMatrix(pkToP1TransferMatrix,p1Basis,*basis0_); + if (localView0.tree().finiteElement().localBasis().order() > 1) + { + if (numLevels>1) { + typedef typename TruncatedCompressedMGTransfer<CorrectionType0>::TransferOperatorType TransferOperatorType; + Dune::Functions::LagrangeBasis<typename GridType::LeafGridView,1> p1Basis(grid_->leafGridView()); - mmgStep0->mgTransfer_.back() = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType0>>(); - std::shared_ptr<TransferOperatorType> topTransferOperator = std::make_shared<TransferOperatorType>(pkToP1TransferMatrix); - std::dynamic_pointer_cast<TruncatedCompressedMGTransfer<CorrectionType0>>(mmgStep0->mgTransfer_.back())->setMatrix(topTransferOperator); + TransferOperatorType pkToP1TransferMatrix; + assembleGlobalBasisTransferMatrix(pkToP1TransferMatrix,p1Basis,*basis0_); - for (size_t i=0; i<mmgStep0->mgTransfer_.size()-1; i++){ - // Construct the local multigrid transfer matrix - auto newTransferOp0 = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType0>>(); - newTransferOp0->setup(*grid_,i+1,i+2); + mmgStep0->mgTransfer_.back() = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType0> >(); + std::shared_ptr<TransferOperatorType> topTransferOperator = std::make_shared<TransferOperatorType>(pkToP1TransferMatrix); + std::dynamic_pointer_cast<TruncatedCompressedMGTransfer<CorrectionType0> >(mmgStep0->mgTransfer_.back())->setMatrix(topTransferOperator); - mmgStep0->mgTransfer_[i] = newTransferOp0; - } + for (size_t i=0; i<mmgStep0->mgTransfer_.size()-1; i++) { + // Construct the local multigrid transfer matrix + auto newTransferOp0 = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType0> >(); + newTransferOp0->setup(*grid_,i+1,i+2); + mmgStep0->mgTransfer_[i] = newTransferOp0; } } - else // order == 1 - { - for (size_t i=0; i<mmgStep0->mgTransfer_.size(); i++){ + } + else // order == 1 + { - // Construct the local multigrid transfer matrix - auto newTransferOp0 = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType0>>(); - newTransferOp0->setup(*grid_,i,i+1); + for (size_t i=0; i<mmgStep0->mgTransfer_.size(); i++) { - mmgStep0->mgTransfer_[i] = newTransferOp0; - } + // Construct the local multigrid transfer matrix + auto newTransferOp0 = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType0> >(); + newTransferOp0->setup(*grid_,i,i+1); + mmgStep0->mgTransfer_[i] = newTransferOp0; } - auto localView1 = basis1.localView(); - localView1.bind(*grid.leafGridView().template begin<0>()); + } - if (localView1.tree().finiteElement().localBasis().order() > 1) - { - if (numLevels>1) { - typedef typename TruncatedCompressedMGTransfer<CorrectionType1>::TransferOperatorType TransferOperatorType; - Dune::Functions::LagrangeBasis<typename GridType::LeafGridView,1> p1Basis(grid_->leafGridView()); - - TransferOperatorType pkToP1TransferMatrix; - assembleGlobalBasisTransferMatrix(pkToP1TransferMatrix,p1Basis,*basis1_); - - mmgStep1->mgTransfer_.back() = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType1>>(); - std::shared_ptr<TransferOperatorType> topTransferOperator = std::make_shared<TransferOperatorType>(pkToP1TransferMatrix); - std::dynamic_pointer_cast<TruncatedCompressedMGTransfer<CorrectionType1>>(mmgStep1->mgTransfer_.back())->setMatrix(topTransferOperator); - - for (size_t i=0; i<mmgStep1->mgTransfer_.size()-1; i++){ - // Construct the local multigrid transfer matrix - auto newTransferOp1 = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType1>>(); - newTransferOp1->setup(*grid_,i+1,i+2); - mmgStep1->mgTransfer_[i] = newTransferOp1; - } + auto localView1 = basis1.localView(); + localView1.bind(*grid.leafGridView().template begin<0>()); - } + if (localView1.tree().finiteElement().localBasis().order() > 1) + { + if (numLevels>1) { + typedef typename TruncatedCompressedMGTransfer<CorrectionType1>::TransferOperatorType TransferOperatorType; + Dune::Functions::LagrangeBasis<typename GridType::LeafGridView,1> p1Basis(grid_->leafGridView()); - } - else // order == 1 - { + TransferOperatorType pkToP1TransferMatrix; + assembleGlobalBasisTransferMatrix(pkToP1TransferMatrix,p1Basis,*basis1_); - for (size_t i=0; i<mmgStep1->mgTransfer_.size(); i++){ + mmgStep1->mgTransfer_.back() = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType1> >(); + std::shared_ptr<TransferOperatorType> topTransferOperator = std::make_shared<TransferOperatorType>(pkToP1TransferMatrix); + std::dynamic_pointer_cast<TruncatedCompressedMGTransfer<CorrectionType1> >(mmgStep1->mgTransfer_.back())->setMatrix(topTransferOperator); + for (size_t i=0; i<mmgStep1->mgTransfer_.size()-1; i++) { // Construct the local multigrid transfer matrix - auto newTransferOp = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType1>>(); - newTransferOp->setup(*grid_,i,i+1); - mmgStep1->mgTransfer_[i] = newTransferOp; + auto newTransferOp1 = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType1> >(); + newTransferOp1->setup(*grid_,i+1,i+2); + mmgStep1->mgTransfer_[i] = newTransferOp1; } + } - // ////////////////////////////////////////////////////////// - // Create obstacles - // ////////////////////////////////////////////////////////// + } + else // order == 1 + { - if (rank==0) - { + for (size_t i=0; i<mmgStep1->mgTransfer_.size(); i++) { + + // Construct the local multigrid transfer matrix + auto newTransferOp = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType1> >(); + newTransferOp->setup(*grid_,i,i+1); + mmgStep1->mgTransfer_[i] = newTransferOp; + } + } + + // ////////////////////////////////////////////////////////// + // Create obstacles + // ////////////////////////////////////////////////////////// + + if (rank==0) + { #if 0 - hasObstacle0_.resize(guIndex_->nGlobalEntity(), true); + hasObstacle0_.resize(guIndex_->nGlobalEntity(), true); #else - hasObstacle0_.resize(assembler->basis_.size({0}), true); - hasObstacle1_.resize(assembler->basis_.size({1}), true); + hasObstacle0_.resize(assembler->basis_.size({0}), true); + hasObstacle1_.resize(assembler->basis_.size({1}), true); #endif - mmgStep0->setHasObstacles(hasObstacle0_); - mmgStep1->setHasObstacles(hasObstacle1_); - } - + mmgStep0->setHasObstacles(hasObstacle0_); + mmgStep1->setHasObstacles(hasObstacle1_); } +} + template <class GridType, - class Basis, - class Basis0, class TargetSpace0, - class Basis1, class TargetSpace1> + class Basis, + class Basis0, class TargetSpace0, + class Basis1, class TargetSpace1> void MixedRiemannianTrustRegionSolver<GridType,Basis,Basis0,TargetSpace0,Basis1,TargetSpace1>::solve() { - int argc = 0; - char** argv; - Dune::MPIHelper& mpiHelper = Dune::MPIHelper::instance(argc,argv); - int rank = grid_->comm().rank(); - - // \todo Use global index set instead of basis for parallel computations - MaxNormTrustRegion<blocksize0> trustRegion0(assembler_->basis_.size({0}), initialTrustRegionRadius_); - MaxNormTrustRegion<blocksize1> trustRegion1(assembler_->basis_.size({1}), initialTrustRegionRadius_); - trustRegion0.set(initialTrustRegionRadius_, std::get<0>(scaling_)); - trustRegion1.set(initialTrustRegionRadius_, std::get<1>(scaling_)); - auto smallestScalingParameter0 = *std::min_element(std::begin(std::get<0>(scaling_)), std::end(std::get<0>(scaling_))); - auto smallestScalingParameter1 = *std::min_element(std::begin(std::get<1>(scaling_)), std::end(std::get<1>(scaling_))); - auto smallestScalingParameter = std::min(smallestScalingParameter0,smallestScalingParameter1); - - std::vector<BoxConstraint<field_type,blocksize0> > trustRegionObstacles0; - std::vector<BoxConstraint<field_type,blocksize1> > trustRegionObstacles1; - - // ///////////////////////////////////////////////////// - // Trust-Region Solver - // ///////////////////////////////////////////////////// - - using namespace Dune::TypeTree::Indices; - - double oldEnergy = assembler_->computeEnergy(x_[_0], x_[_1]); - oldEnergy = mpiHelper.getCommunication().sum(oldEnergy); - - bool recomputeGradientHessian = true; - CorrectionType rhs; - MatrixType stiffnessMatrix; - CorrectionType rhs_global; - double totalAssemblyTime = 0.0; - double totalSolverTime = 0.0; + int argc = 0; + char** argv; + Dune::MPIHelper& mpiHelper = Dune::MPIHelper::instance(argc,argv); + int rank = grid_->comm().rank(); + + // \todo Use global index set instead of basis for parallel computations + MaxNormTrustRegion<blocksize0> trustRegion0(assembler_->basis_.size({0}), initialTrustRegionRadius_); + MaxNormTrustRegion<blocksize1> trustRegion1(assembler_->basis_.size({1}), initialTrustRegionRadius_); + trustRegion0.set(initialTrustRegionRadius_, std::get<0>(scaling_)); + trustRegion1.set(initialTrustRegionRadius_, std::get<1>(scaling_)); + auto smallestScalingParameter0 = *std::min_element(std::begin(std::get<0>(scaling_)), std::end(std::get<0>(scaling_))); + auto smallestScalingParameter1 = *std::min_element(std::begin(std::get<1>(scaling_)), std::end(std::get<1>(scaling_))); + auto smallestScalingParameter = std::min(smallestScalingParameter0,smallestScalingParameter1); + + std::vector<BoxConstraint<field_type,blocksize0> > trustRegionObstacles0; + std::vector<BoxConstraint<field_type,blocksize1> > trustRegionObstacles1; + + // ///////////////////////////////////////////////////// + // Trust-Region Solver + // ///////////////////////////////////////////////////// + + using namespace Dune::TypeTree::Indices; + + double oldEnergy = assembler_->computeEnergy(x_[_0], x_[_1]); + oldEnergy = mpiHelper.getCommunication().sum(oldEnergy); + + bool recomputeGradientHessian = true; + CorrectionType rhs; + MatrixType stiffnessMatrix; + CorrectionType rhs_global; + double totalAssemblyTime = 0.0; + double totalSolverTime = 0.0; #if 0 - VectorCommunicator<GUIndex, CorrectionType> vectorComm(*guIndex_, 0); - MatrixCommunicator<GUIndex, MatrixType> matrixComm(*guIndex_, 0); + VectorCommunicator<GUIndex, CorrectionType> vectorComm(*guIndex_, 0); + MatrixCommunicator<GUIndex, MatrixType> matrixComm(*guIndex_, 0); #endif - for (int i=0; i<maxTrustRegionSteps_; i++) { + for (int i=0; i<maxTrustRegionSteps_; i++) { - statistics_.finalIteration = i; - Dune::Timer totalTimer; - if (this->verbosity_ == Solver::FULL and rank==0) { - std::cout << "----------------------------------------------------" << std::endl; - std::cout << " Trust-Region Step Number: " << i - << ", radius: " << trustRegion0.radius() - << ", energy: " << oldEnergy << std::endl; - std::cout << "----------------------------------------------------" << std::endl; - } + statistics_.finalIteration = i; + Dune::Timer totalTimer; + if (this->verbosity_ == Solver::FULL and rank==0) { + std::cout << "----------------------------------------------------" << std::endl; + std::cout << " Trust-Region Step Number: " << i + << ", radius: " << trustRegion0.radius() + << ", energy: " << oldEnergy << std::endl; + std::cout << "----------------------------------------------------" << std::endl; + } - CorrectionType corr; - corr[_0].resize(x_[_0].size()); - corr[_1].resize(x_[_1].size()); - corr = 0; + CorrectionType corr; + corr[_0].resize(x_[_0].size()); + corr[_1].resize(x_[_1].size()); + corr = 0; - Dune::Timer assemblyTimer; + Dune::Timer assemblyTimer; - if (recomputeGradientHessian) { + if (recomputeGradientHessian) { - assembler_->assembleGradientAndHessian(x_[_0], - x_[_1], - rhs[_0], - rhs[_1], - *hessianMatrix_, - i==0 // assemble occupation pattern only for the first call - ); + assembler_->assembleGradientAndHessian(x_[_0], + x_[_1], + rhs[_0], + rhs[_1], + *hessianMatrix_, + i==0 // assemble occupation pattern only for the first call + ); - rhs *= -1; // The right hand side is the _negative_ gradient + rhs *= -1; // The right hand side is the _negative_ gradient - if (this->verbosity_ == Solver::FULL) - std::cout << "Assembly took " << assemblyTimer.elapsed() << " sec." << std::endl; - totalAssemblyTime += assemblyTimer.elapsed(); + if (this->verbosity_ == Solver::FULL) + std::cout << "Assembly took " << assemblyTimer.elapsed() << " sec." << std::endl; + totalAssemblyTime += assemblyTimer.elapsed(); - // Transfer matrix data + // Transfer matrix data #if 0 - stiffnessMatrix00 = matrixComm.reduceAdd(*hessianMatrix00_); - stiffnessMatrix01 = matrixComm.reduceAdd(*hessianMatrix01_); - stiffnessMatrix10 = matrixComm.reduceAdd(*hessianMatrix10_); - stiffnessMatrix11 = matrixComm.reduceAdd(*hessianMatrix11_); + stiffnessMatrix00 = matrixComm.reduceAdd(*hessianMatrix00_); + stiffnessMatrix01 = matrixComm.reduceAdd(*hessianMatrix01_); + stiffnessMatrix10 = matrixComm.reduceAdd(*hessianMatrix10_); + stiffnessMatrix11 = matrixComm.reduceAdd(*hessianMatrix11_); #endif - stiffnessMatrix = *hessianMatrix_; + stiffnessMatrix = *hessianMatrix_; - // Transfer vector data + // Transfer vector data #if 0 - rhs_global0 = vectorComm.reduceAdd(rhs0); - rhs_global1 = vectorComm.reduceAdd(rhs1); + rhs_global0 = vectorComm.reduceAdd(rhs0); + rhs_global1 = vectorComm.reduceAdd(rhs1); #endif - rhs_global = rhs; + rhs_global = rhs; - recomputeGradientHessian = false; + recomputeGradientHessian = false; - } + } - CorrectionType corr_global; - corr_global[_0].resize(rhs_global[_0].size()); - corr_global[_1].resize(rhs_global[_1].size()); - corr_global = 0; - bool solvedByInnerSolver = true; - - if (rank==0) - { - /////////////////////////////// - // Solve ! - /////////////////////////////// - CorrectionType residual = rhs_global; - - mmgStep0->setProblem(stiffnessMatrix[_0][_0], corr_global[_0], residual[_0]); - trustRegionObstacles0 = trustRegion0.obstacles(); - mmgStep0->setObstacles(trustRegionObstacles0); - - mmgStep0->preprocess(); - - mmgStep1->setProblem(stiffnessMatrix[_1][_1], corr_global[_1], residual[_1]); - trustRegionObstacles1 = trustRegion1.obstacles(); - mmgStep1->setObstacles(trustRegionObstacles1); - - mmgStep1->preprocess(); - - /////////////////////////////// - // Solve ! - /////////////////////////////// - - std::cout << "Solve quadratic problem..." << std::endl; - double oldEnergy = 0; - Dune::Timer solutionTimer; - int ii = 0; - CorrectionType diff{corr_global}; - try { - for (; ii<innerIterations_; ii++) { - diff=corr_global; - residual[_0] = rhs_global[_0]; - stiffnessMatrix[_0][_1].mmv(corr_global[_1], residual[_0]); - mmgStep0->setRhs(residual[_0]); - mmgStep0->iterate(); - - residual[_1] = rhs_global[_1]; - stiffnessMatrix[_1][_0].mmv(corr_global[_0], residual[_1]); - mmgStep1->setRhs(residual[_1]); - mmgStep1->iterate(); - - // Compute energy - CorrectionType tmp(corr_global); - stiffnessMatrix.mv(corr_global,tmp); - double energy = 0.5 * (tmp*corr_global) - (rhs_global*corr_global); - - if (energy > oldEnergy) - //DUNE_THROW(Dune::Exception, "energy increase!"); - std::cout << "Warning: energy increase!" << std::endl; - - oldEnergy = energy; - diff -= corr_global; - if (diff.infinity_norm() < innerTolerance_) - break; - } - } catch (Dune::Exception &e) { - std::cerr << "Error while solving: " << e << std::endl; - solvedByInnerSolver = false; - corr_global = 0; - } - - std::cout << "Solving the quadratic problem took " << solutionTimer.elapsed() << " seconds and " << ii << " steps." << std::endl; - totalSolverTime += solutionTimer.elapsed(); - //std::cout << "Correction: " << std::endl << corr_global << std::endl; + CorrectionType corr_global; + corr_global[_0].resize(rhs_global[_0].size()); + corr_global[_1].resize(rhs_global[_1].size()); + corr_global = 0; + bool solvedByInnerSolver = true; + if (rank==0) + { + /////////////////////////////// + // Solve ! + /////////////////////////////// + CorrectionType residual = rhs_global; + + mmgStep0->setProblem(stiffnessMatrix[_0][_0], corr_global[_0], residual[_0]); + trustRegionObstacles0 = trustRegion0.obstacles(); + mmgStep0->setObstacles(trustRegionObstacles0); + + mmgStep0->preprocess(); + + mmgStep1->setProblem(stiffnessMatrix[_1][_1], corr_global[_1], residual[_1]); + trustRegionObstacles1 = trustRegion1.obstacles(); + mmgStep1->setObstacles(trustRegionObstacles1); + + mmgStep1->preprocess(); + + /////////////////////////////// + // Solve ! + /////////////////////////////// + + std::cout << "Solve quadratic problem..." << std::endl; + double oldEnergy = 0; + Dune::Timer solutionTimer; + int ii = 0; + CorrectionType diff{corr_global}; + try { + for (; ii<innerIterations_; ii++) { + diff=corr_global; + residual[_0] = rhs_global[_0]; + stiffnessMatrix[_0][_1].mmv(corr_global[_1], residual[_0]); + mmgStep0->setRhs(residual[_0]); + mmgStep0->iterate(); + + residual[_1] = rhs_global[_1]; + stiffnessMatrix[_1][_0].mmv(corr_global[_0], residual[_1]); + mmgStep1->setRhs(residual[_1]); + mmgStep1->iterate(); + + // Compute energy + CorrectionType tmp(corr_global); + stiffnessMatrix.mv(corr_global,tmp); + double energy = 0.5 * (tmp*corr_global) - (rhs_global*corr_global); + + if (energy > oldEnergy) + //DUNE_THROW(Dune::Exception, "energy increase!"); + std::cout << "Warning: energy increase!" << std::endl; + + oldEnergy = energy; + diff -= corr_global; + if (diff.infinity_norm() < innerTolerance_) + break; } + } catch (Dune::Exception &e) { + std::cerr << "Error while solving: " << e << std::endl; + solvedByInnerSolver = false; + corr_global = 0; + } + + std::cout << "Solving the quadratic problem took " << solutionTimer.elapsed() << " seconds and " << ii << " steps." << std::endl; + totalSolverTime += solutionTimer.elapsed(); + //std::cout << "Correction: " << std::endl << corr_global << std::endl; + + } + + // Distribute solution + if (mpiHelper.size()>1 and rank==0) + std::cout << "Transfer solution back to root process ..." << std::endl; + + //corr = vectorComm.scatter(corr_global); + corr = corr_global; + double energy = 0; + double modelDecrease = 0; + SolutionType newIterate = x_; + if (solvedByInnerSolver) { + + if (this->verbosity_ == NumProc::FULL) + std::cout << "Infinity norm of the correction: " << corr.infinity_norm() << std::endl; + + if (corr_global.infinity_norm() < tolerance_ && corr_global.infinity_norm() < trustRegion0.radius()*smallestScalingParameter) { + if (verbosity_ == NumProc::FULL and rank==0) + std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; + + if (verbosity_ != NumProc::QUIET and rank==0) + std::cout << i+1 << " trust-region steps were taken" << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; + break; + } + + // //////////////////////////////////////////////////// + // Check whether trust-region step can be accepted + // //////////////////////////////////////////////////// + + for (size_t j=0; j<newIterate[_0].size(); j++) + newIterate[_0][j] = TargetSpace0::exp(newIterate[_0][j], corr[_0][j]); + + for (size_t j=0; j<newIterate[_1].size(); j++) + newIterate[_1][j] = TargetSpace1::exp(newIterate[_1][j], corr[_1][j]); + + try { + energy = assembler_->computeEnergy(newIterate[_0],newIterate[_1]); + } catch (Dune::Exception &e) { + std::cerr << "Error while computing the energy of the new iterate: " << e << std::endl; + std::cerr << "Redoing trust region step with smaller radius..." << std::endl; + newIterate = x_; + solvedByInnerSolver = false; + energy = oldEnergy; + } - // Distribute solution - if (mpiHelper.size()>1 and rank==0) - std::cout << "Transfer solution back to root process ..." << std::endl; + if (solvedByInnerSolver) { + energy = mpiHelper.getCommunication().sum(energy); - //corr = vectorComm.scatter(corr_global); - corr = corr_global; - double energy = 0; - double modelDecrease = 0; - SolutionType newIterate = x_; - if (solvedByInnerSolver) { + // compute the model decrease + // It is $ m(x) - m(x+s) = -<g,s> - 0.5 <s, Hs> + // Note that rhs = -g + CorrectionType tmp(corr); + hessianMatrix_->mv(corr,tmp); + modelDecrease = rhs*corr - 0.5 * (corr*tmp); + modelDecrease = mpiHelper.getCommunication().sum(modelDecrease); + double relativeModelDecrease = modelDecrease / std::fabs(energy); + + if (verbosity_ == NumProc::FULL and rank==0) { + std::cout << "Absolute model decrease: " << modelDecrease + << ", functional decrease: " << oldEnergy - energy << std::endl; + std::cout << "Relative model decrease: " << relativeModelDecrease + << ", functional decrease: " << (oldEnergy - energy)/energy << std::endl; + } + + assert(modelDecrease >= 0); + + if (energy >= oldEnergy and rank==0) { if (this->verbosity_ == NumProc::FULL) - std::cout << "Infinity norm of the correction: " << corr.infinity_norm() << std::endl; - - if (corr_global.infinity_norm() < tolerance_ && corr_global.infinity_norm() < trustRegion0.radius()*smallestScalingParameter) { - if (verbosity_ == NumProc::FULL and rank==0) - std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; - - if (verbosity_ != NumProc::QUIET and rank==0) - std::cout << i+1 << " trust-region steps were taken" << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; - break; - } - - // //////////////////////////////////////////////////// - // Check whether trust-region step can be accepted - // //////////////////////////////////////////////////// - - for (size_t j=0; j<newIterate[_0].size(); j++) - newIterate[_0][j] = TargetSpace0::exp(newIterate[_0][j], corr[_0][j]); - - for (size_t j=0; j<newIterate[_1].size(); j++) - newIterate[_1][j] = TargetSpace1::exp(newIterate[_1][j], corr[_1][j]); - - try { - energy = assembler_->computeEnergy(newIterate[_0],newIterate[_1]); - } catch (Dune::Exception &e) { - std::cerr << "Error while computing the energy of the new iterate: " << e << std::endl; - std::cerr << "Redoing trust region step with smaller radius..." << std::endl; - newIterate = x_; - solvedByInnerSolver = false; - energy = oldEnergy; - } - - if (solvedByInnerSolver) { - energy = mpiHelper.getCommunication().sum(energy); - - // compute the model decrease - // It is $ m(x) - m(x+s) = -<g,s> - 0.5 <s, Hs> - // Note that rhs = -g - CorrectionType tmp(corr); - hessianMatrix_->mv(corr,tmp); - modelDecrease = rhs*corr - 0.5 * (corr*tmp); - modelDecrease = mpiHelper.getCommunication().sum(modelDecrease); - - double relativeModelDecrease = modelDecrease / std::fabs(energy); - - if (verbosity_ == NumProc::FULL and rank==0) { - std::cout << "Absolute model decrease: " << modelDecrease - << ", functional decrease: " << oldEnergy - energy << std::endl; - std::cout << "Relative model decrease: " << relativeModelDecrease - << ", functional decrease: " << (oldEnergy - energy)/energy << std::endl; - } - - assert(modelDecrease >= 0); - - if (energy >= oldEnergy and rank==0) { - if (this->verbosity_ == NumProc::FULL) - std::cout << "Direction is not a descent direction!" << std::endl; - } - - if (energy >= oldEnergy && - (std::abs((oldEnergy-energy)/energy) < 1e-9 || relativeModelDecrease < 1e-9)) { - if (this->verbosity_ == NumProc::FULL and rank==0) - std::cout << "Suspecting rounding problems" << std::endl; - - if (this->verbosity_ != NumProc::QUIET and rank==0) - std::cout << i+1 << " trust-region steps were taken." << std::endl; - - x_ = newIterate; - break; - } - - } + std::cout << "Direction is not a descent direction!" << std::endl; } - // ////////////////////////////////////////////// - // Check for acceptance of the step - // ////////////////////////////////////////////// - if ( solvedByInnerSolver && (oldEnergy-energy) / modelDecrease > 0.9) { - // very successful iteration - x_ = newIterate; - trustRegion0.scale(2); - trustRegion1.scale(2); + if (energy >= oldEnergy && + (std::abs((oldEnergy-energy)/energy) < 1e-9 || relativeModelDecrease < 1e-9)) { + if (this->verbosity_ == NumProc::FULL and rank==0) + std::cout << "Suspecting rounding problems" << std::endl; - // current energy becomes 'oldEnergy' for the next iteration - oldEnergy = energy; + if (this->verbosity_ != NumProc::QUIET and rank==0) + std::cout << i+1 << " trust-region steps were taken." << std::endl; - recomputeGradientHessian = true; + x_ = newIterate; + break; + } - } else if (solvedByInnerSolver && (oldEnergy-energy) / modelDecrease > 0.01 - || std::abs(oldEnergy-energy) < 1e-12) { - // successful iteration - x_ = newIterate; + } + } + // ////////////////////////////////////////////// + // Check for acceptance of the step + // ////////////////////////////////////////////// + if ( solvedByInnerSolver && (oldEnergy-energy) / modelDecrease > 0.9) { + // very successful iteration - // current energy becomes 'oldEnergy' for the next iteration - oldEnergy = energy; + x_ = newIterate; + trustRegion0.scale(2); + trustRegion1.scale(2); - recomputeGradientHessian = true; + // current energy becomes 'oldEnergy' for the next iteration + oldEnergy = energy; - } else { + recomputeGradientHessian = true; - // unsuccessful iteration + } else if (solvedByInnerSolver && (oldEnergy-energy) / modelDecrease > 0.01 + || std::abs(oldEnergy-energy) < 1e-12) { + // successful iteration + x_ = newIterate; - // Decrease the trust-region radius - trustRegion0.scale(0.5); - trustRegion1.scale(0.5); + // current energy becomes 'oldEnergy' for the next iteration + oldEnergy = energy; - if (this->verbosity_ == NumProc::FULL and rank==0) - std::cout << "Unsuccessful iteration!" << std::endl; + recomputeGradientHessian = true; - if (trustRegion0.radius() < 1e-9) { - if (this->verbosity_ == NumProc::FULL and rank==0) - std::cout << "The radius is too small to continue with a meaningful calculation!" << std::endl; + } else { - if (this->verbosity_ != NumProc::QUIET and rank==0) - std::cout << i+1 << " trust-region steps were taken" << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; - break; - } - } + // unsuccessful iteration + + // Decrease the trust-region radius + trustRegion0.scale(0.5); + trustRegion1.scale(0.5); + + if (this->verbosity_ == NumProc::FULL and rank==0) + std::cout << "Unsuccessful iteration!" << std::endl; + + if (trustRegion0.radius() < 1e-9) { + if (this->verbosity_ == NumProc::FULL and rank==0) + std::cout << "The radius is too small to continue with a meaningful calculation!" << std::endl; + + if (this->verbosity_ != NumProc::QUIET and rank==0) + std::cout << i+1 << " trust-region steps were taken" << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; + break; + } + } #if 0 - // Output each iterate, to better understand what the algorithm does - DuneFunctionsBasis<Basis0> fufemBasis0(assembler_->basis0_); - DuneFunctionsBasis<Basis1> fufemBasis1(assembler_->basis1_); - std::stringstream iAsAscii; - iAsAscii << i+1; - CosseratVTKWriter<GridType>::template writeMixed<DuneFunctionsBasis<Basis0>, DuneFunctionsBasis<Basis1> >(fufemBasis0,x_[_0], - fufemBasis1,x_[_1], - "mixed-cosserat_iterate_" + iAsAscii.str()); + // Output each iterate, to better understand what the algorithm does + DuneFunctionsBasis<Basis0> fufemBasis0(assembler_->basis0_); + DuneFunctionsBasis<Basis1> fufemBasis1(assembler_->basis1_); + std::stringstream iAsAscii; + iAsAscii << i+1; + CosseratVTKWriter<GridType>::template writeMixed<DuneFunctionsBasis<Basis0>, DuneFunctionsBasis<Basis1> >(fufemBasis0,x_[_0], + fufemBasis1,x_[_1], + "mixed-cosserat_iterate_" + iAsAscii.str()); #endif - if (rank==0) - std::cout << "iteration took " << totalTimer.elapsed() << " sec." << std::endl; + if (rank==0) + std::cout << "iteration took " << totalTimer.elapsed() << " sec." << std::endl; - } + } - statistics_.finalEnergy = oldEnergy; + statistics_.finalEnergy = oldEnergy; } diff --git a/dune/gfe/mixedriemanniantrsolver.hh b/dune/gfe/mixedriemanniantrsolver.hh index 202bf4c2..9740d09a 100644 --- a/dune/gfe/mixedriemanniantrsolver.hh +++ b/dune/gfe/mixedriemanniantrsolver.hh @@ -23,163 +23,163 @@ /** \brief Riemannian trust-region solver for geodesic finite-element problems */ template <class GridType, - class Basis, - class Basis0, class TargetSpace0, - class Basis1, class TargetSpace1> + class Basis, + class Basis0, class TargetSpace0, + class Basis1, class TargetSpace1> class MixedRiemannianTrustRegionSolver - : public NumProc + : public NumProc { - const static int blocksize0 = TargetSpace0::TangentVector::dimension; - const static int blocksize1 = TargetSpace1::TangentVector::dimension; - - const static int gridDim = GridType::dimension; - - // Centralize the field type here - typedef double field_type; - - // Some types that I need - typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize0, blocksize0> > MatrixType00; - typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize0, blocksize1> > MatrixType01; - typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize1, blocksize0> > MatrixType10; - typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize1, blocksize1> > MatrixType11; - typedef Dune::MultiTypeBlockMatrix<Dune::MultiTypeBlockVector<MatrixType00,MatrixType01>, - Dune::MultiTypeBlockVector<MatrixType10,MatrixType11> > MatrixType; - typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize0> > CorrectionType0; - typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize1> > CorrectionType1; - typedef Dune::MultiTypeBlockVector<CorrectionType0, CorrectionType1> CorrectionType; - typedef Dune::TupleVector<std::vector<TargetSpace0>, std::vector<TargetSpace1> > SolutionType; - - /** \brief Records information about the last run of the RiemannianTrustRegionSolver - * - * This is used primarily for unit testing. - */ - struct Statistics - { - std::size_t finalIteration; - - field_type finalEnergy; - }; + const static int blocksize0 = TargetSpace0::TangentVector::dimension; + const static int blocksize1 = TargetSpace1::TangentVector::dimension; + + const static int gridDim = GridType::dimension; + + // Centralize the field type here + typedef double field_type; + + // Some types that I need + typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize0, blocksize0> > MatrixType00; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize0, blocksize1> > MatrixType01; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize1, blocksize0> > MatrixType10; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize1, blocksize1> > MatrixType11; + typedef Dune::MultiTypeBlockMatrix<Dune::MultiTypeBlockVector<MatrixType00,MatrixType01>, + Dune::MultiTypeBlockVector<MatrixType10,MatrixType11> > MatrixType; + typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize0> > CorrectionType0; + typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize1> > CorrectionType1; + typedef Dune::MultiTypeBlockVector<CorrectionType0, CorrectionType1> CorrectionType; + typedef Dune::TupleVector<std::vector<TargetSpace0>, std::vector<TargetSpace1> > SolutionType; + + /** \brief Records information about the last run of the RiemannianTrustRegionSolver + * + * This is used primarily for unit testing. + */ + struct Statistics + { + std::size_t finalIteration; + + field_type finalEnergy; + }; public: - MixedRiemannianTrustRegionSolver() - : NumProc(NumProc::FULL), - - h1SemiNorm0_(nullptr), h1SemiNorm1_(nullptr) - { - std::fill(std::get<0>(scaling_).begin(), std::get<0>(scaling_).end(), 1.0); - std::fill(std::get<1>(scaling_).begin(), std::get<1>(scaling_).end(), 1.0); - } - - /** \brief Set up the solver using a monotone multigrid method as the inner solver */ - void setup(const GridType& grid, - const MixedGFEAssembler<Basis, TargetSpace0, TargetSpace1>* assembler, - const Basis0& basis0, - const Basis1& basis1, - const SolutionType& x, - const Dune::BitSetVector<blocksize0>& dirichletNodes0, - const Dune::BitSetVector<blocksize1>& dirichletNodes1, - double tolerance, - int maxTrustRegionSteps, - double initialTrustRegionRadius, - int multigridIterations, - double mgTolerance, - int mu, - int nu1, - int nu2, - int baseIterations, - double baseTolerance, - bool instrumented); - - void setScaling(const Dune::FieldVector<double,blocksize0+blocksize1>& scaling) - { - for (int i=0; i<blocksize0; i++) - std::get<0>(scaling_)[i] = scaling[i]; - - for (int i=0; i<blocksize1; i++) - std::get<1>(scaling_)[i] = scaling[i+blocksize0]; - } + MixedRiemannianTrustRegionSolver() + : NumProc(NumProc::FULL), + + h1SemiNorm0_(nullptr), h1SemiNorm1_(nullptr) + { + std::fill(std::get<0>(scaling_).begin(), std::get<0>(scaling_).end(), 1.0); + std::fill(std::get<1>(scaling_).begin(), std::get<1>(scaling_).end(), 1.0); + } + + /** \brief Set up the solver using a monotone multigrid method as the inner solver */ + void setup(const GridType& grid, + const MixedGFEAssembler<Basis, TargetSpace0, TargetSpace1>* assembler, + const Basis0& basis0, + const Basis1& basis1, + const SolutionType& x, + const Dune::BitSetVector<blocksize0>& dirichletNodes0, + const Dune::BitSetVector<blocksize1>& dirichletNodes1, + double tolerance, + int maxTrustRegionSteps, + double initialTrustRegionRadius, + int multigridIterations, + double mgTolerance, + int mu, + int nu1, + int nu2, + int baseIterations, + double baseTolerance, + bool instrumented); + + void setScaling(const Dune::FieldVector<double,blocksize0+blocksize1>& scaling) + { + for (int i=0; i<blocksize0; i++) + std::get<0>(scaling_)[i] = scaling[i]; + + for (int i=0; i<blocksize1; i++) + std::get<1>(scaling_)[i] = scaling[i+blocksize0]; + } #if 0 - void setIgnoreNodes(const Dune::BitSetVector<blocksize0>& ignoreNodes) - { - ignoreNodes_ = &ignoreNodes; - Dune::shared_ptr<LoopSolver<CorrectionType> > loopSolver = std::dynamic_pointer_cast<LoopSolver<CorrectionType> >(innerSolver_); - assert(loopSolver); - loopSolver->iterationStep_->ignoreNodes_ = ignoreNodes_; - } + void setIgnoreNodes(const Dune::BitSetVector<blocksize0>& ignoreNodes) + { + ignoreNodes_ = &ignoreNodes; + Dune::shared_ptr<LoopSolver<CorrectionType> > loopSolver = std::dynamic_pointer_cast<LoopSolver<CorrectionType> >(innerSolver_); + assert(loopSolver); + loopSolver->iterationStep_->ignoreNodes_ = ignoreNodes_; + } #endif - void solve(); + void solve(); - void setInitialIterate(const SolutionType& x) - { - x_ = x; - } + void setInitialIterate(const SolutionType& x) + { + x_ = x; + } - SolutionType getSol() const - { - return x_; - } + SolutionType getSol() const + { + return x_; + } - const Statistics& getStatistics() const {return statistics_;} + const Statistics& getStatistics() const {return statistics_;} protected: #if 0 - std::unique_ptr<GUIndex> guIndex_; + std::unique_ptr<GUIndex> guIndex_; #endif - /** \brief The grid */ - const GridType* grid_; + /** \brief The grid */ + const GridType* grid_; - /** \brief The solution vectors */ - SolutionType x_; + /** \brief The solution vectors */ + SolutionType x_; - /** \brief The initial trust-region radius in the maximum-norm */ - double initialTrustRegionRadius_; + /** \brief The initial trust-region radius in the maximum-norm */ + double initialTrustRegionRadius_; - /** \brief Trust-region norm scaling */ - std::tuple<Dune::FieldVector<double,blocksize0>, Dune::FieldVector<double,blocksize1> > scaling_; + /** \brief Trust-region norm scaling */ + std::tuple<Dune::FieldVector<double,blocksize0>, Dune::FieldVector<double,blocksize1> > scaling_; - /** \brief Maximum number of trust-region steps */ - int maxTrustRegionSteps_; + /** \brief Maximum number of trust-region steps */ + int maxTrustRegionSteps_; - /** \brief Maximum number of multigrid iterations */ - int innerIterations_; + /** \brief Maximum number of multigrid iterations */ + int innerIterations_; - /** \brief Error tolerance of the multigrid QP solver */ - double innerTolerance_; + /** \brief Error tolerance of the multigrid QP solver */ + double innerTolerance_; - /** \brief Hessian matrix */ - std::unique_ptr<MatrixType> hessianMatrix_; + /** \brief Hessian matrix */ + std::unique_ptr<MatrixType> hessianMatrix_; - /** \brief The assembler for the material law */ - const MixedGFEAssembler<Basis, TargetSpace0, TargetSpace1>* assembler_; + /** \brief The assembler for the material law */ + const MixedGFEAssembler<Basis, TargetSpace0, TargetSpace1>* assembler_; - /** \brief TEMPORARY: The two separate matrices */ - std::unique_ptr<Basis0> basis0_; - std::unique_ptr<Basis1> basis1_; + /** \brief TEMPORARY: The two separate matrices */ + std::unique_ptr<Basis0> basis0_; + std::unique_ptr<Basis1> basis1_; - /** \brief The solver for the quadratic inner problems */ - std::shared_ptr<Solver> innerSolver_; + /** \brief The solver for the quadratic inner problems */ + std::shared_ptr<Solver> innerSolver_; - MonotoneMGStep<MatrixType00, CorrectionType0>* mmgStep0; - MonotoneMGStep<MatrixType11, CorrectionType1>* mmgStep1; + MonotoneMGStep<MatrixType00, CorrectionType0>* mmgStep0; + MonotoneMGStep<MatrixType11, CorrectionType1>* mmgStep1; - double tolerance_; + double tolerance_; - /** \brief Contains 'true' everywhere -- the trust-region is bounded */ - Dune::BitSetVector<blocksize0> hasObstacle0_; - Dune::BitSetVector<blocksize1> hasObstacle1_; + /** \brief Contains 'true' everywhere -- the trust-region is bounded */ + Dune::BitSetVector<blocksize0> hasObstacle0_; + Dune::BitSetVector<blocksize1> hasObstacle1_; - /** \brief The Dirichlet nodes */ - const Dune::BitSetVector<blocksize0>* ignoreNodes0_; - const Dune::BitSetVector<blocksize1>* ignoreNodes1_; + /** \brief The Dirichlet nodes */ + const Dune::BitSetVector<blocksize0>* ignoreNodes0_; + const Dune::BitSetVector<blocksize1>* ignoreNodes1_; - /** \brief The norm used to measure multigrid convergence */ - H1SemiNorm<CorrectionType0>* h1SemiNorm0_; - H1SemiNorm<CorrectionType1>* h1SemiNorm1_; + /** \brief The norm used to measure multigrid convergence */ + H1SemiNorm<CorrectionType0>* h1SemiNorm0_; + H1SemiNorm<CorrectionType1>* h1SemiNorm1_; - /** \brief Store information about solver runs for testing */ - Statistics statistics_; + /** \brief Store information about solver runs for testing */ + Statistics statistics_; }; #include "mixedriemanniantrsolver.cc" diff --git a/dune/gfe/neumannenergy.hh b/dune/gfe/neumannenergy.hh index 2af52ba7..34a96f9e 100644 --- a/dune/gfe/neumannenergy.hh +++ b/dune/gfe/neumannenergy.hh @@ -10,95 +10,95 @@ namespace Dune::GFE { /** - \brief Assembles the Neumann energy for a single element on the Neumann Boundary using the Neumann Function. - + \brief Assembles the Neumann energy for a single element on the Neumann Boundary using the Neumann Function. + This class works similarly to the class Dune::Elasticity::NeumannEnergy, where Dune::Elasticity::NeumannEnergy extends Dune::Elasticity::LocalEnergy and Dune::GFE::NeumannEnergy extends Dune::GFE::LocalEnergy. - */ -template<class Basis, class... TargetSpaces> -class NeumannEnergy - : public Dune::GFE::LocalEnergy<Basis,TargetSpaces...> -{ - using LocalView = typename Basis::LocalView; - using GridView = typename LocalView::GridView; - using DT = typename GridView::Grid::ctype; - using RT = typename Dune::GFE::LocalEnergy<Basis,TargetSpaces...>::RT; - - constexpr static int dim = GridView::dimension; - -public: - - /** \brief Constructor with a set of material parameters - * \param parameters The material parameters */ - NeumannEnergy(const std::shared_ptr<BoundaryPatch<GridView>>& neumannBoundary, - std::function<Dune::FieldVector<double,dim>(Dune::FieldVector<DT,dim>)> neumannFunction) - : neumannBoundary_(neumannBoundary), - neumannFunction_(neumannFunction) - {} + template<class Basis, class ... TargetSpaces> + class NeumannEnergy + : public Dune::GFE::LocalEnergy<Basis,TargetSpaces...> + { + using LocalView = typename Basis::LocalView; + using GridView = typename LocalView::GridView; + using DT = typename GridView::Grid::ctype; + using RT = typename Dune::GFE::LocalEnergy<Basis,TargetSpaces...>::RT; + + constexpr static int dim = GridView::dimension; + + public: - /** \brief Assemble the energy for a single element */ - RT energy(const typename Basis::LocalView& localView, - const std::vector<TargetSpaces>&... localSolutions) const - { - static_assert(sizeof...(TargetSpaces) > 0, "NeumannEnergy needs at least one TargetSpace!"); + /** \brief Constructor with a set of material parameters + * \param parameters The material parameters + */ + NeumannEnergy(const std::shared_ptr<BoundaryPatch<GridView> >& neumannBoundary, + std::function<Dune::FieldVector<double,dim>(Dune::FieldVector<DT,dim>)> neumannFunction) + : neumannBoundary_(neumannBoundary), + neumannFunction_(neumannFunction) + {} - using namespace Dune::Indices; - using TargetSpace = typename std::tuple_element<0, std::tuple<TargetSpaces...> >::type; - const std::vector<TargetSpace>& localSolution = std::get<0>(std::forward_as_tuple(localSolutions...)); + /** \brief Assemble the energy for a single element */ + RT energy(const typename Basis::LocalView& localView, + const std::vector<TargetSpaces>& ... localSolutions) const + { + static_assert(sizeof...(TargetSpaces) > 0, "NeumannEnergy needs at least one TargetSpace!"); - const auto& localFiniteElement = localView.tree().child(_0,0).finiteElement(); - const auto& element = localView.element(); + using namespace Dune::Indices; + using TargetSpace = typename std::tuple_element<0, std::tuple<TargetSpaces...> >::type; + const std::vector<TargetSpace>& localSolution = std::get<0>(std::forward_as_tuple(localSolutions ...)); - RT energy = 0; + const auto& localFiniteElement = localView.tree().child(_0,0).finiteElement(); + const auto& element = localView.element(); - for (auto&& intersection : intersections(neumannBoundary_->gridView(), element)) { + RT energy = 0; - if (not neumannBoundary_ or not neumannBoundary_->contains(intersection)) - continue; + for (auto&& intersection : intersections(neumannBoundary_->gridView(), element)) { - int quadOrder = (element.type().isSimplex()) ? localFiniteElement.localBasis().order() + if (not neumannBoundary_ or not neumannBoundary_->contains(intersection)) + continue; + + int quadOrder = (element.type().isSimplex()) ? localFiniteElement.localBasis().order() : localFiniteElement.localBasis().order() * dim; - const auto& quad = Dune::QuadratureRules<DT, dim-1>::rule(intersection.type(), quadOrder); + const auto& quad = Dune::QuadratureRules<DT, dim-1>::rule(intersection.type(), quadOrder); + + for (size_t pt=0; pt<quad.size(); pt++) { - for (size_t pt=0; pt<quad.size(); pt++) { + // Local position of the quadrature point + const Dune::FieldVector<DT,dim>& quadPos = intersection.geometryInInside().global(quad[pt].position()); - // Local position of the quadrature point - const Dune::FieldVector<DT,dim>& quadPos = intersection.geometryInInside().global(quad[pt].position()); + const auto integrationElement = intersection.geometry().integrationElement(quad[pt].position()); - const auto integrationElement = intersection.geometry().integrationElement(quad[pt].position()); + // The value of the local function + std::vector<Dune::FieldVector<DT,1> > shapeFunctionValues; + localFiniteElement.localBasis().evaluateFunction(quadPos, shapeFunctionValues); - // The value of the local function - std::vector<Dune::FieldVector<DT,1> > shapeFunctionValues; - localFiniteElement.localBasis().evaluateFunction(quadPos, shapeFunctionValues); + Dune::FieldVector<RT,dim> value(0); + for (size_t i=0; i<localFiniteElement.size(); i++) + for (int j=0; j<dim; j++) + value[j] += shapeFunctionValues[i] * localSolution[i][j]; - Dune::FieldVector<RT,dim> value(0); - for (size_t i=0; i<localFiniteElement.size(); i++) - for (int j=0; j<dim; j++) - value[j] += shapeFunctionValues[i] * localSolution[i][j]; + // Value of the Neumann data at the current position + auto neumannValue = neumannFunction_( intersection.geometry().global(quad[pt].position()) ); - // Value of the Neumann data at the current position - auto neumannValue = neumannFunction_( intersection.geometry().global(quad[pt].position()) ); + // Only translational dofs are affected by the Neumann force + for (size_t i=0; i<neumannValue.size(); i++) + energy += (neumannValue[i] * value[i]) * quad[pt].weight() * integrationElement; - // Only translational dofs are affected by the Neumann force - for (size_t i=0; i<neumannValue.size(); i++) - energy += (neumannValue[i] * value[i]) * quad[pt].weight() * integrationElement; + } } + return energy; } - return energy; - } - -private: - /** \brief The Neumann boundary */ - const std::shared_ptr<BoundaryPatch<GridView>> neumannBoundary_; + private: + /** \brief The Neumann boundary */ + const std::shared_ptr<BoundaryPatch<GridView> > neumannBoundary_; - /** \brief The function implementing the Neumann data */ - std::function<Dune::FieldVector<double,dim>(Dune::FieldVector<DT,dim>)> neumannFunction_; -}; + /** \brief The function implementing the Neumann data */ + std::function<Dune::FieldVector<double,dim>(Dune::FieldVector<DT,dim>)> neumannFunction_; + }; } // namespace Dune::GFE #endif //#ifndef DUNE_GFE_NEUMANNENERGY_HH diff --git a/dune/gfe/parallel/globalmapper.hh b/dune/gfe/parallel/globalmapper.hh index 4cf617dd..9aad9c30 100644 --- a/dune/gfe/parallel/globalmapper.hh +++ b/dune/gfe/parallel/globalmapper.hh @@ -26,8 +26,8 @@ namespace Dune { { DofMap dofmap = Dune::ParMG::entitiesToDofsMap(&basis); Master m = Dune::ParMG::entityMasterRank(basis.gridView(), [&](int, int codim) -> bool { - return dofmap.codimSet().test(codim); - }); + return dofmap.codimSet().test(codim); + }); globalDof_ = globalDof(basis,m, dofmap); diff --git a/dune/gfe/parallel/globalp1mapper.hh b/dune/gfe/parallel/globalp1mapper.hh index 3b2fa730..90efa155 100644 --- a/dune/gfe/parallel/globalp1mapper.hh +++ b/dune/gfe/parallel/globalp1mapper.hh @@ -14,12 +14,12 @@ namespace Dune { using GridView = typename Basis::GridView; using P1BasisMapper = MultipleCodimMultipleGeomTypeMapper<GridView>; - public: + public: /** \brief The integer number type used for indices */ using typename GlobalMapper<Basis>::Index; GlobalP1Mapper(const typename Basis::GridView& gridView) - : GlobalMapper<Basis>(gridView), + : GlobalMapper<Basis>(gridView), p1Mapper_(gridView,mcmgVertexLayout()) { #if !HAVE_DUNE_PARMG diff --git a/dune/gfe/parallel/globalp2mapper.hh b/dune/gfe/parallel/globalp2mapper.hh index fad3181d..fbecb00b 100644 --- a/dune/gfe/parallel/globalp2mapper.hh +++ b/dune/gfe/parallel/globalp2mapper.hh @@ -52,17 +52,17 @@ namespace Dune { int globalIndex; switch (codim) { - case 1: // vertex dofs - globalIndex = globalVertexIndex.index(element.template subEntity<1>(entity)); - break; + case 1 : // vertex dofs + globalIndex = globalVertexIndex.index(element.template subEntity<1>(entity)); + break; - case 0: // element dofs - globalIndex = globalElementIndex.index(element.template subEntity<0>(entity)) - + globalVertexIndex.size(1); - break; + case 0 : // element dofs + globalIndex = globalElementIndex.index(element.template subEntity<0>(entity)) + + globalVertexIndex.size(1); + break; - default: - DUNE_THROW(Dune::Exception, "Impossible codimension!"); + default : + DUNE_THROW(Dune::Exception, "Impossible codimension!"); } localGlobalMap_[localIndex] = globalIndex; @@ -92,22 +92,22 @@ namespace Dune { int globalIndex; switch (codim) { - case 2: // vertex dofs - globalIndex = globalVertexIndex.index(element.template subEntity<GridView::dimension>(entity)); - break; - - case 1: // edge dofs - globalIndex = globalEdgeIndex.index(element.template subEntity<1>(entity)) + globalVertexIndex.size(2); - break; - - case 0: // element dofs - globalIndex = globalElementIndex.index(element.template subEntity<0>(entity)) - + globalEdgeIndex.size(1) - + globalVertexIndex.size(2); - break; - - default: - DUNE_THROW(Dune::Exception, "Impossible codimension!"); + case 2 : // vertex dofs + globalIndex = globalVertexIndex.index(element.template subEntity<GridView::dimension>(entity)); + break; + + case 1 : // edge dofs + globalIndex = globalEdgeIndex.index(element.template subEntity<1>(entity)) + globalVertexIndex.size(2); + break; + + case 0 : // element dofs + globalIndex = globalElementIndex.index(element.template subEntity<0>(entity)) + + globalEdgeIndex.size(1) + + globalVertexIndex.size(2); + break; + + default : + DUNE_THROW(Dune::Exception, "Impossible codimension!"); } localGlobalMap_[localIndex] = globalIndex; @@ -139,7 +139,7 @@ namespace Dune { for (size_t i=0; i<localView.size(); i++) { if (localView.tree().finiteElement().localCoefficients().localKey(i).subEntity() == subEntity - and localView.tree().finiteElement().localCoefficients().localKey(i).codim() == codim) + and localView.tree().finiteElement().localCoefficients().localKey(i).codim() == codim) { dofFound = true; localIndex = localView.index(i); diff --git a/dune/gfe/parallel/matrixcommunicator.hh b/dune/gfe/parallel/matrixcommunicator.hh index 6387dd76..1f4e53d1 100644 --- a/dune/gfe/parallel/matrixcommunicator.hh +++ b/dune/gfe/parallel/matrixcommunicator.hh @@ -46,7 +46,7 @@ class MatrixCommunicator { public: MatrixCommunicator(const RowGlobalMapper& rowGlobalMapper, const GridView1& gridView, const LocalMapper1& localMapper1, const LocalMapper2& localMapper2, const int& root) - : rowGlobalMapper_(rowGlobalMapper), + : rowGlobalMapper_(rowGlobalMapper), columnGlobalMapper_(rowGlobalMapper), localMapper1_(localMapper1), localMapper2_(localMapper2), @@ -60,7 +60,7 @@ public: MatrixCommunicator(const RowGlobalMapper& rowGlobalMapper, const ColumnGlobalMapper& columnGlobalMapper, const GridView1& gridView1, const GridView2& gridView2, const LocalMapper1& localMapper1, const LocalMapper2& localMapper2, const int& root) - : rowGlobalMapper_(rowGlobalMapper), + : rowGlobalMapper_(rowGlobalMapper), columnGlobalMapper_(columnGlobalMapper), localMapper1_(localMapper1), localMapper2_(localMapper2), diff --git a/dune/gfe/parallel/p2mapper.hh b/dune/gfe/parallel/p2mapper.hh index aa1b62cc..5dcf6969 100644 --- a/dune/gfe/parallel/p2mapper.hh +++ b/dune/gfe/parallel/p2mapper.hh @@ -19,7 +19,7 @@ public: typedef typename Dune::Functions::LagrangeBasis<GridView,2>::MultiIndex::value_type Index; P2BasisMapper(const GridView& gridView) - : p2Basis_(gridView) + : p2Basis_(gridView) {} std::size_t size() const @@ -50,4 +50,3 @@ public: }; #endif - diff --git a/dune/gfe/parallel/vectorcommunicator.hh b/dune/gfe/parallel/vectorcommunicator.hh index 88d29fc2..d83cc649 100644 --- a/dune/gfe/parallel/vectorcommunicator.hh +++ b/dune/gfe/parallel/vectorcommunicator.hh @@ -18,7 +18,7 @@ class VectorCommunicator { TransferVectorTuple() {} TransferVectorTuple(const size_t& r, const EntryType& e) - : globalIndex_(r), + : globalIndex_(r), value_(e) {} }; @@ -29,7 +29,7 @@ private: // Translate vector entries for (size_t k=0; k<localVector.size(); k++) - localVectorEntries.push_back(TransferVectorTuple(guIndex.index(k), localVector[k])); + localVectorEntries.push_back(TransferVectorTuple(guIndex.index(k), localVector[k])); // Get number of vector entries on each process localVectorEntriesSizes = MPIFunctions::shareSizes(communicator_, localVectorEntries.size()); @@ -42,7 +42,7 @@ public: VectorCommunicator(const GUIndex& gi, const Communicator& communicator, const int& root) - : guIndex(gi), communicator_(communicator), root_rank(root) + : guIndex(gi), communicator_(communicator), root_rank(root) {} VectorType reduceAdd(const VectorType& localVector) diff --git a/dune/gfe/polardecomposition.hh b/dune/gfe/polardecomposition.hh index 5b73a5cd..b25e5416 100644 --- a/dune/gfe/polardecomposition.hh +++ b/dune/gfe/polardecomposition.hh @@ -15,312 +15,312 @@ namespace Dune::GFE { - class PolarDecomposition + class PolarDecomposition + { + public: + + /** \brief Compute the polar factor of a matrix iteratively + Attention: For matrices that are quite far away from an orthogonal matrix, + this might return a matrix with determinant = -1 ! */ + template <class field_type> + FieldMatrix<field_type,3,3> operator() (const FieldMatrix<field_type,3,3>& matrix, double tol = 0.001) const { - public: - - /** \brief Compute the polar factor of a matrix iteratively - Attention: For matrices that are quite far away from an orthogonal matrix, - this might return a matrix with determinant = -1 ! */ - template <class field_type> - FieldMatrix<field_type,3,3> operator() (const FieldMatrix<field_type,3,3>& matrix, double tol = 0.001) const - { - size_t maxIterations = 100; - // Use Higham's method - auto polar = matrix; - for (size_t i=0; i<maxIterations; i++) - { - auto oldPolar = polar; - auto polarInvert = polar; - polarInvert.invert(); - for (size_t j=0; j<polar.N(); j++) - for (size_t k=0; k<polar.M(); k++) - polar[j][k] = 0.5 * (polar[j][k] + polarInvert[k][j]); - oldPolar -= polar; - if (oldPolar.frobenius_norm() < tol) { - break; - } - } - return polar; + size_t maxIterations = 100; + // Use Higham's method + auto polar = matrix; + for (size_t i=0; i<maxIterations; i++) + { + auto oldPolar = polar; + auto polarInvert = polar; + polarInvert.invert(); + for (size_t j=0; j<polar.N(); j++) + for (size_t k=0; k<polar.M(); k++) + polar[j][k] = 0.5 * (polar[j][k] + polarInvert[k][j]); + oldPolar -= polar; + if (oldPolar.frobenius_norm() < tol) { + break; } - }; - - class HighamNoferiniPolarDecomposition + } + return polar; + } + }; + + class HighamNoferiniPolarDecomposition + { + public: + /** \brief Compute the polar factor part of a matrix using the paper + "An algorithm to compute the polar decomposition of a 3 × 3 matrix" from Higham and Noferini (2015) + * \param matrix Compute the polar factor part of this matrix + * \param tau1 tolerance, default value taken from Higham and Noferini + * \param tau2 tolerance, default value taken from Higham and Noferini + */ + template <class field_type> + FieldMatrix<field_type, 3, 3> operator() (const FieldMatrix<field_type, 3, 3>& matrix, double tau1 = 0.0001, double tau2 = 0.0001) const { - public: - /** \brief Compute the polar factor part of a matrix using the paper - "An algorithm to compute the polar decomposition of a 3 × 3 matrix" from Higham and Noferini (2015) - * \param matrix Compute the polar factor part of this matrix - * \param tau1 tolerance, default value taken from Higham and Noferini - * \param tau2 tolerance, default value taken from Higham and Noferini - */ - template <class field_type> - FieldMatrix<field_type, 3, 3> operator() (const FieldMatrix<field_type, 3, 3>& matrix, double tau1 = 0.0001, double tau2 = 0.0001) const - { - auto normedMatrix = matrix; - normedMatrix /= normedMatrix.frobenius_norm(); - FieldMatrix<field_type,4,4> bilinearmatrix = bilinearMatrix(normedMatrix); - auto detB = determinantLaplace(bilinearmatrix); - auto detM = normedMatrix.determinant(); - field_type domEV = 0; - if (detM < 0) { - detM = -detM; - bilinearmatrix = -bilinearmatrix; - } - - if ( detB + 1./3 > tau1) { // estimate dominant eigenvalue if well separated => use formula - domEV = dominantEV( detB, detM ); - } else { // eignevalue nearly a double root => use newtons method - auto coefficients = characteristicCoefficients(bilinearmatrix); - domEV = dominantEVNewton(coefficients,detB); - } - // compute iterationmatrix B_S - FieldMatrix<field_type,4,4> BS = {{domEV,0,0,0},{0,domEV,0,0},{0,0,domEV,0},{0,0,0,domEV}}; - BS -= bilinearmatrix; - if (detB < 1 - tau2) { - Rotation<field_type,3> v(obtainQuaternion(BS)); - FieldMatrix<field_type,3,3> mat; - v.matrix(mat); - return mat; - } else { - DUNE_THROW(NotImplemented, "The eigenvalues are not wellseparated => inverse iteration won't converge!"); - } - } - - protected: - - /** \brief Compute the bilinear matrix for a given 3x3 matrix*/ - template <class field_type> - static FieldMatrix<field_type,4,4> bilinearMatrix(const FieldMatrix<field_type,3,3> matrix) - { - FieldMatrix<field_type,4,4> bilinearmatrix; - bilinearmatrix[0][0] = matrix[0][0] + matrix[1][1] + matrix[2][2]; - bilinearmatrix[0][1] = matrix[1][2] - matrix[2][1]; - bilinearmatrix[0][2] = matrix[2][0] - matrix[0][2]; - bilinearmatrix[0][3] = matrix[0][1] - matrix[1][0]; - bilinearmatrix[1][0] = bilinearmatrix[0][1]; - bilinearmatrix[1][1] = matrix[0][0] - matrix[1][1] - matrix[2][2]; - bilinearmatrix[1][2] = matrix[0][1] + matrix[1][0]; - bilinearmatrix[1][3] = matrix[2][0] + matrix[0][2]; - bilinearmatrix[2][0] = bilinearmatrix[0][2]; - bilinearmatrix[2][1] = bilinearmatrix[1][2]; - bilinearmatrix[2][2] = matrix[1][1] - matrix[0][0] - matrix[2][2]; - bilinearmatrix[2][3] = matrix[1][2] + matrix[2][1]; - bilinearmatrix[3][0] = bilinearmatrix[0][3]; - bilinearmatrix[3][1] = bilinearmatrix[1][3]; - bilinearmatrix[3][2] = bilinearmatrix[2][3]; - bilinearmatrix[3][3] = matrix[2][2] - matrix[0][0] - matrix[1][1]; - return bilinearmatrix; - } - - /** \brief Compute the determinant of a 4x4 matrix using the Laplace method - The implementation is faster than calling matrix.determinant()*/ - template <class field_type> - static field_type determinantLaplace( const FieldMatrix<field_type,4,4>& matrix) - { - field_type T2233 = matrix[2][2] * matrix[3][3]; - field_type T3223 = matrix[3][2] * matrix[2][3]; - field_type T1 = T2233 - T3223; - - field_type T1233 = matrix[1][2] * matrix[3][3]; - field_type T3213 = matrix[3][2] * matrix[1][3]; - field_type T2 = T1233 - T3213; - - field_type T1223 = matrix[1][2] * matrix[2][3]; - field_type T2213 = matrix[2][2] * matrix[1][3]; - field_type T3 = T1223 - T2213; - - field_type T0233 = matrix[0][2] * matrix[3][3]; - field_type T3203 = matrix[3][2] * matrix[0][3]; - field_type T4 = T3203 - T0233; - - field_type T0223 = matrix[0][2] * matrix[2][3]; - field_type T2203 = matrix[2][2] * matrix[0][3]; - field_type T5 = T0223- T2203; - - field_type T0213 = matrix[0][2] * matrix[1][3]; - field_type T1203 = matrix[1][2] * matrix[0][3]; - field_type T6 = T0213-T1203; - - return matrix[0][0] * (matrix[1][1] * T1 - matrix[2][1] * T3 + matrix[3][1] * T3) - - matrix[1][0] * (matrix[0][1] * T1 + matrix[2][1] * T4 + matrix[3][1] * T5) - + matrix[2][0] * (matrix[0][1] * T2 + matrix[1][1] * T4 + matrix[3][1] * T6) - - matrix[3][0] * (matrix[0][1] * T2 - matrix[1][1] * T5 + matrix[2][1] * T6); - } - - /** \brief Return the factors a,b,c of the characteristic polynomial x^4+a*x^3+b*x^2+c*x + detB */ - template <class field_type> - static FieldVector<field_type,3> characteristicCoefficients(const FieldMatrix<field_type,4,4>& matrix) - { - field_type a = - Dune::GFE::trace(matrix); - field_type b = matrix[0][0] * (matrix[1][1] + matrix[2][2] + matrix[3][3]) - + matrix[3][3] * (matrix[1][1] + matrix[2][2]) - - (matrix[1][0] * matrix[0][1] + matrix[2][0] * matrix[0][2] + - matrix[3][0] * matrix[0][3] + matrix[1][2] * matrix[2][1] + - matrix[1][3] * matrix[3][1] + matrix[3][2] * matrix[2][3]); - - field_type c = matrix[0][0] * (matrix[1][2] * matrix[2][1] + matrix[1][3] * matrix[3][1] + matrix[2][3] * matrix[3][2] -(matrix[2][2] * matrix[3][3] + matrix[1][1] * (matrix[2][2] + matrix[3][3]))) - + matrix[0][1] * (matrix[1][0] * (matrix[2][2] + matrix[3][3]) -(matrix[1][2] * matrix[2][0] + matrix[1][3] * matrix[3][0])) - + matrix[0][2] * (matrix[2][0] * (matrix[1][1] + matrix[3][3]) -(matrix[1][0] * matrix[2][1] + matrix[2][3] * matrix[3][0])) - + matrix[0][3] * (matrix[3][0] * (matrix[1][1] + matrix[2][2]) -(matrix[1][0] * matrix[3][1] + matrix[2][0] * matrix[3][2])) - + matrix[1][1] * (matrix[2][3] * matrix[3][2] - matrix[2][2] * matrix[3][3]) - + matrix[1][2] * (matrix[2][1] * matrix[3][3] - matrix[2][3] * matrix[3][1]) - + matrix[1][3] * (matrix[2][2] * matrix[3][1] - matrix[2][1] * matrix[3][2]); - return {a, b, c}; - } - - /** \brief Return the dominant Eigenvalue of the matrix B */ - template <class field_type> - static field_type dominantEV(field_type detB, field_type detM ) - { - auto c = 8 * detM; - auto sigma0 = 1 + 3 * detB; - auto sigma1 = 27/16*c*c + 9 * detB - 1; - auto alpha = sigma1/std::pow(sigma0, 1.5 ); - auto z = 4/3 * (1 + std::sqrt(sigma0) * std::cos(std::acos(alpha)/3)); - auto s = std::sqrt(z)/ 2; - auto x = 4-z+c/s; - if (x > 0) - return s + std::sqrt(x)/2; - else - return s; - } - - /** \brief Return the dominant Eigenvalue of the matrix B. - This algorithm corresponds to Algorithm 3.4 in - "An algorithm to compute the polar decomposition - of a 3 × 3 matrix" from Higham and Noferini (2015) - * \param coefficients Coefficients of the characteristic polynomial for the matrix b - * \param detB Determinant of the matrix B - * \param tol Tolerance for the Newton method, the value is taken from the paper by Higham and Noferini - */ - template <class field_type> - static field_type dominantEVNewton(const FieldVector<field_type,3>& coefficients, field_type detB, double tol = 10e-5) - { - field_type charPol = 0; - field_type charPolDeriv = 0; - field_type lambdaOld = 3; - field_type domEV = std::sqrt(3); - while ((lambdaOld - domEV)/domEV > tol) { - lambdaOld = domEV; - charPol = detB + domEV*( coefficients[2] + domEV*( coefficients[1] + domEV*( coefficients[0]+ domEV ) ) ); - charPolDeriv= coefficients[2] + domEV * ( 2*coefficients[1]+ domEV * ( 3*coefficients[0] + 4*domEV) ); - domEV = domEV- charPol / charPolDeriv; + auto normedMatrix = matrix; + normedMatrix /= normedMatrix.frobenius_norm(); + FieldMatrix<field_type,4,4> bilinearmatrix = bilinearMatrix(normedMatrix); + auto detB = determinantLaplace(bilinearmatrix); + auto detM = normedMatrix.determinant(); + field_type domEV = 0; + if (detM < 0) { + detM = -detM; + bilinearmatrix = -bilinearmatrix; + } + + if ( detB + 1./3 > tau1) { // estimate dominant eigenvalue if well separated => use formula + domEV = dominantEV( detB, detM ); + } else { // eignevalue nearly a double root => use newtons method + auto coefficients = characteristicCoefficients(bilinearmatrix); + domEV = dominantEVNewton(coefficients,detB); + } + // compute iterationmatrix B_S + FieldMatrix<field_type,4,4> BS = {{domEV,0,0,0},{0,domEV,0,0},{0,0,domEV,0},{0,0,0,domEV}}; + BS -= bilinearmatrix; + if (detB < 1 - tau2) { + Rotation<field_type,3> v(obtainQuaternion(BS)); + FieldMatrix<field_type,3,3> mat; + v.matrix(mat); + return mat; + } else { + DUNE_THROW(NotImplemented, "The eigenvalues are not wellseparated => inverse iteration won't converge!"); + } + } + + protected: + + /** \brief Compute the bilinear matrix for a given 3x3 matrix*/ + template <class field_type> + static FieldMatrix<field_type,4,4> bilinearMatrix(const FieldMatrix<field_type,3,3> matrix) + { + FieldMatrix<field_type,4,4> bilinearmatrix; + bilinearmatrix[0][0] = matrix[0][0] + matrix[1][1] + matrix[2][2]; + bilinearmatrix[0][1] = matrix[1][2] - matrix[2][1]; + bilinearmatrix[0][2] = matrix[2][0] - matrix[0][2]; + bilinearmatrix[0][3] = matrix[0][1] - matrix[1][0]; + bilinearmatrix[1][0] = bilinearmatrix[0][1]; + bilinearmatrix[1][1] = matrix[0][0] - matrix[1][1] - matrix[2][2]; + bilinearmatrix[1][2] = matrix[0][1] + matrix[1][0]; + bilinearmatrix[1][3] = matrix[2][0] + matrix[0][2]; + bilinearmatrix[2][0] = bilinearmatrix[0][2]; + bilinearmatrix[2][1] = bilinearmatrix[1][2]; + bilinearmatrix[2][2] = matrix[1][1] - matrix[0][0] - matrix[2][2]; + bilinearmatrix[2][3] = matrix[1][2] + matrix[2][1]; + bilinearmatrix[3][0] = bilinearmatrix[0][3]; + bilinearmatrix[3][1] = bilinearmatrix[1][3]; + bilinearmatrix[3][2] = bilinearmatrix[2][3]; + bilinearmatrix[3][3] = matrix[2][2] - matrix[0][0] - matrix[1][1]; + return bilinearmatrix; + } + + /** \brief Compute the determinant of a 4x4 matrix using the Laplace method + The implementation is faster than calling matrix.determinant()*/ + template <class field_type> + static field_type determinantLaplace( const FieldMatrix<field_type,4,4>& matrix) + { + field_type T2233 = matrix[2][2] * matrix[3][3]; + field_type T3223 = matrix[3][2] * matrix[2][3]; + field_type T1 = T2233 - T3223; + + field_type T1233 = matrix[1][2] * matrix[3][3]; + field_type T3213 = matrix[3][2] * matrix[1][3]; + field_type T2 = T1233 - T3213; + + field_type T1223 = matrix[1][2] * matrix[2][3]; + field_type T2213 = matrix[2][2] * matrix[1][3]; + field_type T3 = T1223 - T2213; + + field_type T0233 = matrix[0][2] * matrix[3][3]; + field_type T3203 = matrix[3][2] * matrix[0][3]; + field_type T4 = T3203 - T0233; + + field_type T0223 = matrix[0][2] * matrix[2][3]; + field_type T2203 = matrix[2][2] * matrix[0][3]; + field_type T5 = T0223- T2203; + + field_type T0213 = matrix[0][2] * matrix[1][3]; + field_type T1203 = matrix[1][2] * matrix[0][3]; + field_type T6 = T0213-T1203; + + return matrix[0][0] * (matrix[1][1] * T1 - matrix[2][1] * T3 + matrix[3][1] * T3) + - matrix[1][0] * (matrix[0][1] * T1 + matrix[2][1] * T4 + matrix[3][1] * T5) + + matrix[2][0] * (matrix[0][1] * T2 + matrix[1][1] * T4 + matrix[3][1] * T6) + - matrix[3][0] * (matrix[0][1] * T2 - matrix[1][1] * T5 + matrix[2][1] * T6); + } + + /** \brief Return the factors a,b,c of the characteristic polynomial x^4+a*x^3+b*x^2+c*x + detB */ + template <class field_type> + static FieldVector<field_type,3> characteristicCoefficients(const FieldMatrix<field_type,4,4>& matrix) + { + field_type a = - Dune::GFE::trace(matrix); + field_type b = matrix[0][0] * (matrix[1][1] + matrix[2][2] + matrix[3][3]) + + matrix[3][3] * (matrix[1][1] + matrix[2][2]) + - (matrix[1][0] * matrix[0][1] + matrix[2][0] * matrix[0][2] + + matrix[3][0] * matrix[0][3] + matrix[1][2] * matrix[2][1] + + matrix[1][3] * matrix[3][1] + matrix[3][2] * matrix[2][3]); + + field_type c = matrix[0][0] * (matrix[1][2] * matrix[2][1] + matrix[1][3] * matrix[3][1] + matrix[2][3] * matrix[3][2] -(matrix[2][2] * matrix[3][3] + matrix[1][1] * (matrix[2][2] + matrix[3][3]))) + + matrix[0][1] * (matrix[1][0] * (matrix[2][2] + matrix[3][3]) -(matrix[1][2] * matrix[2][0] + matrix[1][3] * matrix[3][0])) + + matrix[0][2] * (matrix[2][0] * (matrix[1][1] + matrix[3][3]) -(matrix[1][0] * matrix[2][1] + matrix[2][3] * matrix[3][0])) + + matrix[0][3] * (matrix[3][0] * (matrix[1][1] + matrix[2][2]) -(matrix[1][0] * matrix[3][1] + matrix[2][0] * matrix[3][2])) + + matrix[1][1] * (matrix[2][3] * matrix[3][2] - matrix[2][2] * matrix[3][3]) + + matrix[1][2] * (matrix[2][1] * matrix[3][3] - matrix[2][3] * matrix[3][1]) + + matrix[1][3] * (matrix[2][2] * matrix[3][1] - matrix[2][1] * matrix[3][2]); + return {a, b, c}; + } + + /** \brief Return the dominant Eigenvalue of the matrix B */ + template <class field_type> + static field_type dominantEV(field_type detB, field_type detM ) + { + auto c = 8 * detM; + auto sigma0 = 1 + 3 * detB; + auto sigma1 = 27/16*c*c + 9 * detB - 1; + auto alpha = sigma1/std::pow(sigma0, 1.5 ); + auto z = 4/3 * (1 + std::sqrt(sigma0) * std::cos(std::acos(alpha)/3)); + auto s = std::sqrt(z)/ 2; + auto x = 4-z+c/s; + if (x > 0) + return s + std::sqrt(x)/2; + else + return s; + } + + /** \brief Return the dominant Eigenvalue of the matrix B. + This algorithm corresponds to Algorithm 3.4 in + "An algorithm to compute the polar decomposition + of a 3 × 3 matrix" from Higham and Noferini (2015) + * \param coefficients Coefficients of the characteristic polynomial for the matrix b + * \param detB Determinant of the matrix B + * \param tol Tolerance for the Newton method, the value is taken from the paper by Higham and Noferini + */ + template <class field_type> + static field_type dominantEVNewton(const FieldVector<field_type,3>& coefficients, field_type detB, double tol = 10e-5) + { + field_type charPol = 0; + field_type charPolDeriv = 0; + field_type lambdaOld = 3; + field_type domEV = std::sqrt(3); + while ((lambdaOld - domEV)/domEV > tol) { + lambdaOld = domEV; + charPol = detB + domEV*( coefficients[2] + domEV*( coefficients[1] + domEV*( coefficients[0]+ domEV ) ) ); + charPolDeriv= coefficients[2] + domEV * ( 2*coefficients[1]+ domEV * ( 3*coefficients[0] + 4*domEV) ); + domEV = domEV- charPol / charPolDeriv; + } + return domEV; + } + + /** \brief Return quaternion vector resulting from step 7 and 8 in Algorithm 3.2 from Higham and Noferini (2015) + Step 7: Calculate the LDLT factorization of the given matrix with diagonal pivoting + Step 8: Using L and the pivotmatix, calculate the vector v + * \param shiftedB shifted matrix B as given in chapter 3 of Higham an Noferini: shiftedB = lambda * Id - B, where lambda is the dominant eigenvalue of B + \returns v the respective polar factor in quaternion form + *\ + */ + template <class field_type> + static FieldVector<field_type,4> obtainQuaternion(const FieldMatrix<field_type,4,4> shiftedB) + { + FieldMatrix<field_type,4,4> P = 0; + FieldMatrix<field_type,4,4> Pleft = 0; + FieldMatrix<field_type,4,4> Pright = 0; + FieldMatrix<field_type,4,4> L = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}; + FieldVector<field_type,4> D = {0,0,0,0}; + FieldVector<field_type,4> Bdiag = {shiftedB[0][0], shiftedB[1][1], shiftedB[2][2], shiftedB[3][3]}; + int maxi = 0; // Index of the maximal element + int secmax = 0; + int secmin = 0; + int mini = 0; // Index of the minimal element + + // Find Pivotmatrix + for (int i = 0; i < 4; ++i) // going through the diagonal searching for the maximum + if ( Bdiag[maxi] < Bdiag[i] ) // found a bigger one but smaller than the older ones + maxi = i; + + Pleft[0][maxi] = 1; + Pright[maxi][0] = 1; // Largest element to the first position + + for (int i = 0; i < 4; ++i) // going through the diagonal searching for the minimum + if ( Bdiag[mini] > Bdiag[i] ) // found a smaller one but smaller than the older ones + mini = i; + + Pleft[3][mini] = 1; + Pright[mini][3] = 1; // Smallest element to the last position + + + for (int i = 0; i < 4; ++i) { + if ( i != maxi && i != mini ) { + for (int j = 0; j < 4; ++j) { + if ( j != maxi && j != mini && j != i ) { + if ( Bdiag[i] < Bdiag[j] ) { + Pleft[1][j] = 1; // Second largest element at the second position + Pright[j][1] = 1; + Pleft[2][i] = 1; // Third largest element at the third position + Pright[i][2] = 1; + secmin = i; + secmax = j; + } else { + Pleft[2][j] = 1; + Pright[j][2] = 1; + Pleft[1][i] = 1; + Pright[i][1] = 1; + secmin = j; + secmax = i; + } } - return domEV; + } } - - /** \brief Return quaternion vector resulting from step 7 and 8 in Algorithm 3.2 from Higham and Noferini (2015) - Step 7: Calculate the LDLT factorization of the given matrix with diagonal pivoting - Step 8: Using L and the pivotmatix, calculate the vector v - * \param shiftedB shifted matrix B as given in chapter 3 of Higham an Noferini: shiftedB = lambda * Id - B, where lambda is the dominant eigenvalue of B - \returns v the respective polar factor in quaternion form - *\ - */ - template <class field_type> - static FieldVector<field_type,4> obtainQuaternion(const FieldMatrix<field_type,4,4> shiftedB) - { - FieldMatrix<field_type,4,4> P = 0; - FieldMatrix<field_type,4,4> Pleft = 0; - FieldMatrix<field_type,4,4> Pright = 0; - FieldMatrix<field_type,4,4> L = {{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}; - FieldVector<field_type,4> D = {0,0,0,0}; - FieldVector<field_type,4> Bdiag = {shiftedB[0][0], shiftedB[1][1], shiftedB[2][2], shiftedB[3][3]}; - int maxi = 0; // Index of the maximal element - int secmax = 0; - int secmin = 0; - int mini = 0; // Index of the minimal element - - // Find Pivotmatrix - for (int i = 0; i < 4; ++i) // going through the diagonal searching for the maximum - if ( Bdiag[maxi] < Bdiag[i] ) // found a bigger one but smaller than the older ones - maxi = i; - - Pleft[0][maxi] = 1; - Pright[maxi][0] = 1; // Largest element to the first position - - for (int i = 0; i < 4; ++i) // going through the diagonal searching for the minimum - if ( Bdiag[mini] > Bdiag[i] ) // found a smaller one but smaller than the older ones - mini = i; - - Pleft[3][mini] = 1; - Pright[mini][3] = 1; // Smallest element to the last position - - - for (int i = 0; i < 4; ++i) { - if ( i != maxi && i != mini ) { - for (int j = 0; j < 4; ++j) { - if ( j != maxi && j != mini && j != i ) { - if ( Bdiag[i] < Bdiag[j] ) { - Pleft[1][j] = 1; // Second largest element at the second position - Pright[j][1] = 1; - Pleft[2][i] = 1; // Third largest element at the third position - Pright[i][2] = 1; - secmin = i; - secmax = j; - } else { - Pleft[2][j] = 1; - Pright[j][2] = 1; - Pleft[1][i] = 1; - Pright[i][1] = 1; - secmin = j; - secmax = i; - } - } - } - } - } - - //P = Pleft*shiftedB*Pright; - P[maxi][maxi] = shiftedB[0][0]; - P[maxi][secmax] = shiftedB[0][1]; - P[maxi][secmin] = shiftedB[0][2]; - P[maxi][mini] = shiftedB[0][3]; - - P[secmax][maxi] = shiftedB[1][0]; - P[secmax][secmax] = shiftedB[1][1]; - P[secmax][secmin] = shiftedB[1][2]; - P[secmax][mini] = shiftedB[1][3]; - - P[secmin][maxi] = shiftedB[2][0]; - P[secmin][secmax] = shiftedB[2][1]; - P[secmin][secmin] = shiftedB[2][2]; - P[secmin][mini] = shiftedB[2][3]; - - P[mini][maxi] = shiftedB[3][0]; - P[mini][secmax] = shiftedB[3][1]; - P[mini][secmin] = shiftedB[3][2]; - P[mini][mini] = shiftedB[3][3]; - - // Choleskydecomposition - - for (int k = 0; k < 4; ++k) { //columns - D[k] = P[k][k]; - for (int j = 0; j < k; ++j)//sum - D[k] -= L[k][j]*L[k][j] * D[j]; - - for (int i = k+1; i < 4; ++i) { //rows - L[i][k] = P[i][k]; - for (int j = 0; j < k; ++j)//sum - L[i][k] -= L[i][j]*L[k][j] * D[j]; - L[i][k] /= D[k]; - } - } - - FieldVector<field_type,4> v = {0,0,0,0}; - FieldVector<field_type,4> unnormed = {0,0,0,1}; - auto neg = -L[3][2]; - unnormed[0] = L[1][0] * ( L[3][1] + L[2][1]*neg ) + L[2][0] * L[3][2] - L[3][0]; - unnormed[1] = L[2][1] * L[3][2] - L[3][1]; - unnormed[2] = neg; - unnormed /= unnormed.two_norm(); - v[0] = unnormed[maxi]; - v[1] = unnormed[secmax]; - v[2] = unnormed[secmin]; - v[3] = unnormed[mini]; - return v; + } + + //P = Pleft*shiftedB*Pright; + P[maxi][maxi] = shiftedB[0][0]; + P[maxi][secmax] = shiftedB[0][1]; + P[maxi][secmin] = shiftedB[0][2]; + P[maxi][mini] = shiftedB[0][3]; + + P[secmax][maxi] = shiftedB[1][0]; + P[secmax][secmax] = shiftedB[1][1]; + P[secmax][secmin] = shiftedB[1][2]; + P[secmax][mini] = shiftedB[1][3]; + + P[secmin][maxi] = shiftedB[2][0]; + P[secmin][secmax] = shiftedB[2][1]; + P[secmin][secmin] = shiftedB[2][2]; + P[secmin][mini] = shiftedB[2][3]; + + P[mini][maxi] = shiftedB[3][0]; + P[mini][secmax] = shiftedB[3][1]; + P[mini][secmin] = shiftedB[3][2]; + P[mini][mini] = shiftedB[3][3]; + + // Choleskydecomposition + + for (int k = 0; k < 4; ++k) { //columns + D[k] = P[k][k]; + for (int j = 0; j < k; ++j) //sum + D[k] -= L[k][j]*L[k][j] * D[j]; + + for (int i = k+1; i < 4; ++i) { //rows + L[i][k] = P[i][k]; + for (int j = 0; j < k; ++j) //sum + L[i][k] -= L[i][j]*L[k][j] * D[j]; + L[i][k] /= D[k]; } - }; + } + + FieldVector<field_type,4> v = {0,0,0,0}; + FieldVector<field_type,4> unnormed = {0,0,0,1}; + auto neg = -L[3][2]; + unnormed[0] = L[1][0] * ( L[3][1] + L[2][1]*neg ) + L[2][0] * L[3][2] - L[3][0]; + unnormed[1] = L[2][1] * L[3][2] - L[3][1]; + unnormed[2] = neg; + unnormed /= unnormed.two_norm(); + v[0] = unnormed[maxi]; + v[1] = unnormed[secmax]; + v[2] = unnormed[secmin]; + v[3] = unnormed[mini]; + return v; + } + }; } #endif diff --git a/dune/gfe/riemannianpnsolver.cc b/dune/gfe/riemannianpnsolver.cc index a12f4745..70452c05 100644 --- a/dune/gfe/riemannianpnsolver.cc +++ b/dune/gfe/riemannianpnsolver.cc @@ -22,34 +22,34 @@ #include <dune/solvers/norms/h1seminorm.hh> // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) #include <dune/gfe/parallel/matrixcommunicator.hh> #include <dune/gfe/parallel/vectorcommunicator.hh> #endif template <class Basis, class TargetSpace, class Assembler> void RiemannianProximalNewtonSolver<Basis, TargetSpace, Assembler>:: - setup(const GridType& grid, - const Assembler* assembler, - const SolutionType& x, - const Dune::BitSetVector<blocksize>& dirichletNodes, - const Dune::ParameterTree& parameterSet) +setup(const GridType& grid, + const Assembler* assembler, + const SolutionType& x, + const Dune::BitSetVector<blocksize>& dirichletNodes, + const Dune::ParameterTree& parameterSet) { - if(parameterSet.get("norm", "infinity") == "infinity") - normType_ = ErrorNormType::infinity; - else if(parameterSet.get("norm", "infinity") == "H1-Semi") - normType_ = ErrorNormType::H1semi; - else - DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); - - setup(grid, - assembler, - x, - dirichletNodes, - parameterSet.get<double>("tolerance"), - parameterSet.get<int>("maxProximalNewtonSteps "), - parameterSet.get<double>("initialRegularization"), - parameterSet.get<bool>("instrumented", 0)); + if(parameterSet.get("norm", "infinity") == "infinity") + normType_ = ErrorNormType::infinity; + else if(parameterSet.get("norm", "infinity") == "H1-Semi") + normType_ = ErrorNormType::H1semi; + else + DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); + + setup(grid, + assembler, + x, + dirichletNodes, + parameterSet.get<double>("tolerance"), + parameterSet.get<int>("maxProximalNewtonSteps "), + parameterSet.get<double>("initialRegularization"), + parameterSet.get<bool>("instrumented", 0)); } template <class Basis, class TargetSpace, class Assembler> @@ -63,492 +63,492 @@ setup(const GridType& grid, double initialRegularization, bool instrumented) { - grid_ = &grid; - assembler_ = assembler; - x_ = x; - this->tolerance_ = tolerance; - maxProximalNewtonSteps_ = maxProximalNewtonSteps; - initialRegularization_ = initialRegularization; - instrumented_ = instrumented; - ignoreNodes_ = &dirichletNodes; - -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - ////////////////////////////////////////////////////////////////// - // Create global numbering for matrix and vector transfer - ////////////////////////////////////////////////////////////////// - - globalMapper_ = std::make_unique<GlobalMapper>(grid_->leafGridView()); - // Transfer all Dirichlet data to the master processor - VectorCommunicator<GlobalMapper, typename GridType::LeafGridView::CollectiveCommunication, Dune::BitSetVector<blocksize> > vectorComm(*globalMapper_, - grid_->leafGridView().comm(), - 0); - auto globalDirichletNodes = new Dune::BitSetVector<blocksize>(vectorComm.reduceCopy(dirichletNodes)); + grid_ = &grid; + assembler_ = assembler; + x_ = x; + this->tolerance_ = tolerance; + maxProximalNewtonSteps_ = maxProximalNewtonSteps; + initialRegularization_ = initialRegularization; + instrumented_ = instrumented; + ignoreNodes_ = &dirichletNodes; + + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + ////////////////////////////////////////////////////////////////// + // Create global numbering for matrix and vector transfer + ////////////////////////////////////////////////////////////////// + + globalMapper_ = std::make_unique<GlobalMapper>(grid_->leafGridView()); + // Transfer all Dirichlet data to the master processor + VectorCommunicator<GlobalMapper, typename GridType::LeafGridView::CollectiveCommunication, Dune::BitSetVector<blocksize> > vectorComm(*globalMapper_, + grid_->leafGridView().comm(), + 0); + auto globalDirichletNodes = new Dune::BitSetVector<blocksize>(vectorComm.reduceCopy(dirichletNodes)); #else - auto globalDirichletNodes = new Dune::BitSetVector<blocksize>(dirichletNodes); + auto globalDirichletNodes = new Dune::BitSetVector<blocksize>(dirichletNodes); #endif - // ////////////////////////////////////////////////////////////////////////////////////// - // Assemble a Laplace matrix to create a norm that's equivalent to the H1-norm - // ////////////////////////////////////////////////////////////////////////////////////// + // ////////////////////////////////////////////////////////////////////////////////////// + // Assemble a Laplace matrix to create a norm that's equivalent to the H1-norm + // ////////////////////////////////////////////////////////////////////////////////////// - const Basis& basis = assembler_->getBasis(); - Dune::Fufem::DuneFunctionsOperatorAssembler<Basis,Basis> operatorAssembler(basis, basis); + const Basis& basis = assembler_->getBasis(); + Dune::Fufem::DuneFunctionsOperatorAssembler<Basis,Basis> operatorAssembler(basis, basis); - Dune::Fufem::LaplaceAssembler laplaceStiffness; - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> > ScalarMatrixType; - ScalarMatrixType localA; + Dune::Fufem::LaplaceAssembler laplaceStiffness; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> > ScalarMatrixType; + ScalarMatrixType localA; - operatorAssembler.assembleBulk(Dune::Fufem::istlMatrixBackend(localA), laplaceStiffness); + operatorAssembler.assembleBulk(Dune::Fufem::istlMatrixBackend(localA), laplaceStiffness); -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - LocalMapper localMapper = MapperFactory<Basis>::createLocalMapper(grid_->leafGridView()); - - MatrixCommunicator<GlobalMapper, - typename GridType::LeafGridView, - typename GridType::LeafGridView, - ScalarMatrixType, - LocalMapper, - LocalMapper> matrixComm(*globalMapper_, grid_->leafGridView(), localMapper, localMapper, 0); - if (instrumented_) { - auto A = std::make_shared<ScalarMatrixType>(matrixComm.reduceAdd(localA)); + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + LocalMapper localMapper = MapperFactory<Basis>::createLocalMapper(grid_->leafGridView()); + + MatrixCommunicator<GlobalMapper, + typename GridType::LeafGridView, + typename GridType::LeafGridView, + ScalarMatrixType, + LocalMapper, + LocalMapper> matrixComm(*globalMapper_, grid_->leafGridView(), localMapper, localMapper, 0); + if (instrumented_) { + auto A = std::make_shared<ScalarMatrixType>(matrixComm.reduceAdd(localA)); #else - if (instrumented_) { - auto A = std::make_shared<ScalarMatrixType>(localA); + if (instrumented_) { + auto A = std::make_shared<ScalarMatrixType>(localA); #endif - h1SemiNorm_ = std::make_shared<H1SemiNorm<CorrectionType> >(A); - } - ////////////////////////////////////////////////////////////////// - // Create the inner solver using a cholmod solver - ////////////////////////////////////////////////////////////////// + h1SemiNorm_ = std::make_shared<H1SemiNorm<CorrectionType> >(A); + } + ////////////////////////////////////////////////////////////////// + // Create the inner solver using a cholmod solver + ////////////////////////////////////////////////////////////////// #if DUNE_VERSION_GTE(DUNE_SOLVERS, 2, 8) - innerSolver_ = std::make_shared<Dune::Solvers::CholmodSolver<MatrixType,CorrectionType> >(); + innerSolver_ = std::make_shared<Dune::Solvers::CholmodSolver<MatrixType,CorrectionType> >(); #else - std::cout << "using umfpacksolver" << std::endl; - innerSolver_ = std::make_shared<Dune::Solvers::UMFPackSolver<MatrixType,CorrectionType> >(); + std::cout << "using umfpacksolver" << std::endl; + innerSolver_ = std::make_shared<Dune::Solvers::UMFPackSolver<MatrixType,CorrectionType> >(); #endif - innerSolver_->setIgnore(*globalDirichletNodes); + innerSolver_->setIgnore(*globalDirichletNodes); - // ////////////////////////////////////////////////////////////////////////////////////// - // Assemble a mass matrix to create a norm that's equivalent to the L2-norm - // This will be used to monitor the gradient - // ////////////////////////////////////////////////////////////////////////////////////// + // ////////////////////////////////////////////////////////////////////////////////////// + // Assemble a mass matrix to create a norm that's equivalent to the L2-norm + // This will be used to monitor the gradient + // ////////////////////////////////////////////////////////////////////////////////////// - Dune::Fufem::MassAssembler massStiffness; - ScalarMatrixType localMassMatrix; + Dune::Fufem::MassAssembler massStiffness; + ScalarMatrixType localMassMatrix; - operatorAssembler.assembleBulk(Dune::Fufem::istlMatrixBackend(localMassMatrix), massStiffness); + operatorAssembler.assembleBulk(Dune::Fufem::istlMatrixBackend(localMassMatrix), massStiffness); -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - auto massMatrix = std::make_shared<ScalarMatrixType>(matrixComm.reduceAdd(localMassMatrix)); + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + auto massMatrix = std::make_shared<ScalarMatrixType>(matrixComm.reduceAdd(localMassMatrix)); #else - auto massMatrix = std::make_shared<ScalarMatrixType>(localMassMatrix); + auto massMatrix = std::make_shared<ScalarMatrixType>(localMassMatrix); #endif - l2Norm_ = std::make_shared<H1SemiNorm<CorrectionType> >(massMatrix); + l2Norm_ = std::make_shared<H1SemiNorm<CorrectionType> >(massMatrix); - // Write all intermediate solutions, if requested - if (instrumented_ - && dynamic_cast<IterativeSolver<CorrectionType>*>(innerSolver_.get())) - dynamic_cast<IterativeSolver<CorrectionType>*>(innerSolver_.get())->historyBuffer_ = "tmp/mgHistory"; + // Write all intermediate solutions, if requested + if (instrumented_ + && dynamic_cast<IterativeSolver<CorrectionType>*>(innerSolver_.get())) + dynamic_cast<IterativeSolver<CorrectionType>*>(innerSolver_.get())->historyBuffer_ = "tmp/mgHistory"; - // //////////////////////////////////////////////////////////// - // Create Hessian matrix and its occupation structure - // //////////////////////////////////////////////////////////// + // //////////////////////////////////////////////////////////// + // Create Hessian matrix and its occupation structure + // //////////////////////////////////////////////////////////// - hessianMatrix_ = std::make_unique<MatrixType>(); - Dune::MatrixIndexSet indices(grid_->size(1), grid_->size(1)); - assembler_->getNeighborsPerVertex(indices); - indices.exportIdx(*hessianMatrix_); + hessianMatrix_ = std::make_unique<MatrixType>(); + Dune::MatrixIndexSet indices(grid_->size(1), grid_->size(1)); + assembler_->getNeighborsPerVertex(indices); + indices.exportIdx(*hessianMatrix_); } template <class Basis, class TargetSpace, class Assembler> void RiemannianProximalNewtonSolver<Basis,TargetSpace,Assembler>::solve() { - int rank = grid_->comm().rank(); - - // ///////////////////////////////////////////////////// - // Set up the log file, if requested - // ///////////////////////////////////////////////////// - FILE* fp = nullptr; - if (instrumented_) { - - fp = fopen("statistics", "w"); - if (!fp) - DUNE_THROW(Dune::IOError, "Couldn't open statistics file for writing!"); - + int rank = grid_->comm().rank(); + + // ///////////////////////////////////////////////////// + // Set up the log file, if requested + // ///////////////////////////////////////////////////// + FILE* fp = nullptr; + if (instrumented_) { + + fp = fopen("statistics", "w"); + if (!fp) + DUNE_THROW(Dune::IOError, "Couldn't open statistics file for writing!"); + + } + + // ///////////////////////////////////////////////////// + // Proximal Newton Solver + // ///////////////////////////////////////////////////// + + Dune::Timer energyTimer; + double oldEnergy = assembler_->computeEnergy(x_); + if (this->verbosity_ == Solver::FULL) + std::cout << "Energy computation took " << energyTimer.elapsed() << " sec." << std::endl; + + + oldEnergy = grid_->comm().sum(oldEnergy); + + bool recomputeGradientHessian = true; + CorrectionType rhs, rhs_global; + MatrixType stiffnessMatrix; + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + VectorCommunicator<GlobalMapper, typename GridType::LeafGridView::CollectiveCommunication, CorrectionType> vectorComm(*globalMapper_, + grid_->leafGridView().comm(), + 0); + LocalMapper localMapper = MapperFactory<Basis>::createLocalMapper(grid_->leafGridView()); + MatrixCommunicator<GlobalMapper, + typename GridType::LeafGridView, + typename GridType::LeafGridView, + MatrixType, + LocalMapper, + LocalMapper> matrixComm(*globalMapper_, + grid_->leafGridView(), + localMapper, + localMapper, + 0); +#endif + auto& i = statistics_.finalIteration; + double totalAssemblyTime = 0.0; + double totalSolverTime = 0.0; + double regularization = initialRegularization_; + for (i=0; i<maxProximalNewtonSteps_; i++) { + + Dune::Timer totalTimer; + if (this->verbosity_ == Solver::FULL and rank==0) { + std::cout << "----------------------------------------------------" << std::endl; + std::cout << " Proximal Newton Step Number: " << i + << ", regularization parameter: " << regularization + << ", energy: " << oldEnergy << std::endl; + std::cout << "----------------------------------------------------" << std::endl; } - // ///////////////////////////////////////////////////// - // Proximal Newton Solver - // ///////////////////////////////////////////////////// + CorrectionType corr(x_.size()); + corr = 0; - Dune::Timer energyTimer; - double oldEnergy = assembler_->computeEnergy(x_); - if (this->verbosity_ == Solver::FULL) - std::cout << "Energy computation took " << energyTimer.elapsed() << " sec." << std::endl; + if (recomputeGradientHessian) { + Dune::Timer gradientTimer; + assembler_->assembleGradientAndHessian(x_, + rhs, + *hessianMatrix_, + i==0 // assemble occupation pattern only for the first call + ); + std::cout << "Assembly took " << gradientTimer.elapsed() << " sec." << std::endl; + rhs *= -1; // The right hand side is the _negative_ gradient - oldEnergy = grid_->comm().sum(oldEnergy); - - bool recomputeGradientHessian = true; - CorrectionType rhs, rhs_global; - MatrixType stiffnessMatrix; -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - VectorCommunicator<GlobalMapper, typename GridType::LeafGridView::CollectiveCommunication, CorrectionType> vectorComm(*globalMapper_, - grid_->leafGridView().comm(), - 0); - LocalMapper localMapper = MapperFactory<Basis>::createLocalMapper(grid_->leafGridView()); - MatrixCommunicator<GlobalMapper, - typename GridType::LeafGridView, - typename GridType::LeafGridView, - MatrixType, - LocalMapper, - LocalMapper> matrixComm(*globalMapper_, - grid_->leafGridView(), - localMapper, - localMapper, - 0); + // Transfer vector data + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + rhs_global = vectorComm.reduceAdd(rhs); +#else + rhs_global = rhs; #endif - auto& i = statistics_.finalIteration; - double totalAssemblyTime = 0.0; - double totalSolverTime = 0.0; - double regularization = initialRegularization_; - for (i=0; i<maxProximalNewtonSteps_; i++) { - - Dune::Timer totalTimer; - if (this->verbosity_ == Solver::FULL and rank==0) { - std::cout << "----------------------------------------------------" << std::endl; - std::cout << " Proximal Newton Step Number: " << i - << ", regularization parameter: " << regularization - << ", energy: " << oldEnergy << std::endl; - std::cout << "----------------------------------------------------" << std::endl; - } - - CorrectionType corr(x_.size()); - corr = 0; - - if (recomputeGradientHessian) { - Dune::Timer gradientTimer; - assembler_->assembleGradientAndHessian(x_, - rhs, - *hessianMatrix_, - i==0 // assemble occupation pattern only for the first call - ); - std::cout << "Assembly took " << gradientTimer.elapsed() << " sec." << std::endl; - - rhs *= -1; // The right hand side is the _negative_ gradient - - // Transfer vector data -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - rhs_global = vectorComm.reduceAdd(rhs); + CorrectionType gradient = rhs_global; + for (size_t j=0; j<gradient.size(); j++) + for (size_t k=0; k<gradient[j].size(); k++) + if ((innerSolver_->ignore())[j][k]) // global Dirichlet nodes set + gradient[j][k] = 0; + + if (this->verbosity_ == Solver::FULL and rank==0) + std::cout << "Gradient norm: " << l2Norm_->operator()(gradient) << std::endl; + + if (this->verbosity_ == Solver::FULL and rank==0) + std::cout << "Overall assembly took " << gradientTimer.elapsed() << " sec." << std::endl; + totalAssemblyTime += gradientTimer.elapsed(); + + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + stiffnessMatrix = matrixComm.reduceAdd(*hessianMatrix_); #else - rhs_global = rhs; + stiffnessMatrix = *hessianMatrix_; #endif - CorrectionType gradient = rhs_global; - for (size_t j=0; j<gradient.size(); j++) - for (size_t k=0; k<gradient[j].size(); k++) - if ((innerSolver_->ignore())[j][k]) // global Dirichlet nodes set - gradient[j][k] = 0; + recomputeGradientHessian = false; - if (this->verbosity_ == Solver::FULL and rank==0) - std::cout << "Gradient norm: " << l2Norm_->operator()(gradient) << std::endl; + } - if (this->verbosity_ == Solver::FULL and rank==0) - std::cout << "Overall assembly took " << gradientTimer.elapsed() << " sec." << std::endl; - totalAssemblyTime += gradientTimer.elapsed(); + CorrectionType corr_global(rhs_global.size()); + corr_global = 0; + bool solved = true; -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - stiffnessMatrix = matrixComm.reduceAdd(*hessianMatrix_); -#else - stiffnessMatrix = *hessianMatrix_; -#endif - recomputeGradientHessian = false; + if (rank==0) + { + // Add the regularization - Identity Matrix for now + for (std::size_t i=0; i<stiffnessMatrix.N(); i++) + for(int j=0; j<blocksize; j++) + stiffnessMatrix[i][i][j][j] += regularization/scaling_[j]; - } + innerSolver_->setProblem(stiffnessMatrix,corr_global,rhs_global); + innerSolver_->preprocess(); - CorrectionType corr_global(rhs_global.size()); - corr_global = 0; - bool solved = true; - - if (rank==0) - { - // Add the regularization - Identity Matrix for now - for (std::size_t i=0; i<stiffnessMatrix.N(); i++) - for(int j=0; j<blocksize; j++) - stiffnessMatrix[i][i][j][j] += regularization/scaling_[j]; - - innerSolver_->setProblem(stiffnessMatrix,corr_global,rhs_global); - innerSolver_->preprocess(); - - /////////////////////////////// - // Solve ! - /////////////////////////////// - - std::cout << "Solve quadratic problem using cholmod solver..." << std::endl; - - Dune::Timer solutionTimer; - try { - innerSolver_->solve(); - } catch (Dune::Exception &e) { - std::cerr << "Error while solving: " << e << std::endl; - solved = false; - corr_global = 0; - } - std::cout << "Solving the quadratic problem took " << solutionTimer.elapsed() << " seconds." << std::endl; - totalSolverTime += solutionTimer.elapsed(); - } + /////////////////////////////// + // Solve ! + /////////////////////////////// - // Distribute solution - if (grid_->comm().size()>1 and rank==0) - std::cout << "Transfer solution back to root process ..." << std::endl; + std::cout << "Solve quadratic problem using cholmod solver..." << std::endl; -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - solved = grid_->comm().min(solved); - if (solved) { - corr = vectorComm.scatter(corr_global); - } else { - corr_global = 0; - corr = 0; - } + Dune::Timer solutionTimer; + try { + innerSolver_->solve(); + } catch (Dune::Exception &e) { + std::cerr << "Error while solving: " << e << std::endl; + solved = false; + corr_global = 0; + } + std::cout << "Solving the quadratic problem took " << solutionTimer.elapsed() << " seconds." << std::endl; + totalSolverTime += solutionTimer.elapsed(); + } + + // Distribute solution + if (grid_->comm().size()>1 and rank==0) + std::cout << "Transfer solution back to root process ..." << std::endl; + + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + solved = grid_->comm().min(solved); + if (solved) { + corr = vectorComm.scatter(corr_global); + } else { + corr_global = 0; + corr = 0; + } #else - corr = corr_global; + corr = corr_global; #endif - double corrNorm; - if (normType_ == ErrorNormType::infinity) - corrNorm = corr.infinity_norm(); - else if (normType_ == ErrorNormType::H1semi) - corrNorm = h1SemiNorm_->operator()(corr); - else - DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); + double corrNorm; + if (normType_ == ErrorNormType::infinity) + corrNorm = corr.infinity_norm(); + else if (normType_ == ErrorNormType::H1semi) + corrNorm = h1SemiNorm_->operator()(corr); + else + DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); - // Make norm of corr_global known on all processors - double corrGlobalNorm = grid_->comm().max(corrNorm); + // Make norm of corr_global known on all processors + double corrGlobalNorm = grid_->comm().max(corrNorm); - if (std::isnan(corrGlobalNorm)) - solved = false; + if (std::isnan(corrGlobalNorm)) + solved = false; - if (instrumented_) { + if (instrumented_) { #if 0 - fprintf(fp, "Proximal newton step: %ld, regularization parameter: %g\n", - i, regularization); + fprintf(fp, "Proximal newton step: %ld, regularization parameter: %g\n", + i, regularization); - // /////////////////////////////////////////////////////////////// - // Compute and measure progress against the exact solution - // for each proximal newton step - // /////////////////////////////////////////////////////////////// + // /////////////////////////////////////////////////////////////// + // Compute and measure progress against the exact solution + // for each proximal newton step + // /////////////////////////////////////////////////////////////// - CorrectionType exactSolution = corr; + CorrectionType exactSolution = corr; - // Start from 0 - double oldError = 0; - double totalConvRate = 1; - double convRate = 1; + // Start from 0 + double oldError = 0; + double totalConvRate = 1; + double convRate = 1; - // Write statistics of the initial solution - // Compute the energy norm - oldError = h1SemiNorm_->operator()(exactSolution); + // Write statistics of the initial solution + // Compute the energy norm + oldError = h1SemiNorm_->operator()(exactSolution); - for (int j=0; j<innerIterations_; j++) { + for (int j=0; j<innerIterations_; j++) { - // read iteration from file - CorrectionType intermediateSol(grid_->size(gridDim)); - intermediateSol = 0; - char iSolFilename[100]; - sprintf(iSolFilename, "tmp/mgHistory/intermediatesolution_%04d", j); + // read iteration from file + CorrectionType intermediateSol(grid_->size(gridDim)); + intermediateSol = 0; + char iSolFilename[100]; + sprintf(iSolFilename, "tmp/mgHistory/intermediatesolution_%04d", j); - FILE* fpInt = fopen(iSolFilename, "rb"); - if (!fpInt) - DUNE_THROW(Dune::IOError, "Couldn't open intermediate solution"); - for (size_t k=0; k<intermediateSol.size(); k++) - for (int l=0; l<blocksize; l++) - fread(&intermediateSol[k][l], sizeof(double), 1, fpInt); + FILE* fpInt = fopen(iSolFilename, "rb"); + if (!fpInt) + DUNE_THROW(Dune::IOError, "Couldn't open intermediate solution"); + for (size_t k=0; k<intermediateSol.size(); k++) + for (int l=0; l<blocksize; l++) + fread(&intermediateSol[k][l], sizeof(double), 1, fpInt); - fclose(fpInt); - //std::cout << "intermediateSol\n" << intermediateSol << std::endl; + fclose(fpInt); + //std::cout << "intermediateSol\n" << intermediateSol << std::endl; - // Compute errors - intermediateSol -= exactSolution; + // Compute errors + intermediateSol -= exactSolution; - //std::cout << "error\n" << intermediateSol << std::endl; + //std::cout << "error\n" << intermediateSol << std::endl; - // Compute the H1 norm - double error = h1SemiNorm_->operator()(intermediateSol); + // Compute the H1 norm + double error = h1SemiNorm_->operator()(intermediateSol); - convRate = error / oldError; - totalConvRate *= convRate; + convRate = error / oldError; + totalConvRate *= convRate; - if (error < 1e-12) - break; + if (error < 1e-12) + break; - std::cout << "Iteration: " << j << " "; - std::cout << "Errors: error " << error << ", convergence rate: " << convRate - << ", total conv rate " << pow(totalConvRate, 1/((double)j+1)) << std::endl; + std::cout << "Iteration: " << j << " "; + std::cout << "Errors: error " << error << ", convergence rate: " << convRate + << ", total conv rate " << pow(totalConvRate, 1/((double)j+1)) << std::endl; - fprintf(fp, "%d %g %g %g\n", j+1, error, convRate, pow(totalConvRate, 1/((double)j+1))); + fprintf(fp, "%d %g %g %g\n", j+1, error, convRate, pow(totalConvRate, 1/((double)j+1))); - oldError = error; + oldError = error; - } + } #endif + } + double energy = 0; + double modelDecrease = 0; + SolutionType newIterate = x_; + if (i == maxProximalNewtonSteps_ - 1) + std::cout << i+1 << " proximal newton steps were taken, the maximum was reached." << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; + + if (solved) { + if (this->verbosity_ == NumProc::FULL && rank==0) + if (normType_ == ErrorNormType::infinity) + std::cout << "infinity norm of the correction: " << corrGlobalNorm << std::endl; + else if (normType_ == ErrorNormType::H1semi) + std::cout << "H1-semi norm of the correction: " << corrGlobalNorm << std::endl; + else + DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); + + if (corrGlobalNorm < this->tolerance_ && corrGlobalNorm < 1/regularization) { + if (this->verbosity_ == NumProc::FULL and rank==0) + std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; + + if (this->verbosity_ != NumProc::QUIET and rank==0) + std::cout << i+1 << " proximal newton steps were taken" << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; + break; + } + + // //////////////////////////////////////////////////// + // Check whether proximal newton step can be accepted + // //////////////////////////////////////////////////// + + for (size_t j=0; j<newIterate.size(); j++) + newIterate[j] = TargetSpace::exp(newIterate[j], corr[j]); + try { + energy = assembler_->computeEnergy(newIterate); + } catch (Dune::Exception &e) { + std::cerr << "Error while computing the energy of the new Iterate: " << e << std::endl; + std::cerr << "Redoing proximal newton step with higher regularization parameter ..." << std::endl; + solved = false; + } + solved = grid_->comm().min(solved); + + if (!solved) { + energy = oldEnergy; + newIterate = x_; + } else { + energy = grid_->comm().sum(energy); + + // compute the model decrease + // It is $ m(x) - m(x+s) = -<g,s> - 0.5 <s, Hs> + // Note that rhs = -g + CorrectionType tmp(corr.size()); + tmp = 0; + hessianMatrix_->umv(corr, tmp); + modelDecrease = (rhs*corr) - 0.5 * (corr*tmp); + modelDecrease = grid_->comm().sum(modelDecrease); + + double relativeModelDecrease = modelDecrease / std::fabs(energy); + + if (this->verbosity_ == NumProc::FULL and rank==0) { + std::cout << "Absolute model decrease: " << modelDecrease + << ", functional decrease: " << oldEnergy - energy << std::endl; + std::cout << "Relative model decrease: " << relativeModelDecrease + << ", functional decrease: " << (oldEnergy - energy)/energy << std::endl; } - double energy = 0; - double modelDecrease = 0; - SolutionType newIterate = x_; - if (i == maxProximalNewtonSteps_ - 1) - std::cout << i+1 << " proximal newton steps were taken, the maximum was reached." << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; - - if (solved) { - if (this->verbosity_ == NumProc::FULL && rank==0) - if (normType_ == ErrorNormType::infinity) - std::cout << "infinity norm of the correction: " << corrGlobalNorm << std::endl; - else if (normType_ == ErrorNormType::H1semi) - std::cout << "H1-semi norm of the correction: " << corrGlobalNorm << std::endl; - else - DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); - - if (corrGlobalNorm < this->tolerance_ && corrGlobalNorm < 1/regularization) { - if (this->verbosity_ == NumProc::FULL and rank==0) - std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; - - if (this->verbosity_ != NumProc::QUIET and rank==0) - std::cout << i+1 << " proximal newton steps were taken" << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; - break; - } - - // //////////////////////////////////////////////////// - // Check whether proximal newton step can be accepted - // //////////////////////////////////////////////////// - - for (size_t j=0; j<newIterate.size(); j++) - newIterate[j] = TargetSpace::exp(newIterate[j], corr[j]); - try { - energy = assembler_->computeEnergy(newIterate); - } catch (Dune::Exception &e) { - std::cerr << "Error while computing the energy of the new Iterate: " << e << std::endl; - std::cerr << "Redoing proximal newton step with higher regularization parameter ..." << std::endl; - solved = false; - } - solved = grid_->comm().min(solved); - - if (!solved) { - energy = oldEnergy; - newIterate = x_; - } else { - energy = grid_->comm().sum(energy); - - // compute the model decrease - // It is $ m(x) - m(x+s) = -<g,s> - 0.5 <s, Hs> - // Note that rhs = -g - CorrectionType tmp(corr.size()); - tmp = 0; - hessianMatrix_->umv(corr, tmp); - modelDecrease = (rhs*corr) - 0.5 * (corr*tmp); - modelDecrease = grid_->comm().sum(modelDecrease); - - double relativeModelDecrease = modelDecrease / std::fabs(energy); - - if (this->verbosity_ == NumProc::FULL and rank==0) { - std::cout << "Absolute model decrease: " << modelDecrease - << ", functional decrease: " << oldEnergy - energy << std::endl; - std::cout << "Relative model decrease: " << relativeModelDecrease - << ", functional decrease: " << (oldEnergy - energy)/energy << std::endl; - } - assert(modelDecrease >= 0); - - - if (energy >= oldEnergy and rank==0) { - if (this->verbosity_ == NumProc::FULL) - std::cout << "Direction is not a descent direction!" << std::endl; - } - - if (energy >= oldEnergy && - (std::abs((oldEnergy-energy)/energy) < 1e-9 || relativeModelDecrease < 1e-9)) { - if (this->verbosity_ == NumProc::FULL and rank==0) - std::cout << "Suspecting rounding problems" << std::endl; - - if (this->verbosity_ != NumProc::QUIET and rank==0) - std::cout << i+1 << " proximal newton steps were taken." << std::endl; - - x_ = newIterate; - break; - } - } + assert(modelDecrease >= 0); + + + if (energy >= oldEnergy and rank==0) { + if (this->verbosity_ == NumProc::FULL) + std::cout << "Direction is not a descent direction!" << std::endl; } - // ////////////////////////////////////////////// - // Check for acceptance of the step - // ////////////////////////////////////////////// - if (solved && (oldEnergy-energy) / modelDecrease > 0.9) { - // very successful iteration + if (energy >= oldEnergy && + (std::abs((oldEnergy-energy)/energy) < 1e-9 || relativeModelDecrease < 1e-9)) { + if (this->verbosity_ == NumProc::FULL and rank==0) + std::cout << "Suspecting rounding problems" << std::endl; - x_ = newIterate; - regularization *= 0.5; + if (this->verbosity_ != NumProc::QUIET and rank==0) + std::cout << i+1 << " proximal newton steps were taken." << std::endl; - // current energy becomes 'oldEnergy' for the next iteration - oldEnergy = energy; + x_ = newIterate; + break; + } + } + } - recomputeGradientHessian = true; + // ////////////////////////////////////////////// + // Check for acceptance of the step + // ////////////////////////////////////////////// + if (solved && (oldEnergy-energy) / modelDecrease > 0.9) { + // very successful iteration - } else if (solved && ((oldEnergy-energy) / modelDecrease > 0.01 - || std::abs(oldEnergy-energy) < 1e-12)) { - // successful iteration - x_ = newIterate; + x_ = newIterate; + regularization *= 0.5; - // current energy becomes 'oldEnergy' for the next iteration - oldEnergy = energy; + // current energy becomes 'oldEnergy' for the next iteration + oldEnergy = energy; - recomputeGradientHessian = true; + recomputeGradientHessian = true; - } else { + } else if (solved && ((oldEnergy-energy) / modelDecrease > 0.01 + || std::abs(oldEnergy-energy) < 1e-12)) { + // successful iteration + x_ = newIterate; - // unsuccessful iteration + // current energy becomes 'oldEnergy' for the next iteration + oldEnergy = energy; - // Increase the regularization parameter - regularization *= 2; + recomputeGradientHessian = true; - if (this->verbosity_ == NumProc::FULL and rank==0) - std::cout << "Unsuccessful iteration!" << std::endl; - } + } else { - // ///////////////////////////////////////////////////////////////////// - // Write the iterate to disk for later convergence rate measurement - // ///////////////////////////////////////////////////////////////////// + // unsuccessful iteration - if (instrumented_) { + // Increase the regularization parameter + regularization *= 2; - char iFilename[100]; - sprintf(iFilename, "tmp/intermediateSolution_%04ld", i); + if (this->verbosity_ == NumProc::FULL and rank==0) + std::cout << "Unsuccessful iteration!" << std::endl; + } + + // ///////////////////////////////////////////////////////////////////// + // Write the iterate to disk for later convergence rate measurement + // ///////////////////////////////////////////////////////////////////// - FILE* fpIterate = fopen(iFilename, "wb"); - if (!fpIterate) - DUNE_THROW(SolverError, "Couldn't open file " << iFilename << " for writing"); + if (instrumented_) { - for (size_t j=0; j<x_.size(); j++) - fwrite(&x_[j], sizeof(TargetSpace), 1, fpIterate); + char iFilename[100]; + sprintf(iFilename, "tmp/intermediateSolution_%04ld", i); - fclose(fpIterate); + FILE* fpIterate = fopen(iFilename, "wb"); + if (!fpIterate) + DUNE_THROW(SolverError, "Couldn't open file " << iFilename << " for writing"); - } + for (size_t j=0; j<x_.size(); j++) + fwrite(&x_[j], sizeof(TargetSpace), 1, fpIterate); + + fclose(fpIterate); - if (rank==0) - std::cout << "iteration took " << totalTimer.elapsed() << " sec." << std::endl; } - // ////////////////////////////////////////////// - // Close logfile - // ////////////////////////////////////////////// - if (instrumented_) - fclose(fp); + if (rank==0) + std::cout << "iteration took " << totalTimer.elapsed() << " sec." << std::endl; + } + + // ////////////////////////////////////////////// + // Close logfile + // ////////////////////////////////////////////// + if (instrumented_) + fclose(fp); - statistics_.finalEnergy = oldEnergy; + statistics_.finalEnergy = oldEnergy; } diff --git a/dune/gfe/riemannianpnsolver.hh b/dune/gfe/riemannianpnsolver.hh index 67aa29a0..26fd4f50 100644 --- a/dune/gfe/riemannianpnsolver.hh +++ b/dune/gfe/riemannianpnsolver.hh @@ -28,144 +28,144 @@ #include <dune/gfe/parallel/p2mapper.hh> /** \brief Riemannian proximal-newton solver for geodesic finite-element problems */ -template <class Basis, class TargetSpace, class Assembler = GeodesicFEAssembler<Basis,TargetSpace>> +template <class Basis, class TargetSpace, class Assembler = GeodesicFEAssembler<Basis,TargetSpace> > class RiemannianProximalNewtonSolver - : public IterativeSolver<std::vector<TargetSpace>, - Dune::BitSetVector<TargetSpace::TangentVector::dimension> > + : public IterativeSolver<std::vector<TargetSpace>, + Dune::BitSetVector<TargetSpace::TangentVector::dimension> > { - typedef typename Basis::GridView::Grid GridType; + typedef typename Basis::GridView::Grid GridType; - const static int blocksize = TargetSpace::TangentVector::dimension; + const static int blocksize = TargetSpace::TangentVector::dimension; - const static int gridDim = GridType::dimension; + const static int gridDim = GridType::dimension; - // Centralize the field type here - typedef double field_type; + // Centralize the field type here + typedef double field_type; - // Some types that I need - typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize, blocksize> > MatrixType; - typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize> > CorrectionType; - typedef std::vector<TargetSpace> SolutionType; + // Some types that I need + typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize, blocksize> > MatrixType; + typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize> > CorrectionType; + typedef std::vector<TargetSpace> SolutionType; #if HAVE_MPI - typedef typename MapperFactory<Basis>::GlobalMapper GlobalMapper; - typedef typename MapperFactory<Basis>::LocalMapper LocalMapper; + typedef typename MapperFactory<Basis>::GlobalMapper GlobalMapper; + typedef typename MapperFactory<Basis>::LocalMapper LocalMapper; #endif - /** \brief Records information about the last run of the RiemannianProximalNewtonSolver - * - * This is used primarily for unit testing. - */ - struct Statistics - { - std::size_t finalIteration; + /** \brief Records information about the last run of the RiemannianProximalNewtonSolver + * + * This is used primarily for unit testing. + */ + struct Statistics + { + std::size_t finalIteration; - field_type finalEnergy; - }; + field_type finalEnergy; + }; public: - RiemannianProximalNewtonSolver() - : IterativeSolver<std::vector<TargetSpace>, Dune::BitSetVector<blocksize> >(0,100,NumProc::FULL), - hessianMatrix_(nullptr), h1SemiNorm_(NULL) - { - std::fill(scaling_.begin(), scaling_.end(), 1.0); - } - - /** \brief Set up the solver using a choldmod or umfpack solver as the inner solver */ - void setup(const GridType& grid, - const Assembler* assembler, - const SolutionType& x, - const Dune::BitSetVector<blocksize>& dirichletNodes, - double tolerance, - int maxProximalNewtonSteps, - double initialRegularization, - bool instrumented); - - /** \brief Set up the solver using a monotone multigrid method as the inner solver */ - void setup(const GridType& grid, - const Assembler* assembler, - const SolutionType& x, - const Dune::BitSetVector<blocksize>& dirichletNodes, - const Dune::ParameterTree& parameterSet); - - void setScaling(const Dune::FieldVector<double,blocksize>& scaling) - { - scaling_ = scaling; - } - - void setIgnoreNodes(const Dune::BitSetVector<blocksize>& ignoreNodes) - { - ignoreNodes_ = &ignoreNodes; - innerSolver_->ignoreNodes_ = ignoreNodes_; - } - - void solve(); - - [[deprecated]] - void setInitialSolution(const SolutionType& x) { - x_ = x; - } - - void setInitialIterate(const SolutionType& x) { - x_ = x; - } - - SolutionType getSol() const {return x_;} - - const Statistics& getStatistics() const {return statistics_;} + RiemannianProximalNewtonSolver() + : IterativeSolver<std::vector<TargetSpace>, Dune::BitSetVector<blocksize> >(0,100,NumProc::FULL), + hessianMatrix_(nullptr), h1SemiNorm_(NULL) + { + std::fill(scaling_.begin(), scaling_.end(), 1.0); + } + + /** \brief Set up the solver using a choldmod or umfpack solver as the inner solver */ + void setup(const GridType& grid, + const Assembler* assembler, + const SolutionType& x, + const Dune::BitSetVector<blocksize>& dirichletNodes, + double tolerance, + int maxProximalNewtonSteps, + double initialRegularization, + bool instrumented); + + /** \brief Set up the solver using a monotone multigrid method as the inner solver */ + void setup(const GridType& grid, + const Assembler* assembler, + const SolutionType& x, + const Dune::BitSetVector<blocksize>& dirichletNodes, + const Dune::ParameterTree& parameterSet); + + void setScaling(const Dune::FieldVector<double,blocksize>& scaling) + { + scaling_ = scaling; + } + + void setIgnoreNodes(const Dune::BitSetVector<blocksize>& ignoreNodes) + { + ignoreNodes_ = &ignoreNodes; + innerSolver_->ignoreNodes_ = ignoreNodes_; + } + + void solve(); + + [[deprecated]] + void setInitialSolution(const SolutionType& x) { + x_ = x; + } + + void setInitialIterate(const SolutionType& x) { + x_ = x; + } + + SolutionType getSol() const {return x_;} + + const Statistics& getStatistics() const {return statistics_;} protected: #if HAVE_MPI - std::unique_ptr<GlobalMapper> globalMapper_; + std::unique_ptr<GlobalMapper> globalMapper_; #endif - /** \brief The grid */ - const GridType* grid_; + /** \brief The grid */ + const GridType* grid_; - /** \brief The solution vector */ - SolutionType x_; + /** \brief The solution vector */ + SolutionType x_; - /** \brief The initial regularization parameter for the proximal newton step */ - double initialRegularization_; - double tolerance_; + /** \brief The initial regularization parameter for the proximal newton step */ + double initialRegularization_; + double tolerance_; - /** \brief Regularization scaling */ - Dune::FieldVector<double,blocksize> scaling_; + /** \brief Regularization scaling */ + Dune::FieldVector<double,blocksize> scaling_; - /** \brief Maximum number of proximal-newton steps */ - std::size_t maxProximalNewtonSteps_; + /** \brief Maximum number of proximal-newton steps */ + std::size_t maxProximalNewtonSteps_; - /** \brief Hessian matrix */ - std::unique_ptr<MatrixType> hessianMatrix_; + /** \brief Hessian matrix */ + std::unique_ptr<MatrixType> hessianMatrix_; - /** \brief The assembler for the material law */ - const Assembler* assembler_; + /** \brief The assembler for the material law */ + const Assembler* assembler_; - /** \brief The solver for the quadratic inner problems */ + /** \brief The solver for the quadratic inner problems */ #if DUNE_VERSION_GTE(DUNE_SOLVERS, 2, 8) - std::shared_ptr<typename Dune::Solvers::CholmodSolver<MatrixType,CorrectionType>> innerSolver_; + std::shared_ptr<typename Dune::Solvers::CholmodSolver<MatrixType,CorrectionType> > innerSolver_; #else - std::shared_ptr<typename Dune::Solvers::UMFPackSolver<MatrixType,CorrectionType>> innerSolver_; + std::shared_ptr<typename Dune::Solvers::UMFPackSolver<MatrixType,CorrectionType> > innerSolver_; #endif - /** \brief The Dirichlet nodes */ - const Dune::BitSetVector<blocksize>* ignoreNodes_; + /** \brief The Dirichlet nodes */ + const Dune::BitSetVector<blocksize>* ignoreNodes_; - /** \brief The norm used to measure convergence for statistics*/ - std::shared_ptr<H1SemiNorm<CorrectionType> > h1SemiNorm_; + /** \brief The norm used to measure convergence for statistics*/ + std::shared_ptr<H1SemiNorm<CorrectionType> > h1SemiNorm_; - /** \brief An L2-norm, really. The H1SemiNorm class is badly named */ - std::shared_ptr<H1SemiNorm<CorrectionType> > l2Norm_; + /** \brief An L2-norm, really. The H1SemiNorm class is badly named */ + std::shared_ptr<H1SemiNorm<CorrectionType> > l2Norm_; - /** \brief If set to true we log convergence speed and other stuff */ - bool instrumented_; + /** \brief If set to true we log convergence speed and other stuff */ + bool instrumented_; - /** \brief Norm type used for stopping criterion (default is infinity norm) */ - enum class ErrorNormType {infinity, H1semi} normType_ = ErrorNormType::infinity; + /** \brief Norm type used for stopping criterion (default is infinity norm) */ + enum class ErrorNormType {infinity, H1semi} normType_ = ErrorNormType::infinity; - /** \brief Store information about solver runs for unit testing */ - Statistics statistics_; + /** \brief Store information about solver runs for unit testing */ + Statistics statistics_; }; diff --git a/dune/gfe/riemanniantrsolver.cc b/dune/gfe/riemanniantrsolver.cc index 5285d01c..7fc00955 100644 --- a/dune/gfe/riemanniantrsolver.cc +++ b/dune/gfe/riemanniantrsolver.cc @@ -23,304 +23,304 @@ #include <dune/solvers/norms/h1seminorm.hh> // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) #include <dune/gfe/parallel/matrixcommunicator.hh> #include <dune/gfe/parallel/vectorcommunicator.hh> #endif template <class Basis, class TargetSpace, class Assembler> void RiemannianTrustRegionSolver<Basis, TargetSpace, Assembler>:: - setup(const GridType& grid, - const Assembler* assembler, - const SolutionType& x, - const Dune::BitSetVector<blocksize>& dirichletNodes, - const Dune::ParameterTree& parameterSet) +setup(const GridType& grid, + const Assembler* assembler, + const SolutionType& x, + const Dune::BitSetVector<blocksize>& dirichletNodes, + const Dune::ParameterTree& parameterSet) { - if(parameterSet.get("norm", "infinity") == "infinity") - normType_ = ErrorNormType::infinity; - else if(parameterSet.get("norm", "infinity") == "H1-Semi") - normType_ = ErrorNormType::H1semi; - else - DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); - - setup(grid, - assembler, - x, - dirichletNodes, - parameterSet.get<double>("tolerance"), - parameterSet.get<int>("maxTrustRegionSteps"), - parameterSet.get<double>("initialTrustRegionRadius"), - parameterSet.get<int>("numIt"), - parameterSet.get<double>("mgTolerance"), - parameterSet.get<int>("mu"), parameterSet.get<int>("nu1"), parameterSet.get<int>("nu2"), - parameterSet.get<int>("baseIt"), - parameterSet.get<double>("baseTolerance"), - parameterSet.get<bool>("instrumented", 0)); + if(parameterSet.get("norm", "infinity") == "infinity") + normType_ = ErrorNormType::infinity; + else if(parameterSet.get("norm", "infinity") == "H1-Semi") + normType_ = ErrorNormType::H1semi; + else + DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); + + setup(grid, + assembler, + x, + dirichletNodes, + parameterSet.get<double>("tolerance"), + parameterSet.get<int>("maxTrustRegionSteps"), + parameterSet.get<double>("initialTrustRegionRadius"), + parameterSet.get<int>("numIt"), + parameterSet.get<double>("mgTolerance"), + parameterSet.get<int>("mu"), parameterSet.get<int>("nu1"), parameterSet.get<int>("nu2"), + parameterSet.get<int>("baseIt"), + parameterSet.get<double>("baseTolerance"), + parameterSet.get<bool>("instrumented", 0)); } template <class Basis, class TargetSpace, class Assembler> void RiemannianTrustRegionSolver<Basis,TargetSpace,Assembler>:: setup(const GridType& grid, const Assembler* assembler, - const SolutionType& x, - const Dune::BitSetVector<blocksize>& dirichletNodes, - double tolerance, - int maxTrustRegionSteps, - double initialTrustRegionRadius, - int multigridIterations, - double mgTolerance, - int mu, - int nu1, - int nu2, - int baseIterations, - double baseTolerance, - bool instrumented) + const SolutionType& x, + const Dune::BitSetVector<blocksize>& dirichletNodes, + double tolerance, + int maxTrustRegionSteps, + double initialTrustRegionRadius, + int multigridIterations, + double mgTolerance, + int mu, + int nu1, + int nu2, + int baseIterations, + double baseTolerance, + bool instrumented) { - int rank = grid.comm().rank(); - - grid_ = &grid; - assembler_ = assembler; - x_ = x; - this->tolerance_ = tolerance; - maxTrustRegionSteps_ = maxTrustRegionSteps; - initialTrustRegionRadius_ = initialTrustRegionRadius; - innerIterations_ = multigridIterations; - innerTolerance_ = mgTolerance; - instrumented_ = instrumented; - ignoreNodes_ = &dirichletNodes; - - int numLevels = grid_->maxLevel()+1; - -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - ////////////////////////////////////////////////////////////////// - // Create global numbering for matrix and vector transfer - ////////////////////////////////////////////////////////////////// - - globalMapper_ = std::make_unique<GlobalMapper>(grid_->leafGridView()); + int rank = grid.comm().rank(); + + grid_ = &grid; + assembler_ = assembler; + x_ = x; + this->tolerance_ = tolerance; + maxTrustRegionSteps_ = maxTrustRegionSteps; + initialTrustRegionRadius_ = initialTrustRegionRadius; + innerIterations_ = multigridIterations; + innerTolerance_ = mgTolerance; + instrumented_ = instrumented; + ignoreNodes_ = &dirichletNodes; + + int numLevels = grid_->maxLevel()+1; + + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + ////////////////////////////////////////////////////////////////// + // Create global numbering for matrix and vector transfer + ////////////////////////////////////////////////////////////////// + + globalMapper_ = std::make_unique<GlobalMapper>(grid_->leafGridView()); #endif - // //////////////////////////////// - // Create a multigrid solver - // //////////////////////////////// + // //////////////////////////////// + // Create a multigrid solver + // //////////////////////////////// #ifdef HAVE_IPOPT - // First create an IPOpt base solver - QuadraticIPOptSolver<MatrixType,CorrectionType> baseSolver; - baseSolver.setSolverParameter(baseTolerance, 100, NumProc::QUIET); + // First create an IPOpt base solver + QuadraticIPOptSolver<MatrixType,CorrectionType> baseSolver; + baseSolver.setSolverParameter(baseTolerance, 100, NumProc::QUIET); #else - // First create a Gauss-seidel base solver - auto baseSolverStep = std::make_shared<TrustRegionGSStep<MatrixType, CorrectionType>>(); + // First create a Gauss-seidel base solver + auto baseSolverStep = std::make_shared<TrustRegionGSStep<MatrixType, CorrectionType> >(); - // Hack: the two-norm may not scale all that well, but it is fast! - auto baseNorm = std::make_shared<TwoNorm<CorrectionType>>(); + // Hack: the two-norm may not scale all that well, but it is fast! + auto baseNorm = std::make_shared<TwoNorm<CorrectionType> >(); - auto baseSolver = std::make_shared<::LoopSolver<CorrectionType>>(baseSolverStep, - baseIterations, - baseTolerance, - baseNorm, - Solver::QUIET); + auto baseSolver = std::make_shared<::LoopSolver<CorrectionType> >(baseSolverStep, + baseIterations, + baseTolerance, + baseNorm, + Solver::QUIET); #endif -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - // Transfer all Dirichlet data to the master processor - VectorCommunicator<GlobalMapper, typename GridType::LeafGridView::CollectiveCommunication, Dune::BitSetVector<blocksize> > vectorComm(*globalMapper_, - grid_->leafGridView().comm(), - 0); - auto globalDirichletNodes = new Dune::BitSetVector<blocksize>(vectorComm.reduceCopy(dirichletNodes)); + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + // Transfer all Dirichlet data to the master processor + VectorCommunicator<GlobalMapper, typename GridType::LeafGridView::CollectiveCommunication, Dune::BitSetVector<blocksize> > vectorComm(*globalMapper_, + grid_->leafGridView().comm(), + 0); + auto globalDirichletNodes = new Dune::BitSetVector<blocksize>(vectorComm.reduceCopy(dirichletNodes)); #else - auto globalDirichletNodes = new Dune::BitSetVector<blocksize>(dirichletNodes); + auto globalDirichletNodes = new Dune::BitSetVector<blocksize>(dirichletNodes); #endif - // Make smoother (will be used for pre- and postsmoothing - auto smoother = std::make_shared<TrustRegionGSStep<MatrixType, CorrectionType> >(); + // Make smoother (will be used for pre- and postsmoothing + auto smoother = std::make_shared<TrustRegionGSStep<MatrixType, CorrectionType> >(); - auto mmgStep = std::make_shared<MonotoneMGStep<MatrixType, CorrectionType> >(); + auto mmgStep = std::make_shared<MonotoneMGStep<MatrixType, CorrectionType> >(); - mmgStep->setMGType(mu, nu1, nu2); - mmgStep->setIgnore(*globalDirichletNodes); - mmgStep->setBaseSolver(std::move(baseSolver)); - mmgStep->setSmoother(smoother); - mmgStep->setObstacleRestrictor(MandelObstacleRestrictor<CorrectionType>{}); - mmgStep->setVerbosity(Solver::QUIET); + mmgStep->setMGType(mu, nu1, nu2); + mmgStep->setIgnore(*globalDirichletNodes); + mmgStep->setBaseSolver(std::move(baseSolver)); + mmgStep->setSmoother(smoother); + mmgStep->setObstacleRestrictor(MandelObstacleRestrictor<CorrectionType>{}); + mmgStep->setVerbosity(Solver::QUIET); - // ////////////////////////////////////////////////////////////////////////////////////// - // Assemble a Laplace matrix to create a norm that's equivalent to the H1-norm - // ////////////////////////////////////////////////////////////////////////////////////// + // ////////////////////////////////////////////////////////////////////////////////////// + // Assemble a Laplace matrix to create a norm that's equivalent to the H1-norm + // ////////////////////////////////////////////////////////////////////////////////////// - const Basis& basis = assembler_->getBasis(); - Dune::Fufem::DuneFunctionsOperatorAssembler<Basis,Basis> operatorAssembler(basis, basis); + const Basis& basis = assembler_->getBasis(); + Dune::Fufem::DuneFunctionsOperatorAssembler<Basis,Basis> operatorAssembler(basis, basis); - Dune::Fufem::LaplaceAssembler laplaceStiffness; - typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> > ScalarMatrixType; - ScalarMatrixType localA; + Dune::Fufem::LaplaceAssembler laplaceStiffness; + typedef Dune::BCRSMatrix<Dune::FieldMatrix<double,1,1> > ScalarMatrixType; + ScalarMatrixType localA; - operatorAssembler.assembleBulk(Dune::Fufem::istlMatrixBackend(localA), laplaceStiffness); + operatorAssembler.assembleBulk(Dune::Fufem::istlMatrixBackend(localA), laplaceStiffness); -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - LocalMapper localMapper = MapperFactory<Basis>::createLocalMapper(grid_->leafGridView()); + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + LocalMapper localMapper = MapperFactory<Basis>::createLocalMapper(grid_->leafGridView()); - MatrixCommunicator<GlobalMapper, - typename GridType::LeafGridView, - typename GridType::LeafGridView, - ScalarMatrixType, - LocalMapper, - LocalMapper> matrixComm(*globalMapper_, grid_->leafGridView(), localMapper, localMapper, 0); + MatrixCommunicator<GlobalMapper, + typename GridType::LeafGridView, + typename GridType::LeafGridView, + ScalarMatrixType, + LocalMapper, + LocalMapper> matrixComm(*globalMapper_, grid_->leafGridView(), localMapper, localMapper, 0); - auto A = std::make_shared<ScalarMatrixType>(matrixComm.reduceAdd(localA)); + auto A = std::make_shared<ScalarMatrixType>(matrixComm.reduceAdd(localA)); #else - auto A = std::make_shared<ScalarMatrixType>(localA); + auto A = std::make_shared<ScalarMatrixType>(localA); #endif - h1SemiNorm_ = std::make_shared<H1SemiNorm<CorrectionType> >(A); + h1SemiNorm_ = std::make_shared<H1SemiNorm<CorrectionType> >(A); - innerSolver_ = std::make_shared<::LoopSolver<CorrectionType> >(mmgStep, - innerIterations_, - innerTolerance_, - h1SemiNorm_, - Solver::QUIET); + innerSolver_ = std::make_shared<::LoopSolver<CorrectionType> >(mmgStep, + innerIterations_, + innerTolerance_, + h1SemiNorm_, + Solver::QUIET); - // ////////////////////////////////////////////////////////////////////////////////////// - // Assemble a mass matrix to create a norm that's equivalent to the L2-norm - // This will be used to monitor the gradient - // ////////////////////////////////////////////////////////////////////////////////////// + // ////////////////////////////////////////////////////////////////////////////////////// + // Assemble a mass matrix to create a norm that's equivalent to the L2-norm + // This will be used to monitor the gradient + // ////////////////////////////////////////////////////////////////////////////////////// - Dune::Fufem::MassAssembler massStiffness; - ScalarMatrixType localMassMatrix; + Dune::Fufem::MassAssembler massStiffness; + ScalarMatrixType localMassMatrix; - operatorAssembler.assembleBulk(Dune::Fufem::istlMatrixBackend(localMassMatrix), massStiffness); + operatorAssembler.assembleBulk(Dune::Fufem::istlMatrixBackend(localMassMatrix), massStiffness); -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - auto massMatrix = std::make_shared<ScalarMatrixType>(matrixComm.reduceAdd(localMassMatrix)); + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + auto massMatrix = std::make_shared<ScalarMatrixType>(matrixComm.reduceAdd(localMassMatrix)); #else - auto massMatrix = std::make_shared<ScalarMatrixType>(localMassMatrix); + auto massMatrix = std::make_shared<ScalarMatrixType>(localMassMatrix); #endif - l2Norm_ = std::make_shared<H1SemiNorm<CorrectionType> >(massMatrix); - - // Write all intermediate solutions, if requested - if (instrumented_ - && dynamic_cast<IterativeSolver<CorrectionType>*>(innerSolver_.get())) - dynamic_cast<IterativeSolver<CorrectionType>*>(innerSolver_.get())->historyBuffer_ = "tmp/mgHistory"; - - // //////////////////////////////////////////////////////////// - // Create Hessian matrix and its occupation structure - // //////////////////////////////////////////////////////////// - - hessianMatrix_ = std::make_unique<MatrixType>(); - Dune::MatrixIndexSet indices(grid_->size(1), grid_->size(1)); - assembler_->getNeighborsPerVertex(indices); - indices.exportIdx(*hessianMatrix_); - - // //////////////////////////////////// - // Create the transfer operators - // //////////////////////////////////// - - //////////////////////////////////////////////////////////////////////// - // The P1 space (actually P1/Q1, depending on the grid) is special: - // If we work in such a space, then the multigrid hierarchy of spaces - // is constructed in the usual way. For all other space, there is - // an additional restriction operator on the top of the hierarchy, which - // restricts the FE space to the P1/Q1 space on the same grid. - // On the lower grid levels a hierarchy of P1/Q1 spaces is used again. - //////////////////////////////////////////////////////////////////////// - - bool isP1Basis = std::is_same<Basis,Dune::Functions::LagrangeBasis<typename Basis::GridView,1> >::value; - - if (isP1Basis) - mmgStep->mgTransfer_.resize(numLevels-1); - else - mmgStep->mgTransfer_.resize(numLevels); - - // Here we set up the restriction of the leaf grid space into the leaf grid P1/Q1 space - if (not isP1Basis) - { - typedef typename TruncatedCompressedMGTransfer<CorrectionType>::TransferOperatorType TransferOperatorType; - Dune::Functions::LagrangeBasis<typename GridType::LeafGridView,1> p1Basis(grid_->leafGridView()); - - TransferOperatorType pkToP1TransferMatrix; - assembleGlobalBasisTransferMatrix(pkToP1TransferMatrix,p1Basis,basis); + l2Norm_ = std::make_shared<H1SemiNorm<CorrectionType> >(massMatrix); + + // Write all intermediate solutions, if requested + if (instrumented_ + && dynamic_cast<IterativeSolver<CorrectionType>*>(innerSolver_.get())) + dynamic_cast<IterativeSolver<CorrectionType>*>(innerSolver_.get())->historyBuffer_ = "tmp/mgHistory"; + + // //////////////////////////////////////////////////////////// + // Create Hessian matrix and its occupation structure + // //////////////////////////////////////////////////////////// + + hessianMatrix_ = std::make_unique<MatrixType>(); + Dune::MatrixIndexSet indices(grid_->size(1), grid_->size(1)); + assembler_->getNeighborsPerVertex(indices); + indices.exportIdx(*hessianMatrix_); + + // //////////////////////////////////// + // Create the transfer operators + // //////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // The P1 space (actually P1/Q1, depending on the grid) is special: + // If we work in such a space, then the multigrid hierarchy of spaces + // is constructed in the usual way. For all other space, there is + // an additional restriction operator on the top of the hierarchy, which + // restricts the FE space to the P1/Q1 space on the same grid. + // On the lower grid levels a hierarchy of P1/Q1 spaces is used again. + //////////////////////////////////////////////////////////////////////// + + bool isP1Basis = std::is_same<Basis,Dune::Functions::LagrangeBasis<typename Basis::GridView,1> >::value; + + if (isP1Basis) + mmgStep->mgTransfer_.resize(numLevels-1); + else + mmgStep->mgTransfer_.resize(numLevels); + + // Here we set up the restriction of the leaf grid space into the leaf grid P1/Q1 space + if (not isP1Basis) + { + typedef typename TruncatedCompressedMGTransfer<CorrectionType>::TransferOperatorType TransferOperatorType; + Dune::Functions::LagrangeBasis<typename GridType::LeafGridView,1> p1Basis(grid_->leafGridView()); + + TransferOperatorType pkToP1TransferMatrix; + assembleGlobalBasisTransferMatrix(pkToP1TransferMatrix,p1Basis,basis); + + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + // If we are on more than 1 processors, join all local transfer matrices on rank 0, + // and construct a single global transfer operator there. + typedef Dune::GlobalP1Mapper<Dune::Functions::LagrangeBasis<typename Basis::GridView,1> > GlobalLeafP1Mapper; + GlobalLeafP1Mapper p1Index(grid_->leafGridView()); + + typedef Dune::MultipleCodimMultipleGeomTypeMapper<typename GridType::LeafGridView> LeafP1LocalMapper; + LeafP1LocalMapper leafP1LocalMapper(grid_->leafGridView(), Dune::mcmgVertexLayout()); -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - // If we are on more than 1 processors, join all local transfer matrices on rank 0, - // and construct a single global transfer operator there. - typedef Dune::GlobalP1Mapper<Dune::Functions::LagrangeBasis<typename Basis::GridView,1>> GlobalLeafP1Mapper; - GlobalLeafP1Mapper p1Index(grid_->leafGridView()); - - typedef Dune::MultipleCodimMultipleGeomTypeMapper<typename GridType::LeafGridView> LeafP1LocalMapper; - LeafP1LocalMapper leafP1LocalMapper(grid_->leafGridView(), Dune::mcmgVertexLayout()); - - MatrixCommunicator<GlobalMapper, - typename GridType::LeafGridView, - typename GridType::LeafGridView, - TransferOperatorType, - LocalMapper, - LeafP1LocalMapper, - GlobalLeafP1Mapper> matrixComm(*globalMapper_, p1Index, grid_->leafGridView(), grid_->leafGridView(), localMapper, leafP1LocalMapper, 0); - - mmgStep->mgTransfer_.back() = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType> >(); - std::shared_ptr<TransferOperatorType> topTransferOperator = std::make_shared<TransferOperatorType>(matrixComm.reduceCopy(pkToP1TransferMatrix)); + MatrixCommunicator<GlobalMapper, + typename GridType::LeafGridView, + typename GridType::LeafGridView, + TransferOperatorType, + LocalMapper, + LeafP1LocalMapper, + GlobalLeafP1Mapper> matrixComm(*globalMapper_, p1Index, grid_->leafGridView(), grid_->leafGridView(), localMapper, leafP1LocalMapper, 0); + + mmgStep->mgTransfer_.back() = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType> >(); + std::shared_ptr<TransferOperatorType> topTransferOperator = std::make_shared<TransferOperatorType>(matrixComm.reduceCopy(pkToP1TransferMatrix)); #else - mmgStep->mgTransfer_.back() = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType> >(); - std::shared_ptr<TransferOperatorType> topTransferOperator = std::make_shared<TransferOperatorType>(pkToP1TransferMatrix); + mmgStep->mgTransfer_.back() = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType> >(); + std::shared_ptr<TransferOperatorType> topTransferOperator = std::make_shared<TransferOperatorType>(pkToP1TransferMatrix); #endif - std::dynamic_pointer_cast<TruncatedCompressedMGTransfer<CorrectionType> >(mmgStep->mgTransfer_.back())->setMatrix(topTransferOperator); - } - - // Now the P1/Q1 restriction operators for the remaining levels - for (int i=0; i<numLevels-1; i++) { - - // Construct the local multigrid transfer matrix - auto newTransferOp = std::make_unique<TruncatedCompressedMGTransfer<CorrectionType>>(); - newTransferOp->setup(*grid_,i,i+1); -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - // If we are on more than 1 processors, join all local transfer matrices on rank 0, - // and construct a single global transfer operator there. - typedef Dune::Functions::LagrangeBasis<typename GridType::LevelGridView, 1> FEBasis; - typedef Dune::GlobalP1Mapper<FEBasis> GlobalLevelP1Mapper; - GlobalLevelP1Mapper fineGUIndex(grid_->levelGridView(i+1)); - GlobalLevelP1Mapper coarseGUIndex(grid_->levelGridView(i)); - - typedef Dune::MultipleCodimMultipleGeomTypeMapper<typename GridType::LevelGridView> LevelLocalMapper; - LevelLocalMapper fineLevelLocalMapper(grid_->levelGridView(i+1), Dune::mcmgVertexLayout()); - LevelLocalMapper coarseLevelLocalMapper(grid_->levelGridView(i), Dune::mcmgVertexLayout()); + std::dynamic_pointer_cast<TruncatedCompressedMGTransfer<CorrectionType> >(mmgStep->mgTransfer_.back())->setMatrix(topTransferOperator); + } + + // Now the P1/Q1 restriction operators for the remaining levels + for (int i=0; i<numLevels-1; i++) { + + // Construct the local multigrid transfer matrix + auto newTransferOp = std::make_unique<TruncatedCompressedMGTransfer<CorrectionType> >(); + newTransferOp->setup(*grid_,i,i+1); + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + // If we are on more than 1 processors, join all local transfer matrices on rank 0, + // and construct a single global transfer operator there. + typedef Dune::Functions::LagrangeBasis<typename GridType::LevelGridView, 1> FEBasis; + typedef Dune::GlobalP1Mapper<FEBasis> GlobalLevelP1Mapper; + GlobalLevelP1Mapper fineGUIndex(grid_->levelGridView(i+1)); + GlobalLevelP1Mapper coarseGUIndex(grid_->levelGridView(i)); + + typedef Dune::MultipleCodimMultipleGeomTypeMapper<typename GridType::LevelGridView> LevelLocalMapper; + LevelLocalMapper fineLevelLocalMapper(grid_->levelGridView(i+1), Dune::mcmgVertexLayout()); + LevelLocalMapper coarseLevelLocalMapper(grid_->levelGridView(i), Dune::mcmgVertexLayout()); #endif - typedef typename TruncatedCompressedMGTransfer<CorrectionType>::TransferOperatorType TransferOperatorType; -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - MatrixCommunicator<GlobalLevelP1Mapper, - typename GridType::LevelGridView, - typename GridType::LevelGridView, - TransferOperatorType, - LevelLocalMapper, - LevelLocalMapper> matrixComm(fineGUIndex, coarseGUIndex, grid_->levelGridView(i+1), grid_->levelGridView(i), fineLevelLocalMapper, coarseLevelLocalMapper, 0); - - mmgStep->mgTransfer_[i] = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType> >(); - std::shared_ptr<TransferOperatorType> transferOperatorMatrix = std::make_shared<TransferOperatorType>(matrixComm.reduceCopy(newTransferOp->getMatrix())); + typedef typename TruncatedCompressedMGTransfer<CorrectionType>::TransferOperatorType TransferOperatorType; + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + MatrixCommunicator<GlobalLevelP1Mapper, + typename GridType::LevelGridView, + typename GridType::LevelGridView, + TransferOperatorType, + LevelLocalMapper, + LevelLocalMapper> matrixComm(fineGUIndex, coarseGUIndex, grid_->levelGridView(i+1), grid_->levelGridView(i), fineLevelLocalMapper, coarseLevelLocalMapper, 0); + + mmgStep->mgTransfer_[i] = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType> >(); + std::shared_ptr<TransferOperatorType> transferOperatorMatrix = std::make_shared<TransferOperatorType>(matrixComm.reduceCopy(newTransferOp->getMatrix())); #else - mmgStep->mgTransfer_[i] = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType> >(); - std::shared_ptr<TransferOperatorType> transferOperatorMatrix = std::make_shared<TransferOperatorType>(newTransferOp->getMatrix()); + mmgStep->mgTransfer_[i] = std::make_shared<TruncatedCompressedMGTransfer<CorrectionType> >(); + std::shared_ptr<TransferOperatorType> transferOperatorMatrix = std::make_shared<TransferOperatorType>(newTransferOp->getMatrix()); #endif - std::dynamic_pointer_cast<TruncatedCompressedMGTransfer<CorrectionType> >(mmgStep->mgTransfer_[i])->setMatrix(transferOperatorMatrix); - } - - // ////////////////////////////////////////////////////////// - // Create obstacles - // ////////////////////////////////////////////////////////// - - if (rank==0) - { -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - hasObstacle_.resize(globalMapper_->size(), true); + std::dynamic_pointer_cast<TruncatedCompressedMGTransfer<CorrectionType> >(mmgStep->mgTransfer_[i])->setMatrix(transferOperatorMatrix); + } + + // ////////////////////////////////////////////////////////// + // Create obstacles + // ////////////////////////////////////////////////////////// + + if (rank==0) + { + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + hasObstacle_.resize(globalMapper_->size(), true); #else - hasObstacle_.resize(basis.size(), true); + hasObstacle_.resize(basis.size(), true); #endif - mmgStep->setHasObstacles(hasObstacle_); - } + mmgStep->setHasObstacles(hasObstacle_); + } } @@ -328,424 +328,424 @@ setup(const GridType& grid, template <class Basis, class TargetSpace, class Assembler> void RiemannianTrustRegionSolver<Basis,TargetSpace,Assembler>::solve() { - int rank = grid_->comm().rank(); + int rank = grid_->comm().rank(); - MonotoneMGStep<MatrixType,CorrectionType>* mgStep = nullptr; // Non-shared pointer -- the innerSolver keeps the ownership + MonotoneMGStep<MatrixType,CorrectionType>* mgStep = nullptr; // Non-shared pointer -- the innerSolver keeps the ownership - // if the inner solver is a monotone multigrid set up a max-norm trust-region - if (dynamic_cast<LoopSolver<CorrectionType>*>(innerSolver_.get())) { - auto loopSolver = std::dynamic_pointer_cast<LoopSolver<CorrectionType> >(innerSolver_); - mgStep = dynamic_cast<MonotoneMGStep<MatrixType,CorrectionType>*>(&loopSolver->getIterationStep()); - } + // if the inner solver is a monotone multigrid set up a max-norm trust-region + if (dynamic_cast<LoopSolver<CorrectionType>*>(innerSolver_.get())) { + auto loopSolver = std::dynamic_pointer_cast<LoopSolver<CorrectionType> >(innerSolver_); + mgStep = dynamic_cast<MonotoneMGStep<MatrixType,CorrectionType>*>(&loopSolver->getIterationStep()); + } -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - MaxNormTrustRegion<blocksize> trustRegion(globalMapper_->size(), initialTrustRegionRadius_); + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + MaxNormTrustRegion<blocksize> trustRegion(globalMapper_->size(), initialTrustRegionRadius_); #else - const Basis& basis = assembler_->getBasis(); - MaxNormTrustRegion<blocksize> trustRegion(basis.size(), initialTrustRegionRadius_); + const Basis& basis = assembler_->getBasis(); + MaxNormTrustRegion<blocksize> trustRegion(basis.size(), initialTrustRegionRadius_); #endif - trustRegion.set(initialTrustRegionRadius_, scaling_); - auto smallestScalingParameter = *std::min_element(std::begin(scaling_), std::end(scaling_)); - - std::vector<BoxConstraint<field_type,blocksize> > trustRegionObstacles; - - // ///////////////////////////////////////////////////// - // Set up the log file, if requested - // ///////////////////////////////////////////////////// - FILE* fp = nullptr; - if (instrumented_) { - - fp = fopen("statistics", "w"); - if (!fp) - DUNE_THROW(Dune::IOError, "Couldn't open statistics file for writing!"); + trustRegion.set(initialTrustRegionRadius_, scaling_); + auto smallestScalingParameter = *std::min_element(std::begin(scaling_), std::end(scaling_)); + + std::vector<BoxConstraint<field_type,blocksize> > trustRegionObstacles; + + // ///////////////////////////////////////////////////// + // Set up the log file, if requested + // ///////////////////////////////////////////////////// + FILE* fp = nullptr; + if (instrumented_) { + + fp = fopen("statistics", "w"); + if (!fp) + DUNE_THROW(Dune::IOError, "Couldn't open statistics file for writing!"); + + } + + // ///////////////////////////////////////////////////// + // Trust-Region Solver + // ///////////////////////////////////////////////////// + + Dune::Timer energyTimer; + double oldEnergy = assembler_->computeEnergy(x_); + if (this->verbosity_ == Solver::FULL) + std::cout << "Energy computation took " << energyTimer.elapsed() << " sec." << ", final energy: " << oldEnergy << std::endl; + + + oldEnergy = grid_->comm().sum(oldEnergy); + + bool recomputeGradientHessian = true; + CorrectionType rhs; + MatrixType stiffnessMatrix; + CorrectionType rhs_global; + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + VectorCommunicator<GlobalMapper, typename GridType::LeafGridView::CollectiveCommunication, CorrectionType> vectorComm(*globalMapper_, + grid_->leafGridView().comm(), + 0); + LocalMapper localMapper = MapperFactory<Basis>::createLocalMapper(grid_->leafGridView()); + MatrixCommunicator<GlobalMapper, + typename GridType::LeafGridView, + typename GridType::LeafGridView, + MatrixType, + LocalMapper, + LocalMapper> matrixComm(*globalMapper_, + grid_->leafGridView(), + localMapper, + localMapper, + 0); +#endif + auto& i = statistics_.finalIteration; + double totalAssemblyTime = 0.0; + double totalSolverTime = 0.0; + for (i=0; i<maxTrustRegionSteps_; i++) { + /* std::cout << "current iterate:\n"; + for (size_t j=0; j<x_.size(); j++) + std::cout << x_[j] << std::endl;*/ + + Dune::Timer totalTimer; + if (this->verbosity_ == Solver::FULL and rank==0) { + std::cout << "----------------------------------------------------" << std::endl; + std::cout << " Trust-Region Step Number: " << i + << ", radius: " << trustRegion.radius() + << ", energy: " << oldEnergy << std::endl; + std::cout << "----------------------------------------------------" << std::endl; } - // ///////////////////////////////////////////////////// - // Trust-Region Solver - // ///////////////////////////////////////////////////// - - Dune::Timer energyTimer; - double oldEnergy = assembler_->computeEnergy(x_); - if (this->verbosity_ == Solver::FULL) - std::cout << "Energy computation took " << energyTimer.elapsed() << " sec." << ", final energy: " << oldEnergy << std::endl; - - - oldEnergy = grid_->comm().sum(oldEnergy); - - bool recomputeGradientHessian = true; - CorrectionType rhs; - MatrixType stiffnessMatrix; - CorrectionType rhs_global; -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - VectorCommunicator<GlobalMapper, typename GridType::LeafGridView::CollectiveCommunication, CorrectionType> vectorComm(*globalMapper_, - grid_->leafGridView().comm(), - 0); - LocalMapper localMapper = MapperFactory<Basis>::createLocalMapper(grid_->leafGridView()); - MatrixCommunicator<GlobalMapper, - typename GridType::LeafGridView, - typename GridType::LeafGridView, - MatrixType, - LocalMapper, - LocalMapper> matrixComm(*globalMapper_, - grid_->leafGridView(), - localMapper, - localMapper, - 0); -#endif - auto& i = statistics_.finalIteration; - double totalAssemblyTime = 0.0; - double totalSolverTime = 0.0; - for (i=0; i<maxTrustRegionSteps_; i++) { - -/* std::cout << "current iterate:\n"; - for (size_t j=0; j<x_.size(); j++) - std::cout << x_[j] << std::endl;*/ - - Dune::Timer totalTimer; - if (this->verbosity_ == Solver::FULL and rank==0) { - std::cout << "----------------------------------------------------" << std::endl; - std::cout << " Trust-Region Step Number: " << i - << ", radius: " << trustRegion.radius() - << ", energy: " << oldEnergy << std::endl; - std::cout << "----------------------------------------------------" << std::endl; - } - - CorrectionType corr(x_.size()); - corr = 0; + CorrectionType corr(x_.size()); + corr = 0; - Dune::Timer gradientTimer; + Dune::Timer gradientTimer; - if (recomputeGradientHessian) { + if (recomputeGradientHessian) { - assembler_->assembleGradientAndHessian(x_, - rhs, - *hessianMatrix_, - i==0 // assemble occupation pattern only for the first call - ); - std::cout << "Assembly took " << gradientTimer.elapsed() << " sec." << std::endl; + assembler_->assembleGradientAndHessian(x_, + rhs, + *hessianMatrix_, + i==0 // assemble occupation pattern only for the first call + ); + std::cout << "Assembly took " << gradientTimer.elapsed() << " sec." << std::endl; - rhs *= -1; // The right hand side is the _negative_ gradient + rhs *= -1; // The right hand side is the _negative_ gradient - // Transfer vector data -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - rhs_global = vectorComm.reduceAdd(rhs); + // Transfer vector data + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + rhs_global = vectorComm.reduceAdd(rhs); #else - rhs_global = rhs; + rhs_global = rhs; #endif - CorrectionType gradient = rhs_global; - for (size_t j=0; j<gradient.size(); j++) - for (size_t k=0; k<gradient[j].size(); k++) - if ((*mgStep->ignoreNodes_)[j][k]) // global Dirichlet nodes set - gradient[j][k] = 0; - - if (this->verbosity_ == Solver::FULL and rank==0) { - std::cout << "Gradient operator norm: " << l2Norm_->operator()(gradient) << std::endl; - std::cout << "Gradient norm: " << gradient.two_norm() << std::endl; - } - if (this->verbosity_ == Solver::FULL and rank==0) - std::cout << "Overall assembly took " << gradientTimer.elapsed() << " sec." << std::endl; - totalAssemblyTime += gradientTimer.elapsed(); - -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - stiffnessMatrix = matrixComm.reduceAdd(*hessianMatrix_); + CorrectionType gradient = rhs_global; + for (size_t j=0; j<gradient.size(); j++) + for (size_t k=0; k<gradient[j].size(); k++) + if ((*mgStep->ignoreNodes_)[j][k]) // global Dirichlet nodes set + gradient[j][k] = 0; + + if (this->verbosity_ == Solver::FULL and rank==0) { + std::cout << "Gradient operator norm: " << l2Norm_->operator()(gradient) << std::endl; + std::cout << "Gradient norm: " << gradient.two_norm() << std::endl; + } + if (this->verbosity_ == Solver::FULL and rank==0) + std::cout << "Overall assembly took " << gradientTimer.elapsed() << " sec." << std::endl; + totalAssemblyTime += gradientTimer.elapsed(); + + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + stiffnessMatrix = matrixComm.reduceAdd(*hessianMatrix_); #else - stiffnessMatrix = *hessianMatrix_; + stiffnessMatrix = *hessianMatrix_; #endif - recomputeGradientHessian = false; - - } + recomputeGradientHessian = false; - CorrectionType corr_global(rhs_global.size()); - corr_global = 0; - bool solved = true; + } - if (rank==0) - { - mgStep->setProblem(stiffnessMatrix, corr_global, rhs_global); + CorrectionType corr_global(rhs_global.size()); + corr_global = 0; + bool solved = true; - trustRegionObstacles = trustRegion.obstacles(); - mgStep->setObstacles(trustRegionObstacles); + if (rank==0) + { + mgStep->setProblem(stiffnessMatrix, corr_global, rhs_global); - innerSolver_->preprocess(); + trustRegionObstacles = trustRegion.obstacles(); + mgStep->setObstacles(trustRegionObstacles); - /////////////////////////////// - // Solve ! - /////////////////////////////// + innerSolver_->preprocess(); - std::cout << "Solve quadratic problem..." << std::endl; + /////////////////////////////// + // Solve ! + /////////////////////////////// - Dune::Timer solutionTimer; - try { - innerSolver_->solve(); - } catch (Dune::Exception &e) { - std::cerr << "Error while solving: " << e << std::endl; - solved = false; - } - std::cout << "Solving the quadratic problem took " << solutionTimer.elapsed() << " seconds." << std::endl; - totalSolverTime += solutionTimer.elapsed(); + std::cout << "Solve quadratic problem..." << std::endl; - if (mgStep && solved) { - corr_global = mgStep->getSol(); - std::cout << "Two norm of the correction: " << corr_global.two_norm() << std::endl; - } - } + Dune::Timer solutionTimer; + try { + innerSolver_->solve(); + } catch (Dune::Exception &e) { + std::cerr << "Error while solving: " << e << std::endl; + solved = false; + } + std::cout << "Solving the quadratic problem took " << solutionTimer.elapsed() << " seconds." << std::endl; + totalSolverTime += solutionTimer.elapsed(); - // Distribute solution - if (grid_->comm().size()>1 and rank==0) - std::cout << "Transfer solution back to root process ..." << std::endl; + if (mgStep && solved) { + corr_global = mgStep->getSol(); + std::cout << "Two norm of the correction: " << corr_global.two_norm() << std::endl; + } + } -// The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 -#if HAVE_MPI && (!defined(GRID_DIM) or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM) or (defined(WORLD_DIM) && WORLD_DIM < 3)) - solved = grid_->comm().min(solved); - if (solved) { - corr = vectorComm.scatter(corr_global); - } else { - corr_global = 0; - corr = 0; - } + // Distribute solution + if (grid_->comm().size()>1 and rank==0) + std::cout << "Transfer solution back to root process ..." << std::endl; + + // The VectorCommunicator and MatrixCommunicator work only for GRID_DIM == WORLD_DIM == 2 or GRID_DIM == WORLD_DIM == 3 +#if HAVE_MPI && (!defined(GRID_DIM)or (defined(GRID_DIM) && GRID_DIM < 3)) && (!defined(WORLD_DIM)or (defined(WORLD_DIM) && WORLD_DIM < 3)) + solved = grid_->comm().min(solved); + if (solved) { + corr = vectorComm.scatter(corr_global); + } else { + corr_global = 0; + corr = 0; + } #else - corr = corr_global; + corr = corr_global; #endif - double corrNorm; - if (normType_ == ErrorNormType::infinity) - corrNorm = corr.infinity_norm(); - else if (normType_ == ErrorNormType::H1semi) - corrNorm = h1SemiNorm_->operator()(corr); - else - DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); + double corrNorm; + if (normType_ == ErrorNormType::infinity) + corrNorm = corr.infinity_norm(); + else if (normType_ == ErrorNormType::H1semi) + corrNorm = h1SemiNorm_->operator()(corr); + else + DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); + + // Make norm of corr_global known on all processors + double corrGlobalNorm = grid_->comm().max(corrNorm); - // Make norm of corr_global known on all processors - double corrGlobalNorm = grid_->comm().max(corrNorm); + if (instrumented_) { - if (instrumented_) { + fprintf(fp, "Trust-region step: %ld, trust-region radius: %g\n", + i, trustRegion.radius()); - fprintf(fp, "Trust-region step: %ld, trust-region radius: %g\n", - i, trustRegion.radius()); + // /////////////////////////////////////////////////////////////// + // Compute and measure progress against the exact solution + // for each trust region step + // /////////////////////////////////////////////////////////////// - // /////////////////////////////////////////////////////////////// - // Compute and measure progress against the exact solution - // for each trust region step - // /////////////////////////////////////////////////////////////// + CorrectionType exactSolution = corr; - CorrectionType exactSolution = corr; + // Start from 0 + double oldError = 0; + double totalConvRate = 1; + double convRate = 1; - // Start from 0 - double oldError = 0; - double totalConvRate = 1; - double convRate = 1; + // Write statistics of the initial solution + // Compute the energy norm + oldError = h1SemiNorm_->operator()(exactSolution); - // Write statistics of the initial solution - // Compute the energy norm - oldError = h1SemiNorm_->operator()(exactSolution); + for (int j=0; j<innerIterations_; j++) { - for (int j=0; j<innerIterations_; j++) { + // read iteration from file + CorrectionType intermediateSol(grid_->size(gridDim)); + intermediateSol = 0; + char iSolFilename[100]; + sprintf(iSolFilename, "tmp/mgHistory/intermediatesolution_%04d", j); - // read iteration from file - CorrectionType intermediateSol(grid_->size(gridDim)); - intermediateSol = 0; - char iSolFilename[100]; - sprintf(iSolFilename, "tmp/mgHistory/intermediatesolution_%04d", j); + FILE* fpInt = fopen(iSolFilename, "rb"); + if (!fpInt) + DUNE_THROW(Dune::IOError, "Couldn't open intermediate solution"); + for (size_t k=0; k<intermediateSol.size(); k++) + for (int l=0; l<blocksize; l++) + fread(&intermediateSol[k][l], sizeof(double), 1, fpInt); - FILE* fpInt = fopen(iSolFilename, "rb"); - if (!fpInt) - DUNE_THROW(Dune::IOError, "Couldn't open intermediate solution"); - for (size_t k=0; k<intermediateSol.size(); k++) - for (int l=0; l<blocksize; l++) - fread(&intermediateSol[k][l], sizeof(double), 1, fpInt); + fclose(fpInt); + //std::cout << "intermediateSol\n" << intermediateSol << std::endl; - fclose(fpInt); - //std::cout << "intermediateSol\n" << intermediateSol << std::endl; + // Compute errors + intermediateSol -= exactSolution; - // Compute errors - intermediateSol -= exactSolution; + //std::cout << "error\n" << intermediateSol << std::endl; - //std::cout << "error\n" << intermediateSol << std::endl; + // Compute the H1 norm + double error = h1SemiNorm_->operator()(intermediateSol); - // Compute the H1 norm - double error = h1SemiNorm_->operator()(intermediateSol); + convRate = error / oldError; + totalConvRate *= convRate; - convRate = error / oldError; - totalConvRate *= convRate; + if (error < 1e-12) + break; - if (error < 1e-12) - break; + std::cout << "Iteration: " << j << " "; + std::cout << "Errors: error " << error << ", convergence rate: " << convRate + << ", total conv rate " << pow(totalConvRate, 1/((double)j+1)) << std::endl; - std::cout << "Iteration: " << j << " "; - std::cout << "Errors: error " << error << ", convergence rate: " << convRate - << ", total conv rate " << pow(totalConvRate, 1/((double)j+1)) << std::endl; + fprintf(fp, "%d %g %g %g\n", j+1, error, convRate, pow(totalConvRate, 1/((double)j+1))); - fprintf(fp, "%d %g %g %g\n", j+1, error, convRate, pow(totalConvRate, 1/((double)j+1))); + oldError = error; - oldError = error; + } - } + } + double energy = 0; + double modelDecrease = 0; + SolutionType newIterate = x_; + if (i == maxTrustRegionSteps_ - 1) + std::cout << i+1 << " trust-region steps were taken, the maximum was reached." << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; + + if (solved) { + if (this->verbosity_ == NumProc::FULL and rank==0) + if (normType_ == ErrorNormType::infinity) + std::cout << "infinity norm of the correction: " << corrGlobalNorm << std::endl; + else if (normType_ == ErrorNormType::H1semi) + std::cout << "H1-semi norm of the correction: " << corrGlobalNorm << std::endl; + else + DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); + + if (corrGlobalNorm < this->tolerance_ && corrGlobalNorm < trustRegion.radius()*smallestScalingParameter) { + if (this->verbosity_ == NumProc::FULL and rank==0) + std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; + + if (this->verbosity_ != NumProc::QUIET and rank==0) + std::cout << i+1 << " trust-region steps were taken" << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; + break; + } + + // //////////////////////////////////////////////////// + // Check whether trust-region step can be accepted + // //////////////////////////////////////////////////// + + for (size_t j=0; j<newIterate.size(); j++) + newIterate[j] = TargetSpace::exp(newIterate[j], corr[j]); + try { + energy = assembler_->computeEnergy(newIterate); + } catch (Dune::Exception &e) { + std::cerr << "Error while computing the energy of the new iterate: " << e << std::endl; + std::cerr << "Redoing trust region step with smaller radius..." << std::endl; + solved = false; + } + solved = grid_->comm().min(solved); + + if (!solved) { + newIterate = x_; + energy = oldEnergy; + } else { + energy = grid_->comm().sum(energy); + + // compute the model decrease + // It is $ m(x) - m(x+s) = -<g,s> - 0.5 <s, Hs> + // Note that rhs = -g + CorrectionType tmp(corr.size()); + tmp = 0; + hessianMatrix_->umv(corr, tmp); + modelDecrease = (rhs*corr) - 0.5 * (corr*tmp); + modelDecrease = grid_->comm().sum(modelDecrease); + + double relativeModelDecrease = modelDecrease / std::fabs(energy); + + if (this->verbosity_ == NumProc::FULL and rank==0) { + std::cout << "Absolute model decrease: " << modelDecrease + << ", functional decrease: " << oldEnergy - energy << std::endl; + std::cout << "Relative model decrease: " << relativeModelDecrease + << ", functional decrease: " << (oldEnergy - energy)/energy << std::endl; + } + assert(modelDecrease >= 0); + + if (energy >= oldEnergy and rank==0) { + if (this->verbosity_ == NumProc::FULL) + std::cout << "Direction is not a descent direction!" << std::endl; } - double energy = 0; - double modelDecrease = 0; - SolutionType newIterate = x_; - if (i == maxTrustRegionSteps_ - 1) - std::cout << i+1 << " trust-region steps were taken, the maximum was reached." << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; - - if (solved) { - if (this->verbosity_ == NumProc::FULL and rank==0) - if (normType_ == ErrorNormType::infinity) - std::cout << "infinity norm of the correction: " << corrGlobalNorm << std::endl; - else if (normType_ == ErrorNormType::H1semi) - std::cout << "H1-semi norm of the correction: " << corrGlobalNorm << std::endl; - else - DUNE_THROW(Dune::Exception, "Unknown norm type for stopping criterion!"); - - if (corrGlobalNorm < this->tolerance_ && corrGlobalNorm < trustRegion.radius()*smallestScalingParameter) { - if (this->verbosity_ == NumProc::FULL and rank==0) - std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; - - if (this->verbosity_ != NumProc::QUIET and rank==0) - std::cout << i+1 << " trust-region steps were taken" << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; - break; - } - - // //////////////////////////////////////////////////// - // Check whether trust-region step can be accepted - // //////////////////////////////////////////////////// - - for (size_t j=0; j<newIterate.size(); j++) - newIterate[j] = TargetSpace::exp(newIterate[j], corr[j]); - try { - energy = assembler_->computeEnergy(newIterate); - } catch (Dune::Exception &e) { - std::cerr << "Error while computing the energy of the new iterate: " << e << std::endl; - std::cerr << "Redoing trust region step with smaller radius..." << std::endl; - solved = false; - } - solved = grid_->comm().min(solved); - - if (!solved) { - newIterate = x_; - energy = oldEnergy; - } else { - energy = grid_->comm().sum(energy); - - // compute the model decrease - // It is $ m(x) - m(x+s) = -<g,s> - 0.5 <s, Hs> - // Note that rhs = -g - CorrectionType tmp(corr.size()); - tmp = 0; - hessianMatrix_->umv(corr, tmp); - modelDecrease = (rhs*corr) - 0.5 * (corr*tmp); - modelDecrease = grid_->comm().sum(modelDecrease); - - double relativeModelDecrease = modelDecrease / std::fabs(energy); - - if (this->verbosity_ == NumProc::FULL and rank==0) { - std::cout << "Absolute model decrease: " << modelDecrease - << ", functional decrease: " << oldEnergy - energy << std::endl; - std::cout << "Relative model decrease: " << relativeModelDecrease - << ", functional decrease: " << (oldEnergy - energy)/energy << std::endl; - } - assert(modelDecrease >= 0); - - - if (energy >= oldEnergy and rank==0) { - if (this->verbosity_ == NumProc::FULL) - std::cout << "Direction is not a descent direction!" << std::endl; - } - - if (energy >= oldEnergy && - (std::abs((oldEnergy-energy)/energy) < 1e-9 || relativeModelDecrease < 1e-9)) { - if (this->verbosity_ == NumProc::FULL and rank==0) - std::cout << "Suspecting rounding problems" << std::endl; - - if (this->verbosity_ != NumProc::QUIET and rank==0) - std::cout << i+1 << " trust-region steps were taken." << std::endl; - - x_ = newIterate; - break; - } - } + + if (energy >= oldEnergy && + (std::abs((oldEnergy-energy)/energy) < 1e-9 || relativeModelDecrease < 1e-9)) { + if (this->verbosity_ == NumProc::FULL and rank==0) + std::cout << "Suspecting rounding problems" << std::endl; + + if (this->verbosity_ != NumProc::QUIET and rank==0) + std::cout << i+1 << " trust-region steps were taken." << std::endl; + + x_ = newIterate; + break; } + } + } - // ////////////////////////////////////////////// - // Check for acceptance of the step - // ////////////////////////////////////////////// - if (solved && (oldEnergy-energy) / modelDecrease > 0.9) { - // very successful iteration + // ////////////////////////////////////////////// + // Check for acceptance of the step + // ////////////////////////////////////////////// + if (solved && (oldEnergy-energy) / modelDecrease > 0.9) { + // very successful iteration - x_ = newIterate; - trustRegion.scale(2); + x_ = newIterate; + trustRegion.scale(2); - // current energy becomes 'oldEnergy' for the next iteration - oldEnergy = energy; + // current energy becomes 'oldEnergy' for the next iteration + oldEnergy = energy; - recomputeGradientHessian = true; + recomputeGradientHessian = true; - } else if (solved && ((oldEnergy-energy) / modelDecrease > 0.01 - || std::abs(oldEnergy-energy) < 1e-12)) { - // successful iteration - x_ = newIterate; + } else if (solved && ((oldEnergy-energy) / modelDecrease > 0.01 + || std::abs(oldEnergy-energy) < 1e-12)) { + // successful iteration + x_ = newIterate; - // current energy becomes 'oldEnergy' for the next iteration - oldEnergy = energy; + // current energy becomes 'oldEnergy' for the next iteration + oldEnergy = energy; - recomputeGradientHessian = true; + recomputeGradientHessian = true; - } else { + } else { - // unsuccessful iteration + // unsuccessful iteration - // Decrease the trust-region radius - trustRegion.scale(0.5); + // Decrease the trust-region radius + trustRegion.scale(0.5); - if (this->verbosity_ == NumProc::FULL and rank==0) - std::cout << "Unsuccessful iteration!" << std::endl; - if (trustRegion.radius() < 1e-9) { - if (this->verbosity_ == NumProc::FULL and rank==0) - std::cout << "The radius is too small to continue with a meaningful calculation!" << std::endl; + if (this->verbosity_ == NumProc::FULL and rank==0) + std::cout << "Unsuccessful iteration!" << std::endl; + if (trustRegion.radius() < 1e-9) { + if (this->verbosity_ == NumProc::FULL and rank==0) + std::cout << "The radius is too small to continue with a meaningful calculation!" << std::endl; - if (this->verbosity_ != NumProc::QUIET and rank==0) - std::cout << i+1 << " trust-region steps were taken" << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; - break; - } - } + if (this->verbosity_ != NumProc::QUIET and rank==0) + std::cout << i+1 << " trust-region steps were taken" << std::endl << "Total solver time: " << totalSolverTime << " sec., total assembly time: " << totalAssemblyTime << " sec." << std::endl; + break; + } + } - // ///////////////////////////////////////////////////////////////////// - // Write the iterate to disk for later convergence rate measurement - // ///////////////////////////////////////////////////////////////////// + // ///////////////////////////////////////////////////////////////////// + // Write the iterate to disk for later convergence rate measurement + // ///////////////////////////////////////////////////////////////////// - if (instrumented_) { + if (instrumented_) { - char iFilename[100]; - sprintf(iFilename, "tmp/intermediateSolution_%04ld", i); + char iFilename[100]; + sprintf(iFilename, "tmp/intermediateSolution_%04ld", i); - FILE* fpIterate = fopen(iFilename, "wb"); - if (!fpIterate) - DUNE_THROW(SolverError, "Couldn't open file " << iFilename << " for writing"); + FILE* fpIterate = fopen(iFilename, "wb"); + if (!fpIterate) + DUNE_THROW(SolverError, "Couldn't open file " << iFilename << " for writing"); - for (size_t j=0; j<x_.size(); j++) - fwrite(&x_[j], sizeof(TargetSpace), 1, fpIterate); + for (size_t j=0; j<x_.size(); j++) + fwrite(&x_[j], sizeof(TargetSpace), 1, fpIterate); - fclose(fpIterate); - - } + fclose(fpIterate); - if (rank==0) - std::cout << "iteration took " << totalTimer.elapsed() << " sec." << std::endl; } - // ////////////////////////////////////////////// - // Close logfile - // ////////////////////////////////////////////// - if (instrumented_) - fclose(fp); + if (rank==0) + std::cout << "iteration took " << totalTimer.elapsed() << " sec." << std::endl; + } + + // ////////////////////////////////////////////// + // Close logfile + // ////////////////////////////////////////////// + if (instrumented_) + fclose(fp); - statistics_.finalEnergy = oldEnergy; + statistics_.finalEnergy = oldEnergy; } diff --git a/dune/gfe/riemanniantrsolver.hh b/dune/gfe/riemanniantrsolver.hh index 78eacc6f..1d892f33 100644 --- a/dune/gfe/riemanniantrsolver.hh +++ b/dune/gfe/riemanniantrsolver.hh @@ -32,185 +32,185 @@ struct MapperFactory template <typename GridView> struct MapperFactory<Dune::Functions::LagrangeBasis<GridView,1> > { - typedef Dune::GlobalP1Mapper<Dune::Functions::LagrangeBasis<GridView,1>> GlobalMapper; - typedef Dune::MultipleCodimMultipleGeomTypeMapper<GridView> LocalMapper; - static LocalMapper createLocalMapper(const GridView& gridView) - { - return LocalMapper(gridView, Dune::mcmgVertexLayout()); - } + typedef Dune::GlobalP1Mapper<Dune::Functions::LagrangeBasis<GridView,1> > GlobalMapper; + typedef Dune::MultipleCodimMultipleGeomTypeMapper<GridView> LocalMapper; + static LocalMapper createLocalMapper(const GridView& gridView) + { + return LocalMapper(gridView, Dune::mcmgVertexLayout()); + } }; template <typename GridView> struct MapperFactory<Dune::Functions::LagrangeBasis<GridView,2> > { - typedef Dune::GlobalP2Mapper<Dune::Functions::LagrangeBasis<GridView,2>> GlobalMapper; - typedef P2BasisMapper<GridView> LocalMapper; - static LocalMapper createLocalMapper(const GridView& gridView) - { - return LocalMapper(gridView); - } + typedef Dune::GlobalP2Mapper<Dune::Functions::LagrangeBasis<GridView,2> > GlobalMapper; + typedef P2BasisMapper<GridView> LocalMapper; + static LocalMapper createLocalMapper(const GridView& gridView) + { + return LocalMapper(gridView); + } }; /** \brief Specialization for LagrangeBasis<3> */ template <typename GridView> struct MapperFactory<Dune::Functions::LagrangeBasis<GridView,3> > { - // Error: we don't currently have a global P3 mapper + // Error: we don't currently have a global P3 mapper }; /** \brief Riemannian trust-region solver for geodesic finite-element problems */ -template <class Basis, class TargetSpace, class Assembler = GeodesicFEAssembler<Basis,TargetSpace>> +template <class Basis, class TargetSpace, class Assembler = GeodesicFEAssembler<Basis,TargetSpace> > class RiemannianTrustRegionSolver - : public IterativeSolver<std::vector<TargetSpace>, - Dune::BitSetVector<TargetSpace::TangentVector::dimension> > + : public IterativeSolver<std::vector<TargetSpace>, + Dune::BitSetVector<TargetSpace::TangentVector::dimension> > { - typedef typename Basis::GridView::Grid GridType; + typedef typename Basis::GridView::Grid GridType; - const static int blocksize = TargetSpace::TangentVector::dimension; + const static int blocksize = TargetSpace::TangentVector::dimension; - const static int gridDim = GridType::dimension; + const static int gridDim = GridType::dimension; - // Centralize the field type here - typedef double field_type; + // Centralize the field type here + typedef double field_type; - // Some types that I need - typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize, blocksize> > MatrixType; - typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize> > CorrectionType; - typedef std::vector<TargetSpace> SolutionType; + // Some types that I need + typedef Dune::BCRSMatrix<Dune::FieldMatrix<field_type, blocksize, blocksize> > MatrixType; + typedef Dune::BlockVector<Dune::FieldVector<field_type, blocksize> > CorrectionType; + typedef std::vector<TargetSpace> SolutionType; #if HAVE_MPI - typedef typename MapperFactory<Basis>::GlobalMapper GlobalMapper; - typedef typename MapperFactory<Basis>::LocalMapper LocalMapper; + typedef typename MapperFactory<Basis>::GlobalMapper GlobalMapper; + typedef typename MapperFactory<Basis>::LocalMapper LocalMapper; #endif - /** \brief Records information about the last run of the RiemannianTrustRegionSolver - * - * This is used primarily for unit testing. - */ - struct Statistics - { - std::size_t finalIteration; + /** \brief Records information about the last run of the RiemannianTrustRegionSolver + * + * This is used primarily for unit testing. + */ + struct Statistics + { + std::size_t finalIteration; - field_type finalEnergy; - }; + field_type finalEnergy; + }; public: - RiemannianTrustRegionSolver() - : IterativeSolver<std::vector<TargetSpace>, Dune::BitSetVector<blocksize> >(0,100,NumProc::FULL), - hessianMatrix_(nullptr), h1SemiNorm_(NULL) - { - std::fill(scaling_.begin(), scaling_.end(), 1.0); - } - - /** \brief Set up the solver using a monotone multigrid method as the inner solver */ - void setup(const GridType& grid, - const Assembler* assembler, - const SolutionType& x, - const Dune::BitSetVector<blocksize>& dirichletNodes, - double tolerance, - int maxTrustRegionSteps, - double initialTrustRegionRadius, - int multigridIterations, - double mgTolerance, - int mu, - int nu1, - int nu2, - int baseIterations, - double baseTolerance, - bool instrumented); - - /** \brief Set up the solver using a monotone multigrid method as the inner solver */ - void setup(const GridType& grid, - const Assembler* assembler, - const SolutionType& x, - const Dune::BitSetVector<blocksize>& dirichletNodes, - const Dune::ParameterTree& parameterSet); - - void setScaling(const Dune::FieldVector<double,blocksize>& scaling) - { - scaling_ = scaling; - } - - void setIgnoreNodes(const Dune::BitSetVector<blocksize>& ignoreNodes) - { - ignoreNodes_ = &ignoreNodes; - std::shared_ptr<LoopSolver<CorrectionType> > loopSolver = std::dynamic_pointer_cast<LoopSolver<CorrectionType> >(innerSolver_); - assert(loopSolver); - loopSolver->iterationStep_->ignoreNodes_ = ignoreNodes_; - } - - void solve(); - - [[deprecated]] - void setInitialSolution(const SolutionType& x) { - x_ = x; - } - - void setInitialIterate(const SolutionType& x) { - x_ = x; - } - - SolutionType getSol() const {return x_;} - - const Statistics& getStatistics() const {return statistics_;} + RiemannianTrustRegionSolver() + : IterativeSolver<std::vector<TargetSpace>, Dune::BitSetVector<blocksize> >(0,100,NumProc::FULL), + hessianMatrix_(nullptr), h1SemiNorm_(NULL) + { + std::fill(scaling_.begin(), scaling_.end(), 1.0); + } + + /** \brief Set up the solver using a monotone multigrid method as the inner solver */ + void setup(const GridType& grid, + const Assembler* assembler, + const SolutionType& x, + const Dune::BitSetVector<blocksize>& dirichletNodes, + double tolerance, + int maxTrustRegionSteps, + double initialTrustRegionRadius, + int multigridIterations, + double mgTolerance, + int mu, + int nu1, + int nu2, + int baseIterations, + double baseTolerance, + bool instrumented); + + /** \brief Set up the solver using a monotone multigrid method as the inner solver */ + void setup(const GridType& grid, + const Assembler* assembler, + const SolutionType& x, + const Dune::BitSetVector<blocksize>& dirichletNodes, + const Dune::ParameterTree& parameterSet); + + void setScaling(const Dune::FieldVector<double,blocksize>& scaling) + { + scaling_ = scaling; + } + + void setIgnoreNodes(const Dune::BitSetVector<blocksize>& ignoreNodes) + { + ignoreNodes_ = &ignoreNodes; + std::shared_ptr<LoopSolver<CorrectionType> > loopSolver = std::dynamic_pointer_cast<LoopSolver<CorrectionType> >(innerSolver_); + assert(loopSolver); + loopSolver->iterationStep_->ignoreNodes_ = ignoreNodes_; + } + + void solve(); + + [[deprecated]] + void setInitialSolution(const SolutionType& x) { + x_ = x; + } + + void setInitialIterate(const SolutionType& x) { + x_ = x; + } + + SolutionType getSol() const {return x_;} + + const Statistics& getStatistics() const {return statistics_;} protected: #if HAVE_MPI - std::unique_ptr<GlobalMapper> globalMapper_; + std::unique_ptr<GlobalMapper> globalMapper_; #endif - /** \brief The grid */ - const GridType* grid_; + /** \brief The grid */ + const GridType* grid_; - /** \brief The solution vector */ - SolutionType x_; + /** \brief The solution vector */ + SolutionType x_; - /** \brief The initial trust-region radius in the maximum-norm */ - double initialTrustRegionRadius_; + /** \brief The initial trust-region radius in the maximum-norm */ + double initialTrustRegionRadius_; - /** \brief Trust-region norm scaling */ - Dune::FieldVector<double,blocksize> scaling_; + /** \brief Trust-region norm scaling */ + Dune::FieldVector<double,blocksize> scaling_; - /** \brief Maximum number of trust-region steps */ - std::size_t maxTrustRegionSteps_; + /** \brief Maximum number of trust-region steps */ + std::size_t maxTrustRegionSteps_; - /** \brief Maximum number of multigrid iterations */ - int innerIterations_; + /** \brief Maximum number of multigrid iterations */ + int innerIterations_; - /** \brief Error tolerance of the multigrid QP solver */ - double innerTolerance_; + /** \brief Error tolerance of the multigrid QP solver */ + double innerTolerance_; - /** \brief Hessian matrix */ - std::unique_ptr<MatrixType> hessianMatrix_; + /** \brief Hessian matrix */ + std::unique_ptr<MatrixType> hessianMatrix_; - /** \brief The assembler for the material law */ - const Assembler* assembler_; + /** \brief The assembler for the material law */ + const Assembler* assembler_; - /** \brief The solver for the quadratic inner problems */ - std::shared_ptr<Solver> innerSolver_; + /** \brief The solver for the quadratic inner problems */ + std::shared_ptr<Solver> innerSolver_; - /** \brief Contains 'true' everywhere -- the trust-region is bounded */ - Dune::BitSetVector<blocksize> hasObstacle_; + /** \brief Contains 'true' everywhere -- the trust-region is bounded */ + Dune::BitSetVector<blocksize> hasObstacle_; - /** \brief The Dirichlet nodes */ - const Dune::BitSetVector<blocksize>* ignoreNodes_; + /** \brief The Dirichlet nodes */ + const Dune::BitSetVector<blocksize>* ignoreNodes_; - /** \brief The norm used to measure multigrid convergence */ - std::shared_ptr<H1SemiNorm<CorrectionType> > h1SemiNorm_; + /** \brief The norm used to measure multigrid convergence */ + std::shared_ptr<H1SemiNorm<CorrectionType> > h1SemiNorm_; - /** \brief An L2-norm, really. The H1SemiNorm class is badly named */ - std::shared_ptr<H1SemiNorm<CorrectionType> > l2Norm_; + /** \brief An L2-norm, really. The H1SemiNorm class is badly named */ + std::shared_ptr<H1SemiNorm<CorrectionType> > l2Norm_; - /** \brief If set to true we log convergence speed and other stuff */ - bool instrumented_; + /** \brief If set to true we log convergence speed and other stuff */ + bool instrumented_; - /** \brief Norm type used for stopping criterion (default is infinity norm) */ - enum class ErrorNormType {infinity, H1semi} normType_ = ErrorNormType::infinity; + /** \brief Norm type used for stopping criterion (default is infinity norm) */ + enum class ErrorNormType {infinity, H1semi} normType_ = ErrorNormType::infinity; - /** \brief Store information about solver runs for unit testing */ - Statistics statistics_; + /** \brief Store information about solver runs for unit testing */ + Statistics statistics_; }; diff --git a/dune/gfe/rodfactory.hh b/dune/gfe/rodfactory.hh index 94e06e45..933fefee 100644 --- a/dune/gfe/rodfactory.hh +++ b/dune/gfe/rodfactory.hh @@ -19,23 +19,23 @@ template <class GridView> class RodFactory { - static_assert(GridView::dimensionworld==1, "RodFactory is only implemented for grids in a 1d world"); + static_assert(GridView::dimensionworld==1, "RodFactory is only implemented for grids in a 1d world"); public: - RodFactory(const GridView& gridView) + RodFactory(const GridView& gridView) : gridView_(gridView) - {} + {} -/** \brief Make a straight, unsheared rod from two given endpoints + /** \brief Make a straight, unsheared rod from two given endpoints -\param[out] rod The new rod -\param[in] n The number of vertices -*/ -template <int dim> - void create(std::vector<RigidBodyMotion<double,dim> >& rod, - const Dune::FieldVector<double,3>& beginning, const Dune::FieldVector<double,3>& end) -{ + \param[out] rod The new rod + \param[in] n The number of vertices + */ + template <int dim> + void create(std::vector<RigidBodyMotion<double,dim> >& rod, + const Dune::FieldVector<double,3>& beginning, const Dune::FieldVector<double,3>& end) + { // Compute the correct orientation Rotation<double,dim> orientation = Rotation<double,dim>::identity(); @@ -43,7 +43,7 @@ template <int dim> zAxis[2] = 1; Dune::FieldVector<double,3> axis = MatrixVector::crossProduct(Dune::FieldVector<double,3>(end-beginning), zAxis); if (axis.two_norm() != 0) - axis /= -axis.two_norm(); + axis /= -axis.two_norm(); Dune::FieldVector<double,3> d3 = end-beginning; d3 /= d3.two_norm(); @@ -51,22 +51,22 @@ template <int dim> double angle = std::acos(zAxis * d3); if (angle != 0) - orientation = Rotation<double,3>(axis, angle); + orientation = Rotation<double,3>(axis, angle); - // Set the values - create(rod, RigidBodyMotion<double,dim>(beginning,orientation), RigidBodyMotion<double,dim>(end,orientation)); -} + // Set the values + create(rod, RigidBodyMotion<double,dim>(beginning,orientation), RigidBodyMotion<double,dim>(end,orientation)); + } -/** \brief Make a rod by interpolating between two end configurations + /** \brief Make a rod by interpolating between two end configurations -\param[out] rod The new rod -*/ - template <int spaceDim> - void create(std::vector<RigidBodyMotion<double,spaceDim> >& rod, - const RigidBodyMotion<double,spaceDim>& beginning, - const RigidBodyMotion<double,spaceDim>& end) -{ + \param[out] rod The new rod + */ + template <int spaceDim> + void create(std::vector<RigidBodyMotion<double,spaceDim> >& rod, + const RigidBodyMotion<double,spaceDim>& beginning, + const RigidBodyMotion<double,spaceDim>& end) + { static const int dim = GridView::dimension; // de facto: 1 @@ -81,8 +81,8 @@ template <int dim> double max = -std::numeric_limits<double>::max(); for (; vIt != vEndIt; ++vIt) { - min = std::min(min, vIt->geometry().corner(0)[0]); - max = std::max(max, vIt->geometry().corner(0)[0]); + min = std::min(min, vIt->geometry().corner(0)[0]); + max = std::max(max, vIt->geometry().corner(0)[0]); } //////////////////////////////////////////////////////////////////////////////////// @@ -92,105 +92,105 @@ template <int dim> rod.resize(gridView_.size(dim)); for (vIt = gridView_.template begin<dim>(); vIt != vEndIt; ++vIt) { - int idx = gridView_.indexSet().index(*vIt); - Dune::FieldVector<double,1> local = (vIt->geometry().corner(0)[0] - min) / (max - min); + int idx = gridView_.indexSet().index(*vIt); + Dune::FieldVector<double,1> local = (vIt->geometry().corner(0)[0] - min) / (max - min); - for (int i=0; i<3; i++) - rod[idx].r[i] = (1-local)*beginning.r[i] + local*end.r[i]; - rod[idx].q = Rotation<double,3>::interpolate(beginning.q, end.q, local); - } -} - - /** \brief Make a rod by setting each entry to the same value - - \param[out] rod The new rod - */ - template <int spaceDim> - void create(std::vector<RigidBodyMotion<double,spaceDim> >& rod, - const RigidBodyMotion<double,spaceDim>& value) - { - rod.resize(gridView_.size(1)); - std::fill(rod.begin(), rod.end(), value); + for (int i=0; i<3; i++) + rod[idx].r[i] = (1-local)*beginning.r[i] + local*end.r[i]; + rod[idx].q = Rotation<double,3>::interpolate(beginning.q, end.q, local); } + } + + /** \brief Make a rod by setting each entry to the same value + + \param[out] rod The new rod + */ + template <int spaceDim> + void create(std::vector<RigidBodyMotion<double,spaceDim> >& rod, + const RigidBodyMotion<double,spaceDim>& value) + { + rod.resize(gridView_.size(1)); + std::fill(rod.begin(), rod.end(), value); + } + + /** \brief Make a rod by linearly interpolating between the end values + + \note The end values are expected to be in the input container! + \param[in,out] rod The new rod + */ + template <int spaceDim> + void create(std::vector<RigidBodyMotion<double,spaceDim> >& rod) + { + static const int dim = GridView::dimension; // de facto: 1 + assert(gridView_.size(dim)==rod.size()); + + ////////////////////////////////////////////////////////////////////////////////////////////// + // Get smallest and largest coordinate, in order to create an arc-length parametrization + ////////////////////////////////////////////////////////////////////////////////////////////// + + typename GridView::template Codim<dim>::Iterator vIt = gridView_.template begin<dim>(); + typename GridView::template Codim<dim>::Iterator vEndIt = gridView_.template end<dim>(); - /** \brief Make a rod by linearly interpolating between the end values - - \note The end values are expected to be in the input container! - \param[in,out] rod The new rod - */ - template <int spaceDim> - void create(std::vector<RigidBodyMotion<double,spaceDim> >& rod) - { - static const int dim = GridView::dimension; // de facto: 1 - assert(gridView_.size(dim)==rod.size()); - - ////////////////////////////////////////////////////////////////////////////////////////////// - // Get smallest and largest coordinate, in order to create an arc-length parametrization - ////////////////////////////////////////////////////////////////////////////////////////////// - - typename GridView::template Codim<dim>::Iterator vIt = gridView_.template begin<dim>(); - typename GridView::template Codim<dim>::Iterator vEndIt = gridView_.template end<dim>(); - - double min = std::numeric_limits<double>::max(); - double max = -std::numeric_limits<double>::max(); - RigidBodyMotion<double,spaceDim> beginning, end; - - for (; vIt != vEndIt; ++vIt) { - if (vIt->geometry().corner(0)[0] < min) { - min = vIt->geometry().corner(0)[0]; - beginning = rod[gridView_.indexSet().index(*vIt)]; - } - if (vIt->geometry().corner(0)[0] > max) { - max = vIt->geometry().corner(0)[0]; - end = rod[gridView_.indexSet().index(*vIt)]; - } - } - - //////////////////////////////////////////////////////////////////////////////////// - // Interpolate according to arc-length - //////////////////////////////////////////////////////////////////////////////////// - - rod.resize(gridView_.size(dim)); - - for (vIt = gridView_.template begin<dim>(); vIt != vEndIt; ++vIt) { - int idx = gridView_.indexSet().index(*vIt); - Dune::FieldVector<double,1> local = (vIt->geometry().corner(0)[0] - min) / (max - min); - - for (int i=0; i<3; i++) - rod[idx].r[i] = (1-local)*beginning.r[i] + local*end.r[i]; - rod[idx].q = Rotation<double,3>::interpolate(beginning.q, end.q, local); - } + double min = std::numeric_limits<double>::max(); + double max = -std::numeric_limits<double>::max(); + RigidBodyMotion<double,spaceDim> beginning, end; + + for (; vIt != vEndIt; ++vIt) { + if (vIt->geometry().corner(0)[0] < min) { + min = vIt->geometry().corner(0)[0]; + beginning = rod[gridView_.indexSet().index(*vIt)]; + } + if (vIt->geometry().corner(0)[0] > max) { + max = vIt->geometry().corner(0)[0]; + end = rod[gridView_.indexSet().index(*vIt)]; + } } + //////////////////////////////////////////////////////////////////////////////////// + // Interpolate according to arc-length + //////////////////////////////////////////////////////////////////////////////////// -/** \brief Make a rod solving a static Dirichlet problem + rod.resize(gridView_.size(dim)); - \param rod The configuration to be computed - \param radius The rod's radius - \param E The rod's elastic modulus - \param nu The rod's Poission modulus - \param beginning The prescribed Dirichlet values - \param end The prescribed Dirichlet values - \param[out] rod The new rod - */ -template <int spaceDim> -void create(std::vector<RigidBodyMotion<double,spaceDim> >& rod, - double radius, double E, double nu, - const RigidBodyMotion<double,spaceDim>& beginning, - const RigidBodyMotion<double,spaceDim>& end) -{ + for (vIt = gridView_.template begin<dim>(); vIt != vEndIt; ++vIt) { + int idx = gridView_.indexSet().index(*vIt); + Dune::FieldVector<double,1> local = (vIt->geometry().corner(0)[0] - min) / (max - min); + + for (int i=0; i<3; i++) + rod[idx].r[i] = (1-local)*beginning.r[i] + local*end.r[i]; + rod[idx].q = Rotation<double,3>::interpolate(beginning.q, end.q, local); + } + } + + + /** \brief Make a rod solving a static Dirichlet problem + + \param rod The configuration to be computed + \param radius The rod's radius + \param E The rod's elastic modulus + \param nu The rod's Poission modulus + \param beginning The prescribed Dirichlet values + \param end The prescribed Dirichlet values + \param[out] rod The new rod + */ + template <int spaceDim> + void create(std::vector<RigidBodyMotion<double,spaceDim> >& rod, + double radius, double E, double nu, + const RigidBodyMotion<double,spaceDim>& beginning, + const RigidBodyMotion<double,spaceDim>& end) + { // Make Dirichlet bitfields for the rods as well Dune::BitSetVector<6> rodDirichletNodes(gridView_.size(GridView::dimension),false); for (int j=0; j<6; j++) { - rodDirichletNodes[0][j] = true; - rodDirichletNodes.back()[j] = true; + rodDirichletNodes[0][j] = true; + rodDirichletNodes.back()[j] = true; } // Create local assembler for the static elastic problem RodLocalStiffness<GridView,double> rodLocalStiffness(gridView_, radius*radius*M_PI, - std::pow(radius,4) * 0.25* M_PI, std::pow(radius,4) * 0.25* M_PI, E, nu); + std::pow(radius,4) * 0.25* M_PI, std::pow(radius,4) * 0.25* M_PI, E, nu); typedef Dune::Functions::PQKNodalBasis<GridView,1> RodP1Basis; RodP1Basis p1Basis(gridView_); @@ -209,11 +209,11 @@ void create(std::vector<RigidBodyMotion<double,spaceDim> >& rod, // Trust--Region solver RiemannianTrustRegionSolver<RodP1Basis, RigidBodyMotion<double,spaceDim> > rodSolver; rodSolver.setup(gridView_.grid(), &assembler, rod, - rodDirichletNodes, - 1e-10, 100, // TR tolerance and iterations - 20, // init TR radius - 200, 1e-00, 1, 3, 3, // Multigrid parameters - 100, 1e-8 , false); // base solver parameters + rodDirichletNodes, + 1e-10, 100, // TR tolerance and iterations + 20, // init TR radius + 200, 1e-00, 1, 3, 3, // Multigrid parameters + 100, 1e-8 , false); // base solver parameters rodSolver.verbosity_ = NumProc::QUIET; @@ -222,11 +222,11 @@ void create(std::vector<RigidBodyMotion<double,spaceDim> >& rod, rod = rodSolver.getSol(); -} + } private: - const GridView gridView_; + const GridView gridView_; }; #endif diff --git a/dune/gfe/skewmatrix.hh b/dune/gfe/skewmatrix.hh index b194759f..c45484b5 100644 --- a/dune/gfe/skewmatrix.hh +++ b/dune/gfe/skewmatrix.hh @@ -11,91 +11,91 @@ class SkewMatrix template <class T> class SkewMatrix<T,3> { - + public: - - /** \brief Default constructor -- does nothing */ - SkewMatrix() - {} - - /** \brief Constructor from an axial vector */ - explicit SkewMatrix(const Dune::FieldVector<T,3>& v) + + /** \brief Default constructor -- does nothing */ + SkewMatrix() + {} + + /** \brief Constructor from an axial vector */ + explicit SkewMatrix(const Dune::FieldVector<T,3>& v) : data_(v) - {} - - /** \brief Constructor from a general matrix */ - explicit SkewMatrix(const Dune::FieldMatrix<T,3,3>& m) - { - data_ = {m[2][1], m[0][2], m[1][0]}; - } - - SkewMatrix(T v) + {} + + /** \brief Constructor from a general matrix */ + explicit SkewMatrix(const Dune::FieldMatrix<T,3,3>& m) + { + data_ = {m[2][1], m[0][2], m[1][0]}; + } + + SkewMatrix(T v) : data_(v) - {} - - SkewMatrix<T,3>& operator*=(const T& a) - { - data_ *= a; - return *this; - } - - Dune::FieldVector<T,3>& axial() - { - return data_; - } - - const Dune::FieldVector<T,3>& axial() const - { - return data_; - } - - typedef typename Dune::FieldVector<T,3>::Iterator Iterator; - - //! begin iterator - Iterator begin () - { - return Iterator(data_,0); - } - - //! end iterator - Iterator end () - { - return Iterator(data_,3); - } - - typedef typename Dune::FieldVector<T,3>::ConstIterator ConstIterator; - //! begin iterator - ConstIterator begin () const - { - return ConstIterator(data_,0); - } - - //! end iterator - ConstIterator end () const - { - return ConstIterator(data_,3); - } - - /** \brief Embed the skew-symmetric matrix in R^3x3 */ - Dune::FieldMatrix<T,3,3> toMatrix() const - { - Dune::FieldMatrix<T,3,3> mat; - mat = 0; - - mat[0][1] = -data_[2]; - mat[1][0] = data_[2]; - mat[0][2] = data_[1]; - mat[2][0] = -data_[1]; - mat[1][2] = -data_[0]; - mat[2][1] = data_[0]; - - return mat; - } - + {} + + SkewMatrix<T,3>& operator*=(const T& a) + { + data_ *= a; + return *this; + } + + Dune::FieldVector<T,3>& axial() + { + return data_; + } + + const Dune::FieldVector<T,3>& axial() const + { + return data_; + } + + typedef typename Dune::FieldVector<T,3>::Iterator Iterator; + + //! begin iterator + Iterator begin () + { + return Iterator(data_,0); + } + + //! end iterator + Iterator end () + { + return Iterator(data_,3); + } + + typedef typename Dune::FieldVector<T,3>::ConstIterator ConstIterator; + //! begin iterator + ConstIterator begin () const + { + return ConstIterator(data_,0); + } + + //! end iterator + ConstIterator end () const + { + return ConstIterator(data_,3); + } + + /** \brief Embed the skew-symmetric matrix in R^3x3 */ + Dune::FieldMatrix<T,3,3> toMatrix() const + { + Dune::FieldMatrix<T,3,3> mat; + mat = 0; + + mat[0][1] = -data_[2]; + mat[1][0] = data_[2]; + mat[0][2] = data_[1]; + mat[2][0] = -data_[1]; + mat[1][2] = -data_[0]; + mat[2][1] = data_[0]; + + return mat; + } + private: - // we store the axial vector - Dune::FieldVector<T,3> data_; - + // we store the axial vector + Dune::FieldVector<T,3> data_; + }; #endif diff --git a/dune/gfe/spaces/hyperbolichalfspacepoint.hh b/dune/gfe/spaces/hyperbolichalfspacepoint.hh index 2a8f6b20..e4b21e6a 100644 --- a/dune/gfe/spaces/hyperbolichalfspacepoint.hh +++ b/dune/gfe/spaces/hyperbolichalfspacepoint.hh @@ -13,540 +13,540 @@ \tparam N Dimension of the hyperbolic half-space \tparam T The type used for individual coordinates -*/ + */ template <class T, int N> class HyperbolicHalfspacePoint { - static_assert(N>=2, "A hyperbolic half-space needs to be at least two-dimensional!"); - - /** \brief Compute the derivative of arccosh^2 without getting unstable for x close to 1 */ - static T derivativeOfArcCosHSquared(const T& x) { - const T eps = 1e-4; - if (x < 1+eps) { // regular expression is unstable, use the series expansion instead - return 2 - 2*(x-1)/3 + 4/15*(x-1)*(x-1); - } else - return 2*std::acosh(x) / std::sqrt(x*x-1); + static_assert(N>=2, "A hyperbolic half-space needs to be at least two-dimensional!"); + + /** \brief Compute the derivative of arccosh^2 without getting unstable for x close to 1 */ + static T derivativeOfArcCosHSquared(const T& x) { + const T eps = 1e-4; + if (x < 1+eps) { // regular expression is unstable, use the series expansion instead + return 2 - 2*(x-1)/3 + 4/15*(x-1)*(x-1); + } else + return 2*std::acosh(x) / std::sqrt(x*x-1); + } + + /** \brief Compute the second derivative of arccosh^2 without getting unstable for x close to 1 */ + static T secondDerivativeOfArcCosHSquared(const T& x) { + const T eps = 1e-4; + if (x < 1+eps) { // regular expression is unstable, use the series expansion instead + return -2.0/3 + 8*(x-1)/15; + } else + return 2/(x*x-1) - 2*x*std::acosh(x) / std::pow(x*x-1,1.5); + } + + /** \brief Compute the third derivative of arccos^2 without getting unstable for x close to 1 */ + static T thirdDerivativeOfArcCosHSquared(const T& x) { + const T eps = 1e-4; + if (x < 1+eps) { // regular expression is unstable, use the series expansion instead + return 8.0/15 - 24*(x-1)/35; + } else { + T d = x*x-1; + return -6*x/(d*d) + (4*x*x+2)*std::acosh(x)/(std::pow(d,2.5)); } + } - /** \brief Compute the second derivative of arccosh^2 without getting unstable for x close to 1 */ - static T secondDerivativeOfArcCosHSquared(const T& x) { - const T eps = 1e-4; - if (x < 1+eps) { // regular expression is unstable, use the series expansion instead - return -2.0/3 + 8*(x-1)/15; - } else - return 2/(x*x-1) - 2*x*std::acosh(x) / std::pow(x*x-1,1.5); - } + /** \brief Compute derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to p + \param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons + */ + static Dune::FieldVector<T,N> computeDFdp(const HyperbolicHalfspacePoint& p, const HyperbolicHalfspacePoint& q, const T& diffNormSquared) + { + Dune::FieldVector<T,N> result; - /** \brief Compute the third derivative of arccos^2 without getting unstable for x close to 1 */ - static T thirdDerivativeOfArcCosHSquared(const T& x) { - const T eps = 1e-4; - if (x < 1+eps) { // regular expression is unstable, use the series expansion instead - return 8.0/15 - 24*(x-1)/35; - } else { - T d = x*x-1; - return -6*x/(d*d) + (4*x*x+2)*std::acosh(x)/(std::pow(d,2.5)); - } - } + for (size_t i=0; i<N-1; i++) + result[i] = ( p.data_[i] - q.data_[i] ) / (q.data_[N-1] * p.data_[N-1]); - /** \brief Compute derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to p - \param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons - */ - static Dune::FieldVector<T,N> computeDFdp(const HyperbolicHalfspacePoint& p, const HyperbolicHalfspacePoint& q, const T& diffNormSquared) - { - Dune::FieldVector<T,N> result; - - for (size_t i=0; i<N-1; i++) - result[i] = ( p.data_[i] - q.data_[i] ) / (q.data_[N-1] * p.data_[N-1]); - - result[N-1] = - diffNormSquared / (2*p.data_[N-1]*p.data_[N-1]*q.data_[N-1]) + (p.data_[N-1] - q.data_[N-1]) / (p.data_[N-1]*q.data_[N-1]); - - return result; - } - - /** \brief Compute derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to q + result[N-1] = - diffNormSquared / (2*p.data_[N-1]*p.data_[N-1]*q.data_[N-1]) + (p.data_[N-1] - q.data_[N-1]) / (p.data_[N-1]*q.data_[N-1]); + + return result; + } + + /** \brief Compute derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to q \param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons - */ - static Dune::FieldVector<T,N> computeDFdq(const HyperbolicHalfspacePoint& p, const HyperbolicHalfspacePoint& q, const T& diffNormSquared) - { - Dune::FieldVector<T,N> result; - - for (size_t i=0; i<N-1; i++) - result[i] = ( q.data_[i] - p.data_[i] ) / (q.data_[N-1] * p.data_[N-1]); - - result[N-1] = - diffNormSquared / (2*p.data_[N-1]*q.data_[N-1]*q.data_[N-1]) - (p.data_[N-1] - q.data_[N-1]) / (p.data_[N-1]*q.data_[N-1]); - - return result; - } - - /** \brief Compute second derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to p and q + */ + static Dune::FieldVector<T,N> computeDFdq(const HyperbolicHalfspacePoint& p, const HyperbolicHalfspacePoint& q, const T& diffNormSquared) + { + Dune::FieldVector<T,N> result; + + for (size_t i=0; i<N-1; i++) + result[i] = ( q.data_[i] - p.data_[i] ) / (q.data_[N-1] * p.data_[N-1]); + + result[N-1] = - diffNormSquared / (2*p.data_[N-1]*q.data_[N-1]*q.data_[N-1]) - (p.data_[N-1] - q.data_[N-1]) / (p.data_[N-1]*q.data_[N-1]); + + return result; + } + + /** \brief Compute second derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to p and q \param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons - */ - static Dune::FieldMatrix<T,N,N> computeDFdpdq(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b, const T& diffNormSquared) - { - // abbreviate notation - const Dune::FieldVector<T,N>& p = a.data_; - const Dune::FieldVector<T,N>& q = b.data_; - - Dune::FieldMatrix<T,N,N> dFdpdq; - - for (size_t i=0; i<N; i++) { - - for (size_t j=0; j<N; j++) { - - if (i!=N-1 and j!=N-1) { - - dFdpdq[i][j] = -(i==j) / (p[N-1]*q[N-1]); - - } else if (i!=N-1 and j==N-1) { - - dFdpdq[i][j] = -(p[i] - q[i]) / (p[N-1]*q[N-1]*q[N-1]); - - } else if (i==N-1 and j!=N-1) { - - dFdpdq[i][j] = (p[j] - q[j]) / (p[N-1]*p[N-1]*q[N-1]); - - } else if (i==N-1 and j==N-1) { - - dFdpdq[i][j] = -1/(p[N-1]*q[N-1]) - - (p[N-1]-q[N-1]) / (p[N-1]*q[N-1]*q[N-1]) - + (p[N-1]-q[N-1]) / (p[N-1]*p[N-1]*q[N-1]) + diffNormSquared / (2*p[N-1]*p[N-1]*q[N-1]*q[N-1]); - - } - - } - + */ + static Dune::FieldMatrix<T,N,N> computeDFdpdq(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b, const T& diffNormSquared) + { + // abbreviate notation + const Dune::FieldVector<T,N>& p = a.data_; + const Dune::FieldVector<T,N>& q = b.data_; + + Dune::FieldMatrix<T,N,N> dFdpdq; + + for (size_t i=0; i<N; i++) { + + for (size_t j=0; j<N; j++) { + + if (i!=N-1 and j!=N-1) { + + dFdpdq[i][j] = -(i==j) / (p[N-1]*q[N-1]); + + } else if (i!=N-1 and j==N-1) { + + dFdpdq[i][j] = -(p[i] - q[i]) / (p[N-1]*q[N-1]*q[N-1]); + + } else if (i==N-1 and j!=N-1) { + + dFdpdq[i][j] = (p[j] - q[j]) / (p[N-1]*p[N-1]*q[N-1]); + + } else if (i==N-1 and j==N-1) { + + dFdpdq[i][j] = -1/(p[N-1]*q[N-1]) + - (p[N-1]-q[N-1]) / (p[N-1]*q[N-1]*q[N-1]) + + (p[N-1]-q[N-1]) / (p[N-1]*p[N-1]*q[N-1]) + diffNormSquared / (2*p[N-1]*p[N-1]*q[N-1]*q[N-1]); + } - return dFdpdq; + } + } - - /** \brief Compute second derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to q + + return dFdpdq; + } + + /** \brief Compute second derivative of $F(p,q) = 1 + ||p-q||^2 / 2p_nq_n with respect to q \param[in] diffNormSquared Expected to be ||p-q||^2, taken from the caller for efficiency reasons - */ - static Dune::FieldMatrix<T,N,N> computeDFdqdq(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b, const T& diffNormSquared) - { - // abbreviate notation - const Dune::FieldVector<T,N>& p = a.data_; - const Dune::FieldVector<T,N>& q = b.data_; - - Dune::FieldMatrix<T,N,N> dFdqdq; - - for (size_t i=0; i<N; i++) { - - for (size_t j=0; j<N; j++) { - - if (i!=N-1 and j!=N-1) { - - dFdqdq[i][j] = (i==j) / (p[N-1]*q[N-1]); - - } else if (i!=N-1 and j==N-1) { - - dFdqdq[i][j] = (p[i] - q[i]) / (p[N-1]*q[N-1]*q[N-1]); - - } else if (i==N-1 and j!=N-1) { - - dFdqdq[i][j] = (p[j] - q[j]) / (p[N-1]*q[N-1]*q[N-1]); - - } else if (i==N-1 and j==N-1) { - - dFdqdq[i][j] = 1/(q[N-1]*q[N-1]) + (p[N-1]-q[N-1]) / (p[N-1]*q[N-1]*q[N-1]) + diffNormSquared / (p[N-1]*q[N-1]*q[N-1]*q[N-1]); - - } - - } - + */ + static Dune::FieldMatrix<T,N,N> computeDFdqdq(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b, const T& diffNormSquared) + { + // abbreviate notation + const Dune::FieldVector<T,N>& p = a.data_; + const Dune::FieldVector<T,N>& q = b.data_; + + Dune::FieldMatrix<T,N,N> dFdqdq; + + for (size_t i=0; i<N; i++) { + + for (size_t j=0; j<N; j++) { + + if (i!=N-1 and j!=N-1) { + + dFdqdq[i][j] = (i==j) / (p[N-1]*q[N-1]); + + } else if (i!=N-1 and j==N-1) { + + dFdqdq[i][j] = (p[i] - q[i]) / (p[N-1]*q[N-1]*q[N-1]); + + } else if (i==N-1 and j!=N-1) { + + dFdqdq[i][j] = (p[j] - q[j]) / (p[N-1]*q[N-1]*q[N-1]); + + } else if (i==N-1 and j==N-1) { + + dFdqdq[i][j] = 1/(q[N-1]*q[N-1]) + (p[N-1]-q[N-1]) / (p[N-1]*q[N-1]*q[N-1]) + diffNormSquared / (p[N-1]*q[N-1]*q[N-1]*q[N-1]); + } - return dFdqdq; + } + } - - - + + return dFdqdq; + } + + + public: - /** \brief The type used for coordinates */ - typedef T ctype; - - /** \brief The type used for global coordinates */ - typedef Dune::FieldVector<T,N> CoordinateType; - - /** \brief Dimension of the manifold */ - static const int dim = N; - - /** \brief Dimension of the Euclidean space the manifold is embedded in */ - static const int embeddedDim = N; - - /** \brief Type of a tangent vector in local coordinates */ - typedef Dune::FieldVector<T,N> TangentVector; - - /** \brief Type of a tangent vector in the embedding space */ - typedef Dune::FieldVector<T,N> EmbeddedTangentVector; - - /** \brief The global convexity radius of the hyberbolic plane */ - static constexpr T convexityRadius = std::numeric_limits<T>::infinity(); - - /** \brief Default constructor */ - HyperbolicHalfspacePoint() - {} - - /** \brief Constructor from a vector. The vector gets normalized */ - HyperbolicHalfspacePoint(const Dune::FieldVector<T,N>& vector) - : data_(vector) - { - assert(vector[N-1]>0); - } - - /** \brief Constructor from an array. The array gets normalized */ - HyperbolicHalfspacePoint(const std::array<T,N>& vector) - { - assert(vector.back()>0); - for (int i=0; i<N; i++) - data_[i] = vector[i]; - } + /** \brief The type used for coordinates */ + typedef T ctype; - /** \brief The exponential map */ - static HyperbolicHalfspacePoint exp(const HyperbolicHalfspacePoint& p, const TangentVector& v) { - - assert (N==2); - - T vNorm = v.two_norm(); - - // we compute geodesics by applying an isometry to a fixed unit-speed geodesic. - // Hence we need a unit velocity vector. - if (vNorm <= 0) - return p; - - TangentVector vUnit = v; - vUnit /= vNorm; - - // Compute the coefficients a,b,c,d of the Moebius transform that transforms - // the unit speed upward geodesic to the one through p with direction vUnit. - // We expect the Moebius transform to be an isometry, i.e. ad-bc = 1. - T cc = 1/(2*p.data_[N-1]) - vUnit[N-1] / (2*p.data_[N-1]*p.data_[N-1]); - T dd = 1/(2*p.data_[N-1]) + vUnit[N-1] / (2*p.data_[N-1]*p.data_[N-1]); - T ac = vUnit[0] / (2*p.data_[N-1]) + p.data_[0]*cc; - T bd = p.data_[0] / p.data_[N-1] - ac; - - HyperbolicHalfspacePoint result; - - // vertical part - result.data_[1] = std::exp(vNorm) / (cc*std::exp(2*vNorm) + dd); - - // horizontal part - result.data_[0] = (ac*std::exp(2*vNorm) + bd) / (cc*std::exp(2*vNorm) + dd); - - return result; - } + /** \brief The type used for global coordinates */ + typedef Dune::FieldVector<T,N> CoordinateType; - /** \brief Hyperbolic distance between two points - * - * \f dist(a,b) = arccosh ( 1 + ||a-b||^2 / (2a_n b_n) \f - */ - static T distance(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) { - - T result(0); - - for (size_t i=0; i<N; i++) - result += (a.data_[i]-b.data_[i])*(a.data_[i]-b.data_[i]); - - return std::acosh(1 + result / (2*a.data_[N-1]*b.data_[N-1])); - } + /** \brief Dimension of the manifold */ + static const int dim = N; - /** \brief Compute the gradient of the squared distance function keeping the first argument fixed + /** \brief Dimension of the Euclidean space the manifold is embedded in */ + static const int embeddedDim = N; - Unlike the distance itself the squared distance is differentiable at zero - */ - static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) { - - T diffNormSquared(0); - - for (size_t i=0; i<N; i++) - diffNormSquared += (a.data_[i]-b.data_[i])*(a.data_[i]-b.data_[i]); + /** \brief Type of a tangent vector in local coordinates */ + typedef Dune::FieldVector<T,N> TangentVector; - TangentVector result = computeDFdq(a,b,diffNormSquared); - - T x = 1 + diffNormSquared/ (2*a.data_[N-1]*b.data_[N-1]); - - result *= derivativeOfArcCosHSquared(x); + /** \brief Type of a tangent vector in the embedding space */ + typedef Dune::FieldVector<T,N> EmbeddedTangentVector; - return result; - } + /** \brief The global convexity radius of the hyberbolic plane */ + static constexpr T convexityRadius = std::numeric_limits<T>::infinity(); - /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed + /** \brief Default constructor */ + HyperbolicHalfspacePoint() + {} - Unlike the distance itself the squared distance is differentiable at zero - */ - static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) { + /** \brief Constructor from a vector. The vector gets normalized */ + HyperbolicHalfspacePoint(const Dune::FieldVector<T,N>& vector) + : data_(vector) + { + assert(vector[N-1]>0); + } - T diffNormSquared = (a.data_-b.data_).two_norm2(); + /** \brief Constructor from an array. The array gets normalized */ + HyperbolicHalfspacePoint(const std::array<T,N>& vector) + { + assert(vector.back()>0); + for (int i=0; i<N; i++) + data_[i] = vector[i]; + } - // Compute first derivative of F - Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared); + /** \brief The exponential map */ + static HyperbolicHalfspacePoint exp(const HyperbolicHalfspacePoint& p, const TangentVector& v) { - // Compute second derivatives of F - Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared); - - // - T x = 1 + diffNormSquared/ (2*a.data_[N-1]*b.data_[N-1]); - T alphaPrime = derivativeOfArcCosHSquared(x); - T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x); + assert (N==2); - // Sum it all together - Dune::FieldMatrix<T,N,N> result; - for (size_t i=0; i<N; i++) - for (size_t j=0; j<N; j++) - result[i][j] = alphaPrimePrime * dFdq[i] * dFdq[j] + alphaPrime * dFdqdq[i][j]; - - return result; - } + T vNorm = v.two_norm(); - /** \brief Compute the mixed second derivative \partial d^2 / \partial da db - - */ - static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) - { - // abbreviate notation - const Dune::FieldVector<T,N>& p = a.data_; - const Dune::FieldVector<T,N>& q = b.data_; - - T diffNormSquared = (p-q).two_norm2(); - - // Compute first derivatives of F with respect to p and q - Dune::FieldVector<T,N> dFdp = computeDFdp(a,b,diffNormSquared); - Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared); - - // Compute second derivatives of F - Dune::FieldMatrix<T,N,N> dFdpdq = computeDFdpdq(a,b,diffNormSquared); - - // - T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]); - T alphaPrime = derivativeOfArcCosHSquared(x); - T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x); - - // Sum it all together - Dune::FieldMatrix<T,N,N> result; - for (size_t i=0; i<N; i++) - for (size_t j=0; j<N; j++) - result[i][j] = alphaPrimePrime * dFdp[i] * dFdq[j] + alphaPrime * dFdpdq[i][j]; - - return result; - } - - - /** \brief Compute the third derivative \partial d^3 / \partial dq^3 - - Unlike the distance itself the squared distance is differentiable at zero - */ - static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) - { - Tensor3<T,N,N,N> result; - - // abbreviate notation - const Dune::FieldVector<T,N>& p = a.data_; - const Dune::FieldVector<T,N>& q = b.data_; - - T diffNormSquared = (p-q).two_norm2(); - - // Compute first derivative of F - Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared); - - // Compute second derivatives of F - Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared); - - // Compute third derivatives of F - Tensor3<T,N,N,N> dFdqdqdq; - - for (size_t i=0; i<N; i++) { - - for (size_t j=0; j<N; j++) { - - for (size_t k=0; k<N; k++) { - - if (i!=N-1 and j!=N-1 and k!=N-1) { - - dFdqdqdq[i][j][k] = 0; - - } else if (i!=N-1 and j!=N-1 and k==N-1) { - - dFdqdqdq[i][j][k] = -(i==j) / (p[N-1]*q[N-1]*q[N-1]); - - } else if (i!=N-1 and j==N-1 and k!=N-1) { - - dFdqdqdq[i][j][k] = -(i==k) / (p[N-1]*q[N-1]*q[N-1]); - - } else if (i!=N-1 and j==N-1 and k==N-1) { - - dFdqdqdq[i][j][k] = -2*(p[i] - q[i]) / (p[N-1]*Dune::power(q[N-1],3)); - - } else if (i==N-1 and j!=N-1 and k!=N-1) { - - dFdqdqdq[i][j][k] = - (j==k) / (p[N-1]*q[N-1]*q[N-1]); - - } else if (i==N-1 and j!=N-1 and k==N-1) { - - dFdqdqdq[i][j][k] = -2*(p[j] - q[j]) / (p[N-1]*Dune::power(q[N-1],3)); - - } else if (i==N-1 and j==N-1 and k!=N-1) { - - dFdqdqdq[i][j][k] = -2*(p[k] - q[k]) / (p[N-1]*Dune::power(q[N-1],3)); - - } else if (i==N-1 and j==N-1 and k==N-1) { - - dFdqdqdq[i][j][k] = -2/Dune::power(q[N-1],3) - 1/(p[N-1]*q[N-1]*q[N-1]) - 4*(p[N-1]-q[N-1])/(p[N-1]*Dune::power(q[N-1],3)) - - 3*diffNormSquared / (p[N-1]*Dune::power(q[N-1],4)); - - } - - } - - } - - } + // we compute geodesics by applying an isometry to a fixed unit-speed geodesic. + // Hence we need a unit velocity vector. + if (vNorm <= 0) + return p; - // - T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]); - T alphaPrime = derivativeOfArcCosHSquared(x); - T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x); - T alphaPrimePrimePrime = thirdDerivativeOfArcCosHSquared(x); - - // Sum it all together - for (size_t i=0; i<N; i++) - for (size_t j=0; j<N; j++) - for (size_t k=0; k<N; k++) - result[i][j][k] = alphaPrimePrimePrime * dFdq[i] * dFdq[j] * dFdq[k] - + alphaPrimePrime * (dFdqdq[i][j] * dFdq[k] + dFdqdq[i][k] * dFdq[j] + dFdqdq[j][k] * dFdq[i]) - + alphaPrime * dFdqdqdq[i][j][k]; - - return result; - } - - /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2 - */ - static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) - { - Tensor3<T,N,N,N> result; - - // abbreviate notation - const Dune::FieldVector<T,N>& p = a.data_; - const Dune::FieldVector<T,N>& q = b.data_; - - T diffNormSquared = (p-q).two_norm2(); - - // Compute first derivatives of F with respect to p and q - Dune::FieldVector<T,N> dFdp = computeDFdp(a,b,diffNormSquared); - Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared); - - // Compute second derivatives of F - Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared); - - Dune::FieldMatrix<T,N,N> dFdpdq = computeDFdpdq(a,b,diffNormSquared); - - // Compute third derivatives of F - Tensor3<T,N,N,N> dFdpdqdq; - - for (size_t i=0; i<N; i++) { - - for (size_t j=0; j<N; j++) { - - for (size_t k=0; k<N; k++) { - - if (i!=N-1 and j!=N-1 and k!=N-1) { - - dFdpdqdq[i][j][k] = 0; - - } else if (i!=N-1 and j!=N-1 and k==N-1) { - - dFdpdqdq[i][j][k] = (i==j) / (p[N-1]*q[N-1]*q[N-1]); - - } else if (i!=N-1 and j==N-1 and k!=N-1) { - - dFdpdqdq[i][j][k] = (i==k) / (p[N-1]*q[N-1]*q[N-1]); - - } else if (i!=N-1 and j==N-1 and k==N-1) { - - dFdpdqdq[i][j][k] = 2*(p[i] - q[i]) / (p[N-1]*Dune::power(q[N-1],3)); - - } else if (i==N-1 and j!=N-1 and k!=N-1) { - - dFdpdqdq[i][j][k] = -(j==k) / (p[N-1]*p[N-1]*q[N-1]); - - } else if (i==N-1 and j!=N-1 and k==N-1) { - - dFdpdqdq[i][j][k] = -(p[j] - q[j]) / (p[N-1]*p[N-1]*Dune::power(q[N-1],2)); - - } else if (i==N-1 and j==N-1 and k!=N-1) { - - dFdpdqdq[i][j][k] = -(p[k] - q[k]) / (p[N-1]*p[N-1]*Dune::power(q[N-1],2)); - - } else if (i==N-1 and j==N-1 and k==N-1) { - - dFdpdqdq[i][j][k] = 1.0/(p[N-1]*q[N-1]*q[N-1]) - + 2*(p[N-1]-q[N-1])/(p[N-1]*Dune::power(q[N-1],3)) - - (p[N-1]-q[N-1])/(p[N-1]*p[N-1]*q[N-1]*q[N-1]) - - diffNormSquared / (p[N-1]*p[N-1]*Dune::power(q[N-1],3)); - - } - - } - - } - - } + TangentVector vUnit = v; + vUnit /= vNorm; - // - T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]); - T alphaPrime = derivativeOfArcCosHSquared(x); - T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x); - T alphaPrimePrimePrime = thirdDerivativeOfArcCosHSquared(x); + // Compute the coefficients a,b,c,d of the Moebius transform that transforms + // the unit speed upward geodesic to the one through p with direction vUnit. + // We expect the Moebius transform to be an isometry, i.e. ad-bc = 1. + T cc = 1/(2*p.data_[N-1]) - vUnit[N-1] / (2*p.data_[N-1]*p.data_[N-1]); + T dd = 1/(2*p.data_[N-1]) + vUnit[N-1] / (2*p.data_[N-1]*p.data_[N-1]); + T ac = vUnit[0] / (2*p.data_[N-1]) + p.data_[0]*cc; + T bd = p.data_[0] / p.data_[N-1] - ac; - // Sum it all together - for (size_t i=0; i<N; i++) - for (size_t j=0; j<N; j++) - for (size_t k=0; k<N; k++) - result[i][j][k] = alphaPrimePrimePrime * dFdp[i] * dFdq[j] * dFdq[k] - + alphaPrimePrime * (dFdpdq[i][j] * dFdq[k] + dFdpdq[i][k] * dFdq[j] + dFdqdq[j][k] * dFdp[i]) - + alphaPrime * dFdpdqdq[i][j][k]; + HyperbolicHalfspacePoint result; - return result; + // vertical part + result.data_[1] = std::exp(vNorm) / (cc*std::exp(2*vNorm) + dd); - } - - - /** \brief Project tangent vector of R^n onto the tangent space. For H^m this is the identity */ - EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const { - return v; - } + // horizontal part + result.data_[0] = (ac*std::exp(2*vNorm) + bd) / (cc*std::exp(2*vNorm) + dd); - /** \brief The global coordinates, if you really want them */ - const CoordinateType& globalCoordinates() const { - return data_; - } + return result; + } - /** \brief Compute an orthonormal basis of the tangent space of H^N. - */ - Dune::FieldMatrix<T,N,N> orthonormalFrame() const { + /** \brief Hyperbolic distance between two points + * + * \f dist(a,b) = arccosh ( 1 + ||a-b||^2 / (2a_n b_n) \f + */ + static T distance(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) { - Dune::ScaledIdentityMatrix<T,N> result( data_[N-1] ); + T result(0); + + for (size_t i=0; i<N; i++) + result += (a.data_[i]-b.data_[i])*(a.data_[i]-b.data_[i]); + + return std::acosh(1 + result / (2*a.data_[N-1]*b.data_[N-1])); + } + + /** \brief Compute the gradient of the squared distance function keeping the first argument fixed + + Unlike the distance itself the squared distance is differentiable at zero + */ + static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) { + + T diffNormSquared(0); + + for (size_t i=0; i<N; i++) + diffNormSquared += (a.data_[i]-b.data_[i])*(a.data_[i]-b.data_[i]); + + TangentVector result = computeDFdq(a,b,diffNormSquared); + + T x = 1 + diffNormSquared/ (2*a.data_[N-1]*b.data_[N-1]); + + result *= derivativeOfArcCosHSquared(x); + + return result; + } + + /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed + + Unlike the distance itself the squared distance is differentiable at zero + */ + static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) { + + T diffNormSquared = (a.data_-b.data_).two_norm2(); + + // Compute first derivative of F + Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared); + + // Compute second derivatives of F + Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared); + + // + T x = 1 + diffNormSquared/ (2*a.data_[N-1]*b.data_[N-1]); + T alphaPrime = derivativeOfArcCosHSquared(x); + T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x); + + // Sum it all together + Dune::FieldMatrix<T,N,N> result; + for (size_t i=0; i<N; i++) + for (size_t j=0; j<N; j++) + result[i][j] = alphaPrimePrime * dFdq[i] * dFdq[j] + alphaPrime * dFdqdq[i][j]; + + return result; + } + + /** \brief Compute the mixed second derivative \partial d^2 / \partial da db + + */ + static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) + { + // abbreviate notation + const Dune::FieldVector<T,N>& p = a.data_; + const Dune::FieldVector<T,N>& q = b.data_; + + T diffNormSquared = (p-q).two_norm2(); + + // Compute first derivatives of F with respect to p and q + Dune::FieldVector<T,N> dFdp = computeDFdp(a,b,diffNormSquared); + Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared); + + // Compute second derivatives of F + Dune::FieldMatrix<T,N,N> dFdpdq = computeDFdpdq(a,b,diffNormSquared); + + // + T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]); + T alphaPrime = derivativeOfArcCosHSquared(x); + T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x); + + // Sum it all together + Dune::FieldMatrix<T,N,N> result; + for (size_t i=0; i<N; i++) + for (size_t j=0; j<N; j++) + result[i][j] = alphaPrimePrime * dFdp[i] * dFdq[j] + alphaPrime * dFdpdq[i][j]; + + return result; + } + + + /** \brief Compute the third derivative \partial d^3 / \partial dq^3 + + Unlike the distance itself the squared distance is differentiable at zero + */ + static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) + { + Tensor3<T,N,N,N> result; + + // abbreviate notation + const Dune::FieldVector<T,N>& p = a.data_; + const Dune::FieldVector<T,N>& q = b.data_; + + T diffNormSquared = (p-q).two_norm2(); + + // Compute first derivative of F + Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared); + + // Compute second derivatives of F + Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared); + + // Compute third derivatives of F + Tensor3<T,N,N,N> dFdqdqdq; + + for (size_t i=0; i<N; i++) { + + for (size_t j=0; j<N; j++) { + + for (size_t k=0; k<N; k++) { + + if (i!=N-1 and j!=N-1 and k!=N-1) { + + dFdqdqdq[i][j][k] = 0; + + } else if (i!=N-1 and j!=N-1 and k==N-1) { + + dFdqdqdq[i][j][k] = -(i==j) / (p[N-1]*q[N-1]*q[N-1]); + + } else if (i!=N-1 and j==N-1 and k!=N-1) { + + dFdqdqdq[i][j][k] = -(i==k) / (p[N-1]*q[N-1]*q[N-1]); + + } else if (i!=N-1 and j==N-1 and k==N-1) { + + dFdqdqdq[i][j][k] = -2*(p[i] - q[i]) / (p[N-1]*Dune::power(q[N-1],3)); + + } else if (i==N-1 and j!=N-1 and k!=N-1) { + + dFdqdqdq[i][j][k] = - (j==k) / (p[N-1]*q[N-1]*q[N-1]); + + } else if (i==N-1 and j!=N-1 and k==N-1) { + + dFdqdqdq[i][j][k] = -2*(p[j] - q[j]) / (p[N-1]*Dune::power(q[N-1],3)); + + } else if (i==N-1 and j==N-1 and k!=N-1) { + + dFdqdqdq[i][j][k] = -2*(p[k] - q[k]) / (p[N-1]*Dune::power(q[N-1],3)); + + } else if (i==N-1 and j==N-1 and k==N-1) { + + dFdqdqdq[i][j][k] = -2/Dune::power(q[N-1],3) - 1/(p[N-1]*q[N-1]*q[N-1]) - 4*(p[N-1]-q[N-1])/(p[N-1]*Dune::power(q[N-1],3)) + - 3*diffNormSquared / (p[N-1]*Dune::power(q[N-1],4)); + + } + + } + + } - return Dune::FieldMatrix<T,N,N>(result); - } - - /** \brief Scalar product of two tangent vectors */ - T metric(const TangentVector& v, const TangentVector& w) const - { - return v*w/(data_[N-1]*data_[N-1]); } - /** \brief Write unit vector object to output stream */ - friend std::ostream& operator<< (std::ostream& s, const HyperbolicHalfspacePoint& p) - { - return s << p.data_; + // + T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]); + T alphaPrime = derivativeOfArcCosHSquared(x); + T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x); + T alphaPrimePrimePrime = thirdDerivativeOfArcCosHSquared(x); + + // Sum it all together + for (size_t i=0; i<N; i++) + for (size_t j=0; j<N; j++) + for (size_t k=0; k<N; k++) + result[i][j][k] = alphaPrimePrimePrime * dFdq[i] * dFdq[j] * dFdq[k] + + alphaPrimePrime * (dFdqdq[i][j] * dFdq[k] + dFdqdq[i][k] * dFdq[j] + dFdqdq[j][k] * dFdq[i]) + + alphaPrime * dFdqdqdq[i][j][k]; + + return result; + } + + /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2 + */ + static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) + { + Tensor3<T,N,N,N> result; + + // abbreviate notation + const Dune::FieldVector<T,N>& p = a.data_; + const Dune::FieldVector<T,N>& q = b.data_; + + T diffNormSquared = (p-q).two_norm2(); + + // Compute first derivatives of F with respect to p and q + Dune::FieldVector<T,N> dFdp = computeDFdp(a,b,diffNormSquared); + Dune::FieldVector<T,N> dFdq = computeDFdq(a,b,diffNormSquared); + + // Compute second derivatives of F + Dune::FieldMatrix<T,N,N> dFdqdq = computeDFdqdq(a,b,diffNormSquared); + + Dune::FieldMatrix<T,N,N> dFdpdq = computeDFdpdq(a,b,diffNormSquared); + + // Compute third derivatives of F + Tensor3<T,N,N,N> dFdpdqdq; + + for (size_t i=0; i<N; i++) { + + for (size_t j=0; j<N; j++) { + + for (size_t k=0; k<N; k++) { + + if (i!=N-1 and j!=N-1 and k!=N-1) { + + dFdpdqdq[i][j][k] = 0; + + } else if (i!=N-1 and j!=N-1 and k==N-1) { + + dFdpdqdq[i][j][k] = (i==j) / (p[N-1]*q[N-1]*q[N-1]); + + } else if (i!=N-1 and j==N-1 and k!=N-1) { + + dFdpdqdq[i][j][k] = (i==k) / (p[N-1]*q[N-1]*q[N-1]); + + } else if (i!=N-1 and j==N-1 and k==N-1) { + + dFdpdqdq[i][j][k] = 2*(p[i] - q[i]) / (p[N-1]*Dune::power(q[N-1],3)); + + } else if (i==N-1 and j!=N-1 and k!=N-1) { + + dFdpdqdq[i][j][k] = -(j==k) / (p[N-1]*p[N-1]*q[N-1]); + + } else if (i==N-1 and j!=N-1 and k==N-1) { + + dFdpdqdq[i][j][k] = -(p[j] - q[j]) / (p[N-1]*p[N-1]*Dune::power(q[N-1],2)); + + } else if (i==N-1 and j==N-1 and k!=N-1) { + + dFdpdqdq[i][j][k] = -(p[k] - q[k]) / (p[N-1]*p[N-1]*Dune::power(q[N-1],2)); + + } else if (i==N-1 and j==N-1 and k==N-1) { + + dFdpdqdq[i][j][k] = 1.0/(p[N-1]*q[N-1]*q[N-1]) + + 2*(p[N-1]-q[N-1])/(p[N-1]*Dune::power(q[N-1],3)) + - (p[N-1]-q[N-1])/(p[N-1]*p[N-1]*q[N-1]*q[N-1]) + - diffNormSquared / (p[N-1]*p[N-1]*Dune::power(q[N-1],3)); + + } + + } + + } + } + // + T x = 1 + diffNormSquared/ (2*p[N-1]*q[N-1]); + T alphaPrime = derivativeOfArcCosHSquared(x); + T alphaPrimePrime = secondDerivativeOfArcCosHSquared(x); + T alphaPrimePrimePrime = thirdDerivativeOfArcCosHSquared(x); + + // Sum it all together + for (size_t i=0; i<N; i++) + for (size_t j=0; j<N; j++) + for (size_t k=0; k<N; k++) + result[i][j][k] = alphaPrimePrimePrime * dFdp[i] * dFdq[j] * dFdq[k] + + alphaPrimePrime * (dFdpdq[i][j] * dFdq[k] + dFdpdq[i][k] * dFdq[j] + dFdqdq[j][k] * dFdp[i]) + + alphaPrime * dFdpdqdq[i][j][k]; + + return result; + + } + + + /** \brief Project tangent vector of R^n onto the tangent space. For H^m this is the identity */ + EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const { + return v; + } + + /** \brief The global coordinates, if you really want them */ + const CoordinateType& globalCoordinates() const { + return data_; + } + + /** \brief Compute an orthonormal basis of the tangent space of H^N. + */ + Dune::FieldMatrix<T,N,N> orthonormalFrame() const { + + Dune::ScaledIdentityMatrix<T,N> result( data_[N-1] ); + + return Dune::FieldMatrix<T,N,N>(result); + } + + /** \brief Scalar product of two tangent vectors */ + T metric(const TangentVector& v, const TangentVector& w) const + { + return v*w/(data_[N-1]*data_[N-1]); + } + + /** \brief Write unit vector object to output stream */ + friend std::ostream& operator<< (std::ostream& s, const HyperbolicHalfspacePoint& p) + { + return s << p.data_; + } + private: - Dune::FieldVector<T,N> data_; + Dune::FieldVector<T,N> data_; }; #endif diff --git a/dune/gfe/spaces/orthogonalmatrix.hh b/dune/gfe/spaces/orthogonalmatrix.hh index d87effc5..71a8f76e 100644 --- a/dune/gfe/spaces/orthogonalmatrix.hh +++ b/dune/gfe/spaces/orthogonalmatrix.hh @@ -31,96 +31,96 @@ public: /** \brief A tangent vector as a vector in the surrounding coordinate space */ typedef Dune::FieldVector<T,embeddedDim> EmbeddedTangentVector; - /** \brief Default constructor -- leaves the matrix uninitialized */ - OrthogonalMatrix() - {} - - /** \brief Constructor from a general matrix - * - * It is not checked whether the matrix is really orthogonal - */ - explicit OrthogonalMatrix(const Dune::FieldMatrix<T,N,N>& matrix) + /** \brief Default constructor -- leaves the matrix uninitialized */ + OrthogonalMatrix() + {} + + /** \brief Constructor from a general matrix + * + * It is not checked whether the matrix is really orthogonal + */ + explicit OrthogonalMatrix(const Dune::FieldMatrix<T,N,N>& matrix) : data_(matrix) - {} - - /** \brief Copy constructor - * - * It is not checked whether the matrix is really orthogonal - */ - explicit OrthogonalMatrix(const CoordinateType& other) - { - DUNE_THROW(Dune::NotImplemented, "constructor"); - } - - /** \brief Const access to the matrix entries */ - const Dune::FieldMatrix<T,N,N>& data() const - { - return data_; - } - - /** \brief Project the input matrix onto the tangent space at "this" - * - * See Absil, Mahoney, Sepulche, Example 5.3.2 for the formula - * \f[ P_X \xi = X \operatorname{skew} (X^T \xi) \f] - * - */ - Dune::FieldMatrix<T,N,N> projectOntoTangentSpace(const Dune::FieldMatrix<T,N,N>& matrix) const - { - // rename to get the same notation as Absil & Co - const Dune::FieldMatrix<T,N,N>& X = data_; - - // X^T \xi - Dune::FieldMatrix<T,N,N> XTxi(0); - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - for (int k=0; k<N; k++) - XTxi[i][j] += X[k][i] * matrix[k][j]; - - // skew (X^T \xi) - Dune::FieldMatrix<T,N,N> skewXTxi(0); - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - skewXTxi[i][j] = 0.5 * ( XTxi[i][j] - XTxi[j][i] ); - - // X skew (X^T \xi) - Dune::FieldMatrix<T,N,N> result(0); - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - for (int k=0; k<N; k++) - result[i][j] += X[i][k] * skewXTxi[k][j]; - - return result; - } - - /** \brief Rebind the OrthogonalMatrix to another coordinate type */ - template<class U> - struct rebind - { - typedef OrthogonalMatrix<U,N> other; - }; - - OrthogonalMatrix& operator= (const CoordinateType& other) - { - std::size_t idx = 0; - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - data_[i][j] = other[idx++]; - return *this; - } + {} + + /** \brief Copy constructor + * + * It is not checked whether the matrix is really orthogonal + */ + explicit OrthogonalMatrix(const CoordinateType& other) + { + DUNE_THROW(Dune::NotImplemented, "constructor"); + } + + /** \brief Const access to the matrix entries */ + const Dune::FieldMatrix<T,N,N>& data() const + { + return data_; + } + + /** \brief Project the input matrix onto the tangent space at "this" + * + * See Absil, Mahoney, Sepulche, Example 5.3.2 for the formula + * \f[ P_X \xi = X \operatorname{skew} (X^T \xi) \f] + * + */ + Dune::FieldMatrix<T,N,N> projectOntoTangentSpace(const Dune::FieldMatrix<T,N,N>& matrix) const + { + // rename to get the same notation as Absil & Co + const Dune::FieldMatrix<T,N,N>& X = data_; + + // X^T \xi + Dune::FieldMatrix<T,N,N> XTxi(0); + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + for (int k=0; k<N; k++) + XTxi[i][j] += X[k][i] * matrix[k][j]; + + // skew (X^T \xi) + Dune::FieldMatrix<T,N,N> skewXTxi(0); + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + skewXTxi[i][j] = 0.5 * ( XTxi[i][j] - XTxi[j][i] ); + + // X skew (X^T \xi) + Dune::FieldMatrix<T,N,N> result(0); + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + for (int k=0; k<N; k++) + result[i][j] += X[i][k] * skewXTxi[k][j]; + + return result; + } + + /** \brief Rebind the OrthogonalMatrix to another coordinate type */ + template<class U> + struct rebind + { + typedef OrthogonalMatrix<U,N> other; + }; + + OrthogonalMatrix& operator= (const CoordinateType& other) + { + std::size_t idx = 0; + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + data_[i][j] = other[idx++]; + return *this; + } /** \brief The exponential map from a given point $p \in SO(n)$. - \param v A tangent vector. + \param v A tangent vector. */ static OrthogonalMatrix<T,3> exp(const OrthogonalMatrix<T,N>& p, const TangentVector& v) { DUNE_THROW(Dune::NotImplemented, "exp"); } - /** \brief Return the actual matrix */ - void matrix(Dune::FieldMatrix<T,N,N>& m) const + /** \brief Return the actual matrix */ + void matrix(Dune::FieldMatrix<T,N,N>& m) const { - m = data_; + m = data_; } /** \brief Project tangent vector of R^n onto the normal space space */ @@ -171,10 +171,10 @@ public: } private: - - /** \brief The actual data */ - Dune::FieldMatrix<T,N,N> data_; - + + /** \brief The actual data */ + Dune::FieldMatrix<T,N,N> data_; + }; #endif // DUNE_GFE_SPACES_ORTHOGONALMATRIX_HH diff --git a/dune/gfe/spaces/productmanifold.hh b/dune/gfe/spaces/productmanifold.hh index afc37cf9..78a7ffe0 100644 --- a/dune/gfe/spaces/productmanifold.hh +++ b/dune/gfe/spaces/productmanifold.hh @@ -32,13 +32,13 @@ namespace Dune::GFE template<class U,typename Tfirst,typename ... TargetSpaces2> struct rebindHelper { - typedef ProductManifold<typename Tfirst::template rebind<U>::other ,typename rebindHelper<U,TargetSpaces2...>::other> other; + typedef ProductManifold<typename Tfirst::template rebind<U>::other ,typename rebindHelper<U,TargetSpaces2...>::other> other; }; template<class U,typename Tlast> struct rebindHelper<U,Tlast> { - typedef typename Tlast::template rebind<U>::other other; + typedef typename Tlast::template rebind<U>::other other; }; } @@ -87,13 +87,13 @@ namespace Dune::GFE { DUNE_ASSERT_BOUNDS(globalCoordinates.size()== sumEmbeddedDim) auto constructorFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& res = std::get<0>(argsTuple)[manifoldInt]; - const auto& globalCoords =std::get<1>(argsTuple); - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(res)> >; - res = Manifold(Dune::GFE::segmentAt<Manifold::embeddedDim>(globalCoords,posHelper[0])); - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& res = std::get<0>(argsTuple)[manifoldInt]; + const auto& globalCoords =std::get<1>(argsTuple); + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(res)> >; + res = Manifold(Dune::GFE::segmentAt<Manifold::embeddedDim>(globalCoords,posHelper[0])); + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(constructorFunctor,*this,globalCoordinates); } @@ -127,15 +127,15 @@ namespace Dune::GFE { ProductManifold res; auto expFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& res = std::get<0>(argsTuple)[manifoldInt]; - const auto& p = std::get<1>(argsTuple)[manifoldInt]; - const auto& tang = std::get<2>(argsTuple); - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(res)> >; - auto currentEmbeddedTangentVector = Dune::GFE::segmentAt<Manifold::embeddedDim>(tang,posHelper[0]); - res = Manifold::exp(p,currentEmbeddedTangentVector); - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& res = std::get<0>(argsTuple)[manifoldInt]; + const auto& p = std::get<1>(argsTuple)[manifoldInt]; + const auto& tang = std::get<2>(argsTuple); + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(res)> >; + auto currentEmbeddedTangentVector = Dune::GFE::segmentAt<Manifold::embeddedDim>(tang,posHelper[0]); + res = Manifold::exp(p,currentEmbeddedTangentVector); + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(expFunctor,res,p,v); return res; } @@ -154,15 +154,15 @@ namespace Dune::GFE { EmbeddedTangentVector diff; auto logFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& res = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - const auto& b = std::get<2>(argsTuple)[manifoldInt]; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; - const auto diffLoc = Manifold::log(a,b); - std::copy(diffLoc.begin(),diffLoc.end(),res.begin()+posHelper[0]); - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& res = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + const auto& b = std::get<2>(argsTuple)[manifoldInt]; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + const auto diffLoc = Manifold::log(a,b); + std::copy(diffLoc.begin(),diffLoc.end(),res.begin()+posHelper[0]); + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(logFunctor,diff,a, b); return diff; } @@ -172,13 +172,13 @@ namespace Dune::GFE { field_type dist=0.0; auto distanceFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& res = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - const auto& b = std::get<2>(argsTuple)[manifoldInt]; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; - res += power(Manifold::distance(a,b),2); - }; + { + auto& res = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + const auto& b = std::get<2>(argsTuple)[manifoldInt]; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + res += power(Manifold::distance(a,b),2); + }; foreachManifold(distanceFunctor,dist,a, b); return sqrt(dist); } @@ -191,15 +191,15 @@ namespace Dune::GFE derivative= 0.0; auto derivOfDistSqdWRTSecArgFunctor = [] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& derivative = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - const auto& b = std::get<2>(argsTuple)[manifoldInt]; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; - const auto diffLoc = Manifold::derivativeOfDistanceSquaredWRTSecondArgument(a,b); - std::copy(diffLoc.begin(),diffLoc.end(),derivative.begin()+posHelper[0]); - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& derivative = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + const auto& b = std::get<2>(argsTuple)[manifoldInt]; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + const auto diffLoc = Manifold::derivativeOfDistanceSquaredWRTSecondArgument(a,b); + std::copy(diffLoc.begin(),diffLoc.end(),derivative.begin()+posHelper[0]); + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(derivOfDistSqdWRTSecArgFunctor, derivative,a, b); return derivative; } @@ -210,17 +210,17 @@ namespace Dune::GFE { Dune::SymmetricMatrix<field_type,embeddedDim> result; auto secDerivOfDistSqWRTSecArgFunctor = [] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& deriv = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - const auto& b = std::get<2>(argsTuple)[manifoldInt]; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; - const auto diffLoc = Manifold::secondDerivativeOfDistanceSquaredWRTSecondArgument(a,b); - for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i ) - for(size_t j=posHelper[0]; j<=i; ++j ) - deriv(i,j) = diffLoc(i - posHelper[0], j - posHelper[0]); - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& deriv = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + const auto& b = std::get<2>(argsTuple)[manifoldInt]; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + const auto diffLoc = Manifold::secondDerivativeOfDistanceSquaredWRTSecondArgument(a,b); + for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i ) + for(size_t j=posHelper[0]; j<=i; ++j ) + deriv(i,j) = diffLoc(i - posHelper[0], j - posHelper[0]); + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(secDerivOfDistSqWRTSecArgFunctor,result,a, b); return result; } @@ -232,17 +232,17 @@ namespace Dune::GFE { Dune::FieldMatrix<field_type,embeddedDim,embeddedDim> result(0); auto secDerivOfDistSqWRTFirstAndSecArgFunctor = [] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& deriv = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - const auto& b = std::get<2>(argsTuple)[manifoldInt]; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; - const auto diffLoc = Manifold::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(a,b); - for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i ) - for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j ) - deriv[i][j] = diffLoc[i - posHelper[0]][ j - posHelper[0]]; - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& deriv = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + const auto& b = std::get<2>(argsTuple)[manifoldInt]; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + const auto diffLoc = Manifold::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(a,b); + for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i ) + for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j ) + deriv[i][j] = diffLoc[i - posHelper[0]][ j - posHelper[0]]; + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(secDerivOfDistSqWRTFirstAndSecArgFunctor,result,a, b); return result; } @@ -254,18 +254,18 @@ namespace Dune::GFE { Tensor3<field_type,embeddedDim,embeddedDim,embeddedDim> result(0); auto thirdDerivOfDistSqWRTSecArgFunctor =[](auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& deriv = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - const auto& b = std::get<2>(argsTuple)[manifoldInt]; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)>>; - const auto diffLoc = Manifold::thirdDerivativeOfDistanceSquaredWRTSecondArgument(a,b); - for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i ) - for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j ) - for(size_t k=posHelper[0]; k<posHelper[0]+Manifold::embeddedDim; ++k ) - deriv[i][j][k] = diffLoc[i - posHelper[0]][ j - posHelper[0]][k - posHelper[0]]; - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& deriv = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + const auto& b = std::get<2>(argsTuple)[manifoldInt]; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + const auto diffLoc = Manifold::thirdDerivativeOfDistanceSquaredWRTSecondArgument(a,b); + for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i ) + for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j ) + for(size_t k=posHelper[0]; k<posHelper[0]+Manifold::embeddedDim; ++k ) + deriv[i][j][k] = diffLoc[i - posHelper[0]][ j - posHelper[0]][k - posHelper[0]]; + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(thirdDerivOfDistSqWRTSecArgFunctor,result,a, b); return result; } @@ -277,18 +277,18 @@ namespace Dune::GFE { Tensor3<field_type,embeddedDim,embeddedDim,embeddedDim> result(0); auto thirdDerivOfDistSqWRT1And2ArgFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& deriv = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - const auto& b = std::get<2>(argsTuple)[manifoldInt]; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)>>; - const auto diffLoc = Manifold::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(a,b); - for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i ) - for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j ) - for(size_t k=posHelper[0]; k<posHelper[0]+Manifold::embeddedDim; ++k ) - deriv[i][j][k] = diffLoc[i - posHelper[0]][ j - posHelper[0]][k - posHelper[0]]; - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& deriv = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + const auto& b = std::get<2>(argsTuple)[manifoldInt]; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + const auto diffLoc = Manifold::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(a,b); + for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::embeddedDim; ++i ) + for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j ) + for(size_t k=posHelper[0]; k<posHelper[0]+Manifold::embeddedDim; ++k ) + deriv[i][j][k] = diffLoc[i - posHelper[0]][ j - posHelper[0]][k - posHelper[0]]; + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(thirdDerivOfDistSqWRT1And2ArgFunctor,result,a, b); return result; } @@ -298,14 +298,14 @@ namespace Dune::GFE { EmbeddedTangentVector result {v}; auto projectOntoTangentSpaceFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& v = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; - const auto vLoc = a.projectOntoTangentSpace(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0])); - std::copy(vLoc.begin(),vLoc.end(),v.begin()+posHelper[0]); - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& v = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + const auto vLoc = a.projectOntoTangentSpace(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0])); + std::copy(vLoc.begin(),vLoc.end(),v.begin()+posHelper[0]); + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(projectOntoTangentSpaceFunctor,result,*this); return result; } @@ -315,14 +315,14 @@ namespace Dune::GFE { EmbeddedTangentVector result {v}; auto projectOntoNormalSpaceFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& v = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; - const auto vLoc = a.projectOntoNormalSpace(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0])); - std::copy(vLoc.begin(),vLoc.end(),v.begin()+posHelper[0]); - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& v = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + const auto vLoc = a.projectOntoNormalSpace(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0])); + std::copy(vLoc.begin(),vLoc.end(),v.begin()+posHelper[0]); + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(projectOntoNormalSpaceFunctor,result,*this); return result; } @@ -332,13 +332,13 @@ namespace Dune::GFE { ProductManifold<TargetSpaces ...> result {v}; auto projectOntoFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& res = std::get<0>(argsTuple)[manifoldInt]; - auto& v = std::get<1>(argsTuple); - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(res)> >; - res = Manifold::projectOnto(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0])); - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& res = std::get<0>(argsTuple)[manifoldInt]; + auto& v = std::get<1>(argsTuple); + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(res)> >; + res = Manifold::projectOnto(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0])); + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(projectOntoFunctor,result, v); return result; } @@ -348,17 +348,17 @@ namespace Dune::GFE { Dune::FieldMatrix<typename CoordinateType::value_type, embeddedDim, embeddedDim> result; auto derivativeOfProjectionFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& res = std::get<0>(argsTuple); - const auto& v = std::get<1>(argsTuple); - const Dune::TupleVector<TargetSpaces...> ManifoldTuple; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(ManifoldTuple[manifoldInt])> >; - const auto vLoc = Manifold::derivativeOfProjection(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0])); - for(size_t k=posHelper[0]; k<posHelper[0]+Manifold::embeddedDim; ++k ) - for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j ) - res[k][j] = vLoc[k - posHelper[0]][j-posHelper[0]]; - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& res = std::get<0>(argsTuple); + const auto& v = std::get<1>(argsTuple); + const Dune::TupleVector<TargetSpaces...> ManifoldTuple; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(ManifoldTuple[manifoldInt])> >; + const auto vLoc = Manifold::derivativeOfProjection(Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0])); + for(size_t k=posHelper[0]; k<posHelper[0]+Manifold::embeddedDim; ++k ) + for(size_t j=posHelper[0]; j<posHelper[0]+Manifold::embeddedDim; ++j ) + res[k][j] = vLoc[k - posHelper[0]][j-posHelper[0]]; + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(derivativeOfProjectionFunctor,result, v); return result; } @@ -368,17 +368,17 @@ namespace Dune::GFE { EmbeddedTangentVector result; auto weingartenFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& res = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - const auto& z = std::get<2>(argsTuple); - const auto& v = std::get<3>(argsTuple); - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; - const auto resLoc = a.weingarten(Dune::GFE::segmentAt<Manifold::embeddedDim>(z,posHelper[0]), - Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0])); - std::copy(resLoc.begin(),resLoc.end(),res.begin()+posHelper[0]); - posHelper[0] +=Manifold::embeddedDim; - }; + { + auto& res = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + const auto& z = std::get<2>(argsTuple); + const auto& v = std::get<3>(argsTuple); + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + const auto resLoc = a.weingarten(Dune::GFE::segmentAt<Manifold::embeddedDim>(z,posHelper[0]), + Dune::GFE::segmentAt<Manifold::embeddedDim>(v,posHelper[0])); + std::copy(resLoc.begin(),resLoc.end(),res.begin()+posHelper[0]); + posHelper[0] +=Manifold::embeddedDim; + }; foreachManifold(weingartenFunctor,result, *this,z, v); return result; } @@ -389,17 +389,17 @@ namespace Dune::GFE { Dune::FieldMatrix<field_type,dim,embeddedDim> result(0); auto orthonormalFrameFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& manifoldInt) - { - auto& res = std::get<0>(argsTuple); - const auto& a = std::get<1>(argsTuple)[manifoldInt]; - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; - const auto resLoc = a.orthonormalFrame(); - for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::dim; ++i ) - for(size_t j=posHelper[1]; j<posHelper[1]+Manifold::embeddedDim; ++j ) - res[i][j] = resLoc[i - posHelper[0]][j-posHelper[1]]; - posHelper[0] += Manifold::dim; - posHelper[1] += Manifold::embeddedDim; - }; + { + auto& res = std::get<0>(argsTuple); + const auto& a = std::get<1>(argsTuple)[manifoldInt]; + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(a)> >; + const auto resLoc = a.orthonormalFrame(); + for(size_t i=posHelper[0]; i<posHelper[0]+Manifold::dim; ++i ) + for(size_t j=posHelper[1]; j<posHelper[1]+Manifold::embeddedDim; ++j ) + res[i][j] = resLoc[i - posHelper[0]][j-posHelper[1]]; + posHelper[0] += Manifold::dim; + posHelper[1] += Manifold::embeddedDim; + }; foreachManifold(orthonormalFrameFunctor,result,*this); return result; } @@ -409,14 +409,14 @@ namespace Dune::GFE { CoordinateType returnValue; auto globalCoordinatesFunctor =[] (auto& argsTuple, std::array<std::size_t,2>& posHelper, const auto& i) - { - auto& res = std::get<0>(argsTuple); - const auto& p = std::get<1>(argsTuple)[i]; - const auto vLoc = p.globalCoordinates(); - using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(p)> >; - std::copy(vLoc.begin(),vLoc.end(),res.begin()+posHelper[0]); - posHelper[0] += Manifold::embeddedDim; - }; + { + auto& res = std::get<0>(argsTuple); + const auto& p = std::get<1>(argsTuple)[i]; + const auto vLoc = p.globalCoordinates(); + using Manifold = std::remove_const_t<typename std::remove_reference_t<decltype(p)> >; + std::copy(vLoc.begin(),vLoc.end(),res.begin()+posHelper[0]); + posHelper[0] += Manifold::embeddedDim; + }; foreachManifold(globalCoordinatesFunctor,returnValue,*this); return returnValue; } @@ -478,11 +478,11 @@ namespace Dune::GFE auto argsTuple = std::forward_as_tuple(args ...); std::array<std::size_t,2> posHelper({0,0}); Dune::Hybrid::forEach(Dune::Hybrid::integralRange(IC<numTS>()),[&](auto&& i) { - functor(argsTuple,posHelper,i); + functor(argsTuple,posHelper,i); }); } std::tuple<TargetSpaces ...> data_; -}; + }; } #endif diff --git a/dune/gfe/spaces/quaternion.hh b/dune/gfe/spaces/quaternion.hh index 3ac4ab1b..9d67dc5b 100644 --- a/dune/gfe/spaces/quaternion.hh +++ b/dune/gfe/spaces/quaternion.hh @@ -12,152 +12,152 @@ class Quaternion : public Dune::FieldVector<T,4> { public: - /** \brief Default constructor */ - Quaternion() {} - - /** \brief Constructor with the four components */ - Quaternion(const T& a, const T& b, const T& c, const T& d) { - - (*this)[0] = a; - (*this)[1] = b; - (*this)[2] = c; - (*this)[3] = d; - - } - - /** \brief Constructor from a single scalar */ - Quaternion(const T& a) : Dune::FieldVector<T,4>(a) {} - - /** \brief Copy constructor */ - Quaternion(const Dune::FieldVector<T,4>& other) : Dune::FieldVector<T,4>(other) {} - - /** \brief Assignment from a scalar */ - Quaternion<T>& operator=(const T& v) { - for (int i=0; i<4; i++) - (*this)[i] = v; - return (*this); - } - - /** \brief Return the identity element */ - static Quaternion<T> identity() { - Quaternion<T> id; - id[0] = 0; - id[1] = 0; - id[2] = 0; - id[3] = 1; - return id; - } - - /** \brief Right quaternion multiplication */ - Quaternion<T> mult(const Quaternion<T>& other) const { - Quaternion<T> q; - q[0] = (*this)[3]*other[0] - (*this)[2]*other[1] + (*this)[1]*other[2] + (*this)[0]*other[3]; - q[1] = (*this)[2]*other[0] + (*this)[3]*other[1] - (*this)[0]*other[2] + (*this)[1]*other[3]; - q[2] = - (*this)[1]*other[0] + (*this)[0]*other[1] + (*this)[3]*other[2] + (*this)[2]*other[3]; - q[3] = - (*this)[0]*other[0] - (*this)[1]*other[1] - (*this)[2]*other[2] + (*this)[3]*other[3]; - - return q; + /** \brief Default constructor */ + Quaternion() {} + + /** \brief Constructor with the four components */ + Quaternion(const T& a, const T& b, const T& c, const T& d) { + + (*this)[0] = a; + (*this)[1] = b; + (*this)[2] = c; + (*this)[3] = d; + + } + + /** \brief Constructor from a single scalar */ + Quaternion(const T& a) : Dune::FieldVector<T,4>(a) {} + + /** \brief Copy constructor */ + Quaternion(const Dune::FieldVector<T,4>& other) : Dune::FieldVector<T,4>(other) {} + + /** \brief Assignment from a scalar */ + Quaternion<T>& operator=(const T& v) { + for (int i=0; i<4; i++) + (*this)[i] = v; + return (*this); + } + + /** \brief Return the identity element */ + static Quaternion<T> identity() { + Quaternion<T> id; + id[0] = 0; + id[1] = 0; + id[2] = 0; + id[3] = 1; + return id; + } + + /** \brief Right quaternion multiplication */ + Quaternion<T> mult(const Quaternion<T>& other) const { + Quaternion<T> q; + q[0] = (*this)[3]*other[0] - (*this)[2]*other[1] + (*this)[1]*other[2] + (*this)[0]*other[3]; + q[1] = (*this)[2]*other[0] + (*this)[3]*other[1] - (*this)[0]*other[2] + (*this)[1]*other[3]; + q[2] = - (*this)[1]*other[0] + (*this)[0]*other[1] + (*this)[3]*other[2] + (*this)[2]*other[3]; + q[3] = - (*this)[0]*other[0] - (*this)[1]*other[1] - (*this)[2]*other[2] + (*this)[3]*other[3]; + + return q; + } + /** \brief Return the tripel of director vectors represented by a unit quaternion + + The formulas are taken from Dichmann, Li, Maddocks, (2.6.4), (2.6.5), (2.6.6) + */ + Dune::FieldVector<T,3> director(int i) const { + + Dune::FieldVector<T,3> d; + const Dune::FieldVector<T,4>& q = *this; // simpler notation + + if (i==0) { + d[0] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3]; + d[1] = 2 * (q[0]*q[1] + q[2]*q[3]); + d[2] = 2 * (q[0]*q[2] - q[1]*q[3]); + } else if (i==1) { + d[0] = 2 * (q[0]*q[1] - q[2]*q[3]); + d[1] = -q[0]*q[0] + q[1]*q[1] - q[2]*q[2] + q[3]*q[3]; + d[2] = 2 * (q[1]*q[2] + q[0]*q[3]); + } else if (i==2) { + d[0] = 2 * (q[0]*q[2] + q[1]*q[3]); + d[1] = 2 * (q[1]*q[2] - q[0]*q[3]); + d[2] = -q[0]*q[0] - q[1]*q[1] + q[2]*q[2] + q[3]*q[3]; + } else + DUNE_THROW(Dune::Exception, "Nonexisting director " << i << " requested!"); + + return d; + } + + /** \brief Turn quaternion into a unit quaternion by dividing by its Euclidean norm */ + void normalize() { + (*this) /= this->two_norm(); + } + + Dune::FieldVector<double,3> rotate(const Dune::FieldVector<double,3>& v) const { + + Dune::FieldVector<double,3> result; + Dune::FieldVector<double,3> d0 = director(0); + Dune::FieldVector<double,3> d1 = director(1); + Dune::FieldVector<double,3> d2 = director(2); + + for (int i=0; i<3; i++) + result[i] = v[0]*d0[i] + v[1]*d1[i] + v[2]*d2[i]; + + return result; + } + + /** \brief Conjugate the quaternion */ + void conjugate() { + + (*this)[0] *= -1; + (*this)[1] *= -1; + (*this)[2] *= -1; + + } + + /** \brief Invert the quaternion */ + void invert() { + + conjugate(); + (*this) /= this->two_norm2(); + + } + + /** \brief Yield the inverse quaternion */ + Quaternion<T> inverse() const { + + Quaternion<T> result = *this; + result.invert(); + return result; + } + + /** \brief Create three vectors which form an orthonormal basis of \mathbb{H} together + with this one. + + This is used to compute the strain in rod problems. + See: Dichmann, Li, Maddocks, 'Hamiltonian Formulations and Symmetries in + Rod Mechanics', page 83 + */ + Quaternion<T> B(int m) const { + assert(m>=0 && m<3); + Quaternion<T> r; + if (m==0) { + r[0] = (*this)[3]; + r[1] = (*this)[2]; + r[2] = -(*this)[1]; + r[3] = -(*this)[0]; + } else if (m==1) { + r[0] = -(*this)[2]; + r[1] = (*this)[3]; + r[2] = (*this)[0]; + r[3] = -(*this)[1]; + } else { + r[0] = (*this)[1]; + r[1] = -(*this)[0]; + r[2] = (*this)[3]; + r[3] = -(*this)[2]; } - /** \brief Return the tripel of director vectors represented by a unit quaternion - - The formulas are taken from Dichmann, Li, Maddocks, (2.6.4), (2.6.5), (2.6.6) - */ - Dune::FieldVector<T,3> director(int i) const { - - Dune::FieldVector<T,3> d; - const Dune::FieldVector<T,4>& q = *this; // simpler notation - - if (i==0) { - d[0] = q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3]; - d[1] = 2 * (q[0]*q[1] + q[2]*q[3]); - d[2] = 2 * (q[0]*q[2] - q[1]*q[3]); - } else if (i==1) { - d[0] = 2 * (q[0]*q[1] - q[2]*q[3]); - d[1] = -q[0]*q[0] + q[1]*q[1] - q[2]*q[2] + q[3]*q[3]; - d[2] = 2 * (q[1]*q[2] + q[0]*q[3]); - } else if (i==2) { - d[0] = 2 * (q[0]*q[2] + q[1]*q[3]); - d[1] = 2 * (q[1]*q[2] - q[0]*q[3]); - d[2] = -q[0]*q[0] - q[1]*q[1] + q[2]*q[2] + q[3]*q[3]; - } else - DUNE_THROW(Dune::Exception, "Nonexisting director " << i << " requested!"); - - return d; - } - - /** \brief Turn quaternion into a unit quaternion by dividing by its Euclidean norm */ - void normalize() { - (*this) /= this->two_norm(); - } - - Dune::FieldVector<double,3> rotate(const Dune::FieldVector<double,3>& v) const { - Dune::FieldVector<double,3> result; - Dune::FieldVector<double,3> d0 = director(0); - Dune::FieldVector<double,3> d1 = director(1); - Dune::FieldVector<double,3> d2 = director(2); + return r; + } - for (int i=0; i<3; i++) - result[i] = v[0]*d0[i] + v[1]*d1[i] + v[2]*d2[i]; - - return result; - } - - /** \brief Conjugate the quaternion */ - void conjugate() { - - (*this)[0] *= -1; - (*this)[1] *= -1; - (*this)[2] *= -1; - - } - - /** \brief Invert the quaternion */ - void invert() { - - conjugate(); - (*this) /= this->two_norm2(); - - } - - /** \brief Yield the inverse quaternion */ - Quaternion<T> inverse() const { - - Quaternion<T> result = *this; - result.invert(); - return result; - } - - /** \brief Create three vectors which form an orthonormal basis of \mathbb{H} together - with this one. - - This is used to compute the strain in rod problems. - See: Dichmann, Li, Maddocks, 'Hamiltonian Formulations and Symmetries in - Rod Mechanics', page 83 - */ - Quaternion<T> B(int m) const { - assert(m>=0 && m<3); - Quaternion<T> r; - if (m==0) { - r[0] = (*this)[3]; - r[1] = (*this)[2]; - r[2] = -(*this)[1]; - r[3] = -(*this)[0]; - } else if (m==1) { - r[0] = -(*this)[2]; - r[1] = (*this)[3]; - r[2] = (*this)[0]; - r[3] = -(*this)[1]; - } else { - r[0] = (*this)[1]; - r[1] = -(*this)[0]; - r[2] = (*this)[3]; - r[3] = -(*this)[2]; - } - - return r; - } - }; diff --git a/dune/gfe/spaces/realtuple.hh b/dune/gfe/spaces/realtuple.hh index 1a3f3d07..92a674e3 100644 --- a/dune/gfe/spaces/realtuple.hh +++ b/dune/gfe/spaces/realtuple.hh @@ -11,246 +11,246 @@ /** \brief Implement a tuple of real numbers as a Riemannian manifold -Currently this class only exists for testing purposes. -*/ + Currently this class only exists for testing purposes. + */ template <class T, int N> class RealTuple { public: - typedef T ctype; - typedef T field_type; - - /** \brief The type used for global coordinates */ - typedef Dune::FieldVector<T,N> CoordinateType; - - /** \brief Dimension of the manifold formed by unit vectors */ - static const int dim = N; - - /** \brief Dimension of the Euclidean space the manifold is embedded in */ - static const int embeddedDim = N; - - typedef Dune::FieldVector<T,N> EmbeddedTangentVector; - - typedef Dune::FieldVector<T,N> TangentVector; - - /** \brief The global convexity radius of the Euclidean space */ - static constexpr double convexityRadius = std::numeric_limits<double>::infinity(); - - /** \brief The return type of the derivativeOfProjection method */ - typedef Dune::ScaledIdentityMatrix<T, N> DerivativeOfProjection; - - /** \brief Default constructor */ - RealTuple() - {} - - /** \brief Construction from a scalar */ - RealTuple(T v) - { - data_ = v; - } - - /** \brief Copy constructor */ - RealTuple(const RealTuple<T,N>& other) - : data_(other.data_) - {} - - /** \brief Constructor from FieldVector*/ - RealTuple(const Dune::FieldVector<T,N>& other) - : data_(other) - {} - - RealTuple& operator=(const Dune::FieldVector<T,N>& other) { - data_ = other; - return *this; - } - - RealTuple& operator-=(const Dune::FieldVector<T,N>& other) { - data_ -= other; - return *this; - } - - template <class T2> - RealTuple& operator-=(const RealTuple<T2,N>& other) { - data_ -= other.data_; - return *this; - } - - /** \brief Assignment from RealTuple with different type -- used for automatic differentiation with ADOL-C */ - template <class T2> - RealTuple& operator <<= (const RealTuple<T2,N>& other) { - for (size_t i=0; i<N; i++) - data_[i] <<= other.data_[i]; - return *this; - } - - /** \brief Const random-access operator*/ - T operator[] (const size_t indexVariable ) const { - return data_[indexVariable]; - } - - /** \brief Rebind the RealTuple to another coordinate type */ - template<class U> - struct rebind - { - typedef RealTuple<U,N> other; - }; - - /** \brief The exponention map */ - static RealTuple exp(const RealTuple& p, const TangentVector& v) { - return RealTuple(p.data_+v); - } - - /** \brief Geodesic distance between two points - - Simply the Euclidean distance */ - static T distance(const RealTuple& a, const RealTuple& b) { - return log(a.data_,b.data_).two_norm(); - } - - /** \brief The logarithmic map - * - * \result A vector in the tangent space of a, viz: b-a - * */ - static auto log(const RealTuple& a, const RealTuple& b) { - return b.data_ - a.data_; - } + typedef T ctype; + typedef T field_type; + + /** \brief The type used for global coordinates */ + typedef Dune::FieldVector<T,N> CoordinateType; + + /** \brief Dimension of the manifold formed by unit vectors */ + static const int dim = N; + + /** \brief Dimension of the Euclidean space the manifold is embedded in */ + static const int embeddedDim = N; + + typedef Dune::FieldVector<T,N> EmbeddedTangentVector; + + typedef Dune::FieldVector<T,N> TangentVector; + + /** \brief The global convexity radius of the Euclidean space */ + static constexpr double convexityRadius = std::numeric_limits<double>::infinity(); + + /** \brief The return type of the derivativeOfProjection method */ + typedef Dune::ScaledIdentityMatrix<T, N> DerivativeOfProjection; + + /** \brief Default constructor */ + RealTuple() + {} + + /** \brief Construction from a scalar */ + RealTuple(T v) + { + data_ = v; + } + + /** \brief Copy constructor */ + RealTuple(const RealTuple<T,N>& other) + : data_(other.data_) + {} + + /** \brief Constructor from FieldVector*/ + RealTuple(const Dune::FieldVector<T,N>& other) + : data_(other) + {} + + RealTuple& operator=(const Dune::FieldVector<T,N>& other) { + data_ = other; + return *this; + } + + RealTuple& operator-=(const Dune::FieldVector<T,N>& other) { + data_ -= other; + return *this; + } + + template <class T2> + RealTuple& operator-=(const RealTuple<T2,N>& other) { + data_ -= other.data_; + return *this; + } + + /** \brief Assignment from RealTuple with different type -- used for automatic differentiation with ADOL-C */ + template <class T2> + RealTuple& operator <<= (const RealTuple<T2,N>& other) { + for (size_t i=0; i<N; i++) + data_[i] <<= other.data_[i]; + return *this; + } + + /** \brief Const random-access operator*/ + T operator[] (const size_t indexVariable ) const { + return data_[indexVariable]; + } + + /** \brief Rebind the RealTuple to another coordinate type */ + template<class U> + struct rebind + { + typedef RealTuple<U,N> other; + }; + + /** \brief The exponention map */ + static RealTuple exp(const RealTuple& p, const TangentVector& v) { + return RealTuple(p.data_+v); + } + + /** \brief Geodesic distance between two points + + Simply the Euclidean distance */ + static T distance(const RealTuple& a, const RealTuple& b) { + return log(a.data_,b.data_).two_norm(); + } + + /** \brief The logarithmic map + * + * \result A vector in the tangent space of a, viz: b-a + * */ + static auto log(const RealTuple& a, const RealTuple& b) { + return b.data_ - a.data_; + } #if ADOLC_ADOUBLE_H - /** \brief Geodesic distance squared between two points - - Simply the Euclidean distance squared */ - static adouble distanceSquared(const RealTuple<double,N>& a, const RealTuple<adouble,N>& b) { - adouble result(0.0); - for (int i=0; i<N; i++) - result += (a.globalCoordinates()[i] - b.globalCoordinates()[i]) * (a.globalCoordinates()[i] - b.globalCoordinates()[i]); - return result; - } + /** \brief Geodesic distance squared between two points + + Simply the Euclidean distance squared */ + static adouble distanceSquared(const RealTuple<double,N>& a, const RealTuple<adouble,N>& b) { + adouble result(0.0); + for (int i=0; i<N; i++) + result += (a.globalCoordinates()[i] - b.globalCoordinates()[i]) * (a.globalCoordinates()[i] - b.globalCoordinates()[i]); + return result; + } #endif - /** \brief Compute the gradient of the squared distance function keeping the first argument fixed - - Unlike the distance itself the squared distance is differentiable at zero - */ - static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) { - EmbeddedTangentVector result = a.data_; - result -= b.data_; - result *= -2; - return result; - } - - /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed + /** \brief Compute the gradient of the squared distance function keeping the first argument fixed + + Unlike the distance itself the squared distance is differentiable at zero + */ + static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) { + EmbeddedTangentVector result = a.data_; + result -= b.data_; + result *= -2; + return result; + } + + /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed - Unlike the distance itself the squared distance is differentiable at zero - */ - static Dune::SymmetricMatrix<T,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) { - - Dune::SymmetricMatrix<T,N> result; - for (int i=0; i<N; i++) - for (int j=0; j<=i; j++) - result(i,j) = 2*(i==j); - - return result; - } - - /** \brief Compute the mixed second derivate \partial d^2 / \partial da db - - Unlike the distance itself the squared distance is differentiable at zero - */ - static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const RealTuple& a, const RealTuple& b) { - - Dune::FieldMatrix<T,N,N> result; - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - result[i][j] = -2*(i==j); - - return result; - } - - /** \brief Compute the mixed third derivative \partial d^3 / \partial db^3 - - The result is the constant zero-tensor. - */ - static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) { - return Tensor3<T,N,N,N>(0); - } - - /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2 - - The result is the constant zero-tensor. - */ - static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const RealTuple& a, const RealTuple& b) { - return Tensor3<T,N,N,N>(0); - } - - /** \brief Project tangent vector of R^n onto the tangent space */ - EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const { - return v; - } - - /** \brief Project tangent vector of R^n onto the normal space space */ - EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const { - return EmbeddedTangentVector(0); - } - - /** \brief The Weingarten map */ - EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const { - return EmbeddedTangentVector(0); - } - - /** \brief Projection from the embedding space onto the manifold - * - * For RealTuples this is simply the identity map - */ - static RealTuple<T,N> projectOnto(const CoordinateType& p) - { - return RealTuple<T,N>(p); - } - - /** \brief Derivative of the projection from the embedding space onto the manifold - * - * For RealTuples this is simply the identity - */ - static DerivativeOfProjection derivativeOfProjection(const Dune::FieldVector<T,N>& p) - { - return Dune::ScaledIdentityMatrix<T,N>(1.0); - } - - /** \brief The global coordinates, if you really want them */ - const Dune::FieldVector<T,N>& globalCoordinates() const { - return data_; - } - - Dune::FieldVector<T,N>& globalCoordinates() { - return data_; - } - - - /** \brief Compute an orthonormal basis of the tangent space of R^n. - - In general this frame field, may of course not be continuous, but for RealTuples it is. - */ - Dune::FieldMatrix<T,N,N> orthonormalFrame() const { - - Dune::FieldMatrix<T,N,N> result; - - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - result[i][j] = (i==j); - return result; - } - - /** \brief Write LocalKey object to output stream */ - friend std::ostream& operator<< (std::ostream& s, const RealTuple& realTuple) - { - return s << realTuple.data_; - } + Unlike the distance itself the squared distance is differentiable at zero + */ + static Dune::SymmetricMatrix<T,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) { + + Dune::SymmetricMatrix<T,N> result; + for (int i=0; i<N; i++) + for (int j=0; j<=i; j++) + result(i,j) = 2*(i==j); + + return result; + } + + /** \brief Compute the mixed second derivate \partial d^2 / \partial da db + + Unlike the distance itself the squared distance is differentiable at zero + */ + static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const RealTuple& a, const RealTuple& b) { + + Dune::FieldMatrix<T,N,N> result; + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + result[i][j] = -2*(i==j); + + return result; + } + + /** \brief Compute the mixed third derivative \partial d^3 / \partial db^3 + + The result is the constant zero-tensor. + */ + static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const RealTuple& a, const RealTuple& b) { + return Tensor3<T,N,N,N>(0); + } + + /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2 + + The result is the constant zero-tensor. + */ + static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const RealTuple& a, const RealTuple& b) { + return Tensor3<T,N,N,N>(0); + } + + /** \brief Project tangent vector of R^n onto the tangent space */ + EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const { + return v; + } + + /** \brief Project tangent vector of R^n onto the normal space space */ + EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const { + return EmbeddedTangentVector(0); + } + + /** \brief The Weingarten map */ + EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const { + return EmbeddedTangentVector(0); + } + + /** \brief Projection from the embedding space onto the manifold + * + * For RealTuples this is simply the identity map + */ + static RealTuple<T,N> projectOnto(const CoordinateType& p) + { + return RealTuple<T,N>(p); + } + + /** \brief Derivative of the projection from the embedding space onto the manifold + * + * For RealTuples this is simply the identity + */ + static DerivativeOfProjection derivativeOfProjection(const Dune::FieldVector<T,N>& p) + { + return Dune::ScaledIdentityMatrix<T,N>(1.0); + } + + /** \brief The global coordinates, if you really want them */ + const Dune::FieldVector<T,N>& globalCoordinates() const { + return data_; + } + + Dune::FieldVector<T,N>& globalCoordinates() { + return data_; + } + + + /** \brief Compute an orthonormal basis of the tangent space of R^n. + + In general this frame field, may of course not be continuous, but for RealTuples it is. + */ + Dune::FieldMatrix<T,N,N> orthonormalFrame() const { + + Dune::FieldMatrix<T,N,N> result; + + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + result[i][j] = (i==j); + return result; + } + + /** \brief Write LocalKey object to output stream */ + friend std::ostream& operator<< (std::ostream& s, const RealTuple& realTuple) + { + return s << realTuple.data_; + } private: - Dune::FieldVector<T,N> data_; + Dune::FieldVector<T,N> data_; }; diff --git a/dune/gfe/spaces/rigidbodymotion.hh b/dune/gfe/spaces/rigidbodymotion.hh index c82c2688..e284c18f 100644 --- a/dune/gfe/spaces/rigidbodymotion.hh +++ b/dune/gfe/spaces/rigidbodymotion.hh @@ -12,420 +12,420 @@ struct RigidBodyMotion { public: - /** \brief Dimension of manifold */ - static const int dim = N + Rotation<T,N>::dim; + /** \brief Dimension of manifold */ + static const int dim = N + Rotation<T,N>::dim; - /** \brief Dimension of the embedding space */ - static const int embeddedDim = N + Rotation<T,N>::embeddedDim; + /** \brief Dimension of the embedding space */ + static const int embeddedDim = N + Rotation<T,N>::embeddedDim; - /** \brief Type of an infinitesimal rigid body motion */ - typedef Dune::FieldVector<T, dim> TangentVector; + /** \brief Type of an infinitesimal rigid body motion */ + typedef Dune::FieldVector<T, dim> TangentVector; - /** \brief Type of an infinitesimal rigid body motion */ - typedef Dune::FieldVector<T, embeddedDim> EmbeddedTangentVector; + /** \brief Type of an infinitesimal rigid body motion */ + typedef Dune::FieldVector<T, embeddedDim> EmbeddedTangentVector; - /** \brief The type used for coordinates */ - typedef T ctype; - typedef T field_type; + /** \brief The type used for coordinates */ + typedef T ctype; + typedef T field_type; - /** \brief The type used for global coordinates */ - typedef Dune::FieldVector<T,embeddedDim> CoordinateType; + /** \brief The type used for global coordinates */ + typedef Dune::FieldVector<T,embeddedDim> CoordinateType; - /** \brief The global convexity radius of the rigid body motions */ - static constexpr double convexityRadius = Rotation<T,N>::convexityRadius; + /** \brief The global convexity radius of the rigid body motions */ + static constexpr double convexityRadius = Rotation<T,N>::convexityRadius; - /** \brief Default constructor */ - RigidBodyMotion() - {} + /** \brief Default constructor */ + RigidBodyMotion() + {} - /** \brief Constructor from a translation and a rotation */ - RigidBodyMotion(const Dune::FieldVector<ctype, N>& translation, - const Rotation<ctype,N>& rotation) + /** \brief Constructor from a translation and a rotation */ + RigidBodyMotion(const Dune::FieldVector<ctype, N>& translation, + const Rotation<ctype,N>& rotation) : r(translation), q(rotation) - {} + {} - RigidBodyMotion(const CoordinateType& globalCoordinates) - { - for (int i=0; i<N; i++) - r[i] = globalCoordinates[i]; + RigidBodyMotion(const CoordinateType& globalCoordinates) + { + for (int i=0; i<N; i++) + r[i] = globalCoordinates[i]; - for (int i=N; i<embeddedDim; i++) - q[i-N] = globalCoordinates[i]; + for (int i=N; i<embeddedDim; i++) + q[i-N] = globalCoordinates[i]; - // Turn this into a unit quaternion if it isn't already - q.normalize(); - } + // Turn this into a unit quaternion if it isn't already + q.normalize(); + } - /** \brief Assignment from RigidBodyMotion with different type -- used for automatic differentiation with ADOL-C */ - template <class T2> - RigidBodyMotion& operator <<= (const RigidBodyMotion<T2,N>& other) { - for (int i=0; i<N; i++) - r[i] <<= other.r[i]; - q <<= other.q; - return *this; - } + /** \brief Assignment from RigidBodyMotion with different type -- used for automatic differentiation with ADOL-C */ + template <class T2> + RigidBodyMotion& operator <<= (const RigidBodyMotion<T2,N>& other) { + for (int i=0; i<N; i++) + r[i] <<= other.r[i]; + q <<= other.q; + return *this; + } - /** \brief Rebind the RigidBodyMotion to another coordinate type */ - template<class U> - struct rebind - { - typedef RigidBodyMotion<U,N> other; - }; + /** \brief Rebind the RigidBodyMotion to another coordinate type */ + template<class U> + struct rebind + { + typedef RigidBodyMotion<U,N> other; + }; - /** \brief The exponential map from a given point $p \in SE(d)$. + /** \brief The exponential map from a given point $p \in SE(d)$. Why the template parameter? Well, it should work with both TangentVector and EmbeddedTangentVector. In general these differ and we could just have two exp methods. However in 2d they do _not_ differ, and then the compiler complains about having two methods with the same signature. - */ - template <class TVector> - static RigidBodyMotion<ctype,N> exp(const RigidBodyMotion<ctype,N>& p, const TVector& v) { - - RigidBodyMotion<ctype,N> result; - - // Add translational correction - for (int i=0; i<N; i++) - result.r[i] = p.r[i] + v[i]; - - // Add rotational correction - typedef typename std::conditional<std::is_same<TVector,TangentVector>::value, - typename Rotation<ctype,N>::TangentVector, - typename Rotation<ctype,N>::EmbeddedTangentVector>::type RotationTangentVector; - RotationTangentVector qCorr; - for (int i=0; i<RotationTangentVector::dimension; i++) - qCorr[i] = v[N+i]; - - result.q = Rotation<ctype,N>::exp(p.q, qCorr); - return result; - } + */ + template <class TVector> + static RigidBodyMotion<ctype,N> exp(const RigidBodyMotion<ctype,N>& p, const TVector& v) { + + RigidBodyMotion<ctype,N> result; + + // Add translational correction + for (int i=0; i<N; i++) + result.r[i] = p.r[i] + v[i]; + + // Add rotational correction + typedef typename std::conditional<std::is_same<TVector,TangentVector>::value, + typename Rotation<ctype,N>::TangentVector, + typename Rotation<ctype,N>::EmbeddedTangentVector>::type RotationTangentVector; + RotationTangentVector qCorr; + for (int i=0; i<RotationTangentVector::dimension; i++) + qCorr[i] = v[N+i]; + + result.q = Rotation<ctype,N>::exp(p.q, qCorr); + return result; + } - /** \brief Compute difference vector from a to b on the tangent space of a */ - static EmbeddedTangentVector log(const RigidBodyMotion<ctype,N>& a, - const RigidBodyMotion<ctype,N>& b) - { - EmbeddedTangentVector result; + /** \brief Compute difference vector from a to b on the tangent space of a */ + static EmbeddedTangentVector log(const RigidBodyMotion<ctype,N>& a, + const RigidBodyMotion<ctype,N>& b) + { + EmbeddedTangentVector result; - // Usual linear difference - for (int i=0; i<N; i++) - result[i] = b.r[i] - a.r[i]; + // Usual linear difference + for (int i=0; i<N; i++) + result[i] = b.r[i] - a.r[i]; - // Subtract orientations on the tangent space of 'a' - typename Rotation<ctype,N>::EmbeddedTangentVector v = Rotation<ctype,N>::log(a.q, b.q); + // Subtract orientations on the tangent space of 'a' + typename Rotation<ctype,N>::EmbeddedTangentVector v = Rotation<ctype,N>::log(a.q, b.q); - // Compute difference on T_a SO(3) - for (int i=0; i<Rotation<ctype,N>::EmbeddedTangentVector::dimension; i++) - result[i+N] = v[i]; + // Compute difference on T_a SO(3) + for (int i=0; i<Rotation<ctype,N>::EmbeddedTangentVector::dimension; i++) + result[i+N] = v[i]; - return result; - } + return result; + } - /** \brief Compute geodesic distance from a to b */ - static T distance(const RigidBodyMotion<ctype,N>& a, const RigidBodyMotion<ctype,N>& b) { + /** \brief Compute geodesic distance from a to b */ + static T distance(const RigidBodyMotion<ctype,N>& a, const RigidBodyMotion<ctype,N>& b) { - T euclideanDistanceSquared = (a.r - b.r).two_norm2(); + T euclideanDistanceSquared = (a.r - b.r).two_norm2(); - T rotationDistance = Rotation<ctype,N>::distance(a.q, b.q); + T rotationDistance = Rotation<ctype,N>::distance(a.q, b.q); - return std::sqrt(euclideanDistanceSquared + rotationDistance*rotationDistance); - } + return std::sqrt(euclideanDistanceSquared + rotationDistance*rotationDistance); + } - /** \brief Compute difference vector from a to b on the tangent space of a + /** \brief Compute difference vector from a to b on the tangent space of a - * \warning The method is buggy! See https://gitlab.mn.tu-dresden.de/osander/dune-gfe/-/merge_requests/2 - * \deprecated Use the log method instead! - */ - [[deprecated("Use RigidBodyMotion::log instead of RigidBodyMotion::difference!")]] - static TangentVector difference(const RigidBodyMotion<ctype,N>& a, - const RigidBodyMotion<ctype,N>& b) { + * \warning The method is buggy! See https://gitlab.mn.tu-dresden.de/osander/dune-gfe/-/merge_requests/2 + * \deprecated Use the log method instead! + */ + [[deprecated("Use RigidBodyMotion::log instead of RigidBodyMotion::difference!")]] + static TangentVector difference(const RigidBodyMotion<ctype,N>& a, + const RigidBodyMotion<ctype,N>& b) { - TangentVector result; + TangentVector result; - // Usual linear difference - for (int i=0; i<N; i++) - result[i] = a.r[i] - b.r[i]; + // Usual linear difference + for (int i=0; i<N; i++) + result[i] = a.r[i] - b.r[i]; - // Subtract orientations on the tangent space of 'a' - typename Rotation<ctype,N>::TangentVector v = Rotation<ctype,N>::difference(a.q, b.q).axial(); + // Subtract orientations on the tangent space of 'a' + typename Rotation<ctype,N>::TangentVector v = Rotation<ctype,N>::difference(a.q, b.q).axial(); - // Compute difference on T_a SO(3) - for (int i=0; i<Rotation<ctype,N>::TangentVector::dimension; i++) - result[i+N] = v[i]; + // Compute difference on T_a SO(3) + for (int i=0; i<Rotation<ctype,N>::TangentVector::dimension; i++) + result[i+N] = v[i]; - return result; - } + return result; + } - static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const RigidBodyMotion<ctype,N>& a, - const RigidBodyMotion<ctype,N>& b) { + static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const RigidBodyMotion<ctype,N>& a, + const RigidBodyMotion<ctype,N>& b) { - // linear part - Dune::FieldVector<ctype,N> linearDerivative = a.r; - linearDerivative -= b.r; - linearDerivative *= -2; + // linear part + Dune::FieldVector<ctype,N> linearDerivative = a.r; + linearDerivative -= b.r; + linearDerivative *= -2; - // rotation part - typename Rotation<ctype,N>::EmbeddedTangentVector rotationDerivative - = Rotation<ctype,N>::derivativeOfDistanceSquaredWRTSecondArgument(a.q, b.q); + // rotation part + typename Rotation<ctype,N>::EmbeddedTangentVector rotationDerivative + = Rotation<ctype,N>::derivativeOfDistanceSquaredWRTSecondArgument(a.q, b.q); - return concat(linearDerivative, rotationDerivative); - } + return concat(linearDerivative, rotationDerivative); + } - /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed */ - static Dune::SymmetricMatrix<T,embeddedDim> secondDerivativeOfDistanceSquaredWRTSecondArgument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q) + /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed */ + static Dune::SymmetricMatrix<T,embeddedDim> secondDerivativeOfDistanceSquaredWRTSecondArgument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q) + { + Dune::SymmetricMatrix<T,embeddedDim> result; + + // The linear part + Dune::SymmetricMatrix<T,N> linearPart = RealTuple<T,N>::secondDerivativeOfDistanceSquaredWRTSecondArgument(p.r,q.r); + for (int i=0; i<N; i++) + for (int j=0; j<=i; j++) + result(i,j) = linearPart(i,j); + + // The rotation part + Dune::SymmetricMatrix<T,Rotation<T,N>::embeddedDim> rotationPart + = Rotation<ctype,N>::secondDerivativeOfDistanceSquaredWRTSecondArgument(p.q,q.q); + for (int i=0; i<Rotation<T,N>::embeddedDim; i++) { - Dune::SymmetricMatrix<T,embeddedDim> result; - - // The linear part - Dune::SymmetricMatrix<T,N> linearPart = RealTuple<T,N>::secondDerivativeOfDistanceSquaredWRTSecondArgument(p.r,q.r); - for (int i=0; i<N; i++) - for (int j=0; j<=i; j++) - result(i,j) = linearPart(i,j); - - // The rotation part - Dune::SymmetricMatrix<T,Rotation<T,N>::embeddedDim> rotationPart - = Rotation<ctype,N>::secondDerivativeOfDistanceSquaredWRTSecondArgument(p.q,q.q); - for (int i=0; i<Rotation<T,N>::embeddedDim; i++) - { - for (int j=0; j<N; j++) - result(N+i,j) = 0; - for (int j=0; j<=i; j++) - result(N+i,N+j) = rotationPart(i,j); - } - - return result; + for (int j=0; j<N; j++) + result(N+i,j) = 0; + for (int j=0; j<=i; j++) + result(N+i,N+j) = rotationPart(i,j); } - /** \brief Compute the mixed second derivate \partial d^2 / \partial da db + return result; + } - Unlike the distance itself the squared distance is differentiable at zero - */ - static Dune::FieldMatrix<T,embeddedDim,embeddedDim> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q) - { - Dune::FieldMatrix<T,embeddedDim,embeddedDim> result(0); - - // The linear part - Dune::FieldMatrix<T,N,N> linearPart = RealTuple<T,N>::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(p.r,q.r); - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - result[i][j] = linearPart[i][j]; - - // The rotation part - Dune::FieldMatrix<T,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim> rotationPart - = Rotation<ctype,N>::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(p.q,q.q); - for (int i=0; i<Rotation<T,N>::embeddedDim; i++) - for (int j=0; j<Rotation<T,N>::embeddedDim; j++) - result[N+i][N+j] = rotationPart[i][j]; - - return result; - } + /** \brief Compute the mixed second derivate \partial d^2 / \partial da db - /** \brief Compute the third derivative \partial d^3 / \partial dq^3 + Unlike the distance itself the squared distance is differentiable at zero + */ + static Dune::FieldMatrix<T,embeddedDim,embeddedDim> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q) + { + Dune::FieldMatrix<T,embeddedDim,embeddedDim> result(0); + + // The linear part + Dune::FieldMatrix<T,N,N> linearPart = RealTuple<T,N>::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(p.r,q.r); + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + result[i][j] = linearPart[i][j]; + + // The rotation part + Dune::FieldMatrix<T,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim> rotationPart + = Rotation<ctype,N>::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(p.q,q.q); + for (int i=0; i<Rotation<T,N>::embeddedDim; i++) + for (int j=0; j<Rotation<T,N>::embeddedDim; j++) + result[N+i][N+j] = rotationPart[i][j]; + + return result; + } - Unlike the distance itself the squared distance is differentiable at zero - */ - static Tensor3<T,embeddedDim,embeddedDim,embeddedDim> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q) - { - Tensor3<T,embeddedDim,embeddedDim,embeddedDim> result(0); + /** \brief Compute the third derivative \partial d^3 / \partial dq^3 + + Unlike the distance itself the squared distance is differentiable at zero + */ + static Tensor3<T,embeddedDim,embeddedDim,embeddedDim> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q) + { + Tensor3<T,embeddedDim,embeddedDim,embeddedDim> result(0); - // The linear part - Tensor3<T,N,N,N> linearPart = RealTuple<T,N>::thirdDerivativeOfDistanceSquaredWRTSecondArgument(p.r,q.r); - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - for (int k=0; k<N; k++) - result[i][j][k] = linearPart[i][j][k]; + // The linear part + Tensor3<T,N,N,N> linearPart = RealTuple<T,N>::thirdDerivativeOfDistanceSquaredWRTSecondArgument(p.r,q.r); + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + for (int k=0; k<N; k++) + result[i][j][k] = linearPart[i][j][k]; - // The rotation part - Tensor3<T,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim> rotationPart - = Rotation<ctype,N>::thirdDerivativeOfDistanceSquaredWRTSecondArgument(p.q,q.q); + // The rotation part + Tensor3<T,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim> rotationPart + = Rotation<ctype,N>::thirdDerivativeOfDistanceSquaredWRTSecondArgument(p.q,q.q); - for (int i=0; i<Rotation<T,N>::embeddedDim; i++) - for (int j=0; j<Rotation<T,N>::embeddedDim; j++) - for (int k=0; k<Rotation<T,N>::embeddedDim; k++) - result[N+i][N+j][N+k] = rotationPart[i][j][k]; + for (int i=0; i<Rotation<T,N>::embeddedDim; i++) + for (int j=0; j<Rotation<T,N>::embeddedDim; j++) + for (int k=0; k<Rotation<T,N>::embeddedDim; k++) + result[N+i][N+j][N+k] = rotationPart[i][j][k]; - return result; - } + return result; + } - /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2 + /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2 - Unlike the distance itself the squared distance is differentiable at zero - */ - static Tensor3<T,embeddedDim,embeddedDim,embeddedDim> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q) - { - Tensor3<T,embeddedDim,embeddedDim,embeddedDim> result(0); - - // The linear part - Tensor3<T,N,N,N> linearPart = RealTuple<T,N>::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(p.r,q.r); - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - for (int k=0; k<N; k++) - result[i][j][k] = linearPart[i][j][k]; - - // The rotation part - Tensor3<T,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim> rotationPart = Rotation<ctype,N>::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(p.q,q.q); - for (int i=0; i<Rotation<T,N>::embeddedDim; i++) - for (int j=0; j<Rotation<T,N>::embeddedDim; j++) - for (int k=0; k<Rotation<T,N>::embeddedDim; k++) - result[N+i][N+j][N+k] = rotationPart[i][j][k]; - - return result; - } + Unlike the distance itself the squared distance is differentiable at zero + */ + static Tensor3<T,embeddedDim,embeddedDim,embeddedDim> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const RigidBodyMotion<ctype,N> & p, const RigidBodyMotion<ctype,N> & q) + { + Tensor3<T,embeddedDim,embeddedDim,embeddedDim> result(0); + + // The linear part + Tensor3<T,N,N,N> linearPart = RealTuple<T,N>::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(p.r,q.r); + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + for (int k=0; k<N; k++) + result[i][j][k] = linearPart[i][j][k]; + + // The rotation part + Tensor3<T,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim,Rotation<T,N>::embeddedDim> rotationPart = Rotation<ctype,N>::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(p.q,q.q); + for (int i=0; i<Rotation<T,N>::embeddedDim; i++) + for (int j=0; j<Rotation<T,N>::embeddedDim; j++) + for (int k=0; k<Rotation<T,N>::embeddedDim; k++) + result[N+i][N+j][N+k] = rotationPart[i][j][k]; + + return result; + } - /** \brief Project tangent vector of R^n onto the tangent space */ - EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const { - EmbeddedTangentVector result; + /** \brief Project tangent vector of R^n onto the tangent space */ + EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const { + EmbeddedTangentVector result; - // translation part - for (int i=0; i<N; i++) - result[i] = v[i]; + // translation part + for (int i=0; i<N; i++) + result[i] = v[i]; - // rotation part - typename Rotation<T,N>::EmbeddedTangentVector rotV; - for (int i=0; i<Rotation<T,N>::embeddedDim; i++) - rotV[i] = v[i+N]; + // rotation part + typename Rotation<T,N>::EmbeddedTangentVector rotV; + for (int i=0; i<Rotation<T,N>::embeddedDim; i++) + rotV[i] = v[i+N]; - rotV = q.projectOntoTangentSpace(rotV); + rotV = q.projectOntoTangentSpace(rotV); - for (int i=0; i<Rotation<T,N>::embeddedDim; i++) - result[i+N] = rotV[i]; + for (int i=0; i<Rotation<T,N>::embeddedDim; i++) + result[i+N] = rotV[i]; - return result; - } + return result; + } - /** \brief Project tangent vector of R^n onto the normal space space */ - EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const { + /** \brief Project tangent vector of R^n onto the normal space space */ + EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const { - EmbeddedTangentVector result; + EmbeddedTangentVector result; - // translation part - for (int i=0; i<N; i++) - result[i] = 0; + // translation part + for (int i=0; i<N; i++) + result[i] = 0; - // rotation part - T sp = 0; - for (int i=0; i<Rotation<T,N>::embeddedDim; i++) - sp += v[i+N] * q[i]; + // rotation part + T sp = 0; + for (int i=0; i<Rotation<T,N>::embeddedDim; i++) + sp += v[i+N] * q[i]; - for (int i=0; i<Rotation<T,N>::embeddedDim; i++) - result[i+N] = sp * q[i]; + for (int i=0; i<Rotation<T,N>::embeddedDim; i++) + result[i+N] = sp * q[i]; - return result; - } + return result; + } - /** \brief Transform tangent vectors from quaternion representation to matrix representation - * - * This class represents rotations as unit quaternions, and therefore variations of rotations - * (i.e., tangent vector) are represented in quaternion space, too. However, some applications - * require the tangent vectors as matrices. To obtain matrix coordinates we use the - * chain rule, which means that we have to multiply the given derivative with - * the derivative of the embedding of the unit quaternion into the space of 3x3 matrices. - * This second derivative is almost given by the method getFirstDerivativesOfDirectors. - * However, since the directors of a given unit quaternion are the _columns_ of the - * corresponding orthogonal matrix, we need to invert the i and j indices - * - * As a typical GFE assembler will require this for several tangent vectors at once, - * the implementation here is a vector one: It allows to treat several tangent vectors - * together, which have to come as the columns of the `derivative` parameter. - * - * So, if I am not mistaken, result[i][j][k] contains \partial R_ij / \partial k - * - * \param derivative Tangent vector in quaternion coordinates - * \returns DR Tangent vector in matrix coordinates - */ - template <int blocksize> - Tensor3<T,3,3,blocksize> quaternionTangentToMatrixTangent(const Dune::FieldMatrix<T,7,blocksize>& derivative) const - { - Tensor3<T,3,3,blocksize> result = T(0); + /** \brief Transform tangent vectors from quaternion representation to matrix representation + * + * This class represents rotations as unit quaternions, and therefore variations of rotations + * (i.e., tangent vector) are represented in quaternion space, too. However, some applications + * require the tangent vectors as matrices. To obtain matrix coordinates we use the + * chain rule, which means that we have to multiply the given derivative with + * the derivative of the embedding of the unit quaternion into the space of 3x3 matrices. + * This second derivative is almost given by the method getFirstDerivativesOfDirectors. + * However, since the directors of a given unit quaternion are the _columns_ of the + * corresponding orthogonal matrix, we need to invert the i and j indices + * + * As a typical GFE assembler will require this for several tangent vectors at once, + * the implementation here is a vector one: It allows to treat several tangent vectors + * together, which have to come as the columns of the `derivative` parameter. + * + * So, if I am not mistaken, result[i][j][k] contains \partial R_ij / \partial k + * + * \param derivative Tangent vector in quaternion coordinates + * \returns DR Tangent vector in matrix coordinates + */ + template <int blocksize> + Tensor3<T,3,3,blocksize> quaternionTangentToMatrixTangent(const Dune::FieldMatrix<T,7,blocksize>& derivative) const + { + Tensor3<T,3,3,blocksize> result = T(0); - Tensor3<T,3 , 3, 4> dd_dq; - q.getFirstDerivativesOfDirectors(dd_dq); + Tensor3<T,3 , 3, 4> dd_dq; + q.getFirstDerivativesOfDirectors(dd_dq); - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) - for (int k=0; k<blocksize; k++) - for (int l=0; l<4; l++) - result[i][j][k] += dd_dq[j][i][l] * derivative[l+3][k]; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + for (int k=0; k<blocksize; k++) + for (int l=0; l<4; l++) + result[i][j][k] += dd_dq[j][i][l] * derivative[l+3][k]; - return result; - } + return result; + } - /** \brief The Weingarten map */ - EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const { + /** \brief The Weingarten map */ + EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const { - EmbeddedTangentVector result; + EmbeddedTangentVector result; - // translation part: nothing, the space is flat - for (int i=0; i<N; i++) - result[i] = 0; + // translation part: nothing, the space is flat + for (int i=0; i<N; i++) + result[i] = 0; - // rotation part - T sp = 0; - for (int i=0; i<Rotation<T,N>::embeddedDim; i++) - sp += v[i+N] * q[i]; + // rotation part + T sp = 0; + for (int i=0; i<Rotation<T,N>::embeddedDim; i++) + sp += v[i+N] * q[i]; - for (int i=0; i<Rotation<T,N>::embeddedDim; i++) - result[i+N] = -sp * z[i+N]; + for (int i=0; i<Rotation<T,N>::embeddedDim; i++) + result[i+N] = -sp * z[i+N]; - return result; - } + return result; + } - /** \brief Compute an orthonormal basis of the tangent space of SE(3). + /** \brief Compute an orthonormal basis of the tangent space of SE(3). - This basis may not be globally continuous. - */ - Dune::FieldMatrix<T,dim,embeddedDim> orthonormalFrame() const { - Dune::FieldMatrix<T,dim,embeddedDim> result(0); + This basis may not be globally continuous. + */ + Dune::FieldMatrix<T,dim,embeddedDim> orthonormalFrame() const { + Dune::FieldMatrix<T,dim,embeddedDim> result(0); - // Get the R^d part - for (int i=0; i<N; i++) - result[i][i] = 1; + // Get the R^d part + for (int i=0; i<N; i++) + result[i][i] = 1; - Dune::FieldMatrix<T,Rotation<T,N>::dim,Rotation<T,N>::embeddedDim> SO3Part = q.orthonormalFrame(); + Dune::FieldMatrix<T,Rotation<T,N>::dim,Rotation<T,N>::embeddedDim> SO3Part = q.orthonormalFrame(); - for (int i=0; i<Rotation<T,N>::dim; i++) - for (int j=0; j<Rotation<T,N>::embeddedDim; j++) - result[N+i][N+j] = SO3Part[i][j]; + for (int i=0; i<Rotation<T,N>::dim; i++) + for (int j=0; j<Rotation<T,N>::embeddedDim; j++) + result[N+i][N+j] = SO3Part[i][j]; - return result; - } + return result; + } - /** \brief The global coordinates, if you really want them */ - CoordinateType globalCoordinates() const { - return concat(r, q.globalCoordinates()); - } + /** \brief The global coordinates, if you really want them */ + CoordinateType globalCoordinates() const { + return concat(r, q.globalCoordinates()); + } - // Translational part - Dune::FieldVector<ctype, N> r; + // Translational part + Dune::FieldVector<ctype, N> r; - // Rotational part - Rotation<ctype,N> q; + // Rotational part + Rotation<ctype,N> q; private: - /** \brief Concatenate two FieldVectors */ - template <int NN, int M> - static Dune::FieldVector<ctype,NN+M> concat(const Dune::FieldVector<ctype,NN>& a, - const Dune::FieldVector<ctype,M>& b) - { - Dune::FieldVector<ctype,NN+M> result; - for (int i=0; i<NN; i++) - result[i] = a[i]; - for (int i=0; i<M; i++) - result[i+NN] = b[i]; - return result; - } + /** \brief Concatenate two FieldVectors */ + template <int NN, int M> + static Dune::FieldVector<ctype,NN+M> concat(const Dune::FieldVector<ctype,NN>& a, + const Dune::FieldVector<ctype,M>& b) + { + Dune::FieldVector<ctype,NN+M> result; + for (int i=0; i<NN; i++) + result[i] = a[i]; + for (int i=0; i<M; i++) + result[i+NN] = b[i]; + return result; + } }; //! Send configuration to output stream template <int N, class ctype> std::ostream& operator<< (std::ostream& s, const RigidBodyMotion<ctype,N>& c) - { - s << "(" << c.r << ") (" << c.q << ")"; - return s; - } +{ + s << "(" << c.r << ") (" << c.q << ")"; + return s; +} #endif diff --git a/dune/gfe/spaces/rotation.hh b/dune/gfe/spaces/rotation.hh index fce1ce78..271fc228 100644 --- a/dune/gfe/spaces/rotation.hh +++ b/dune/gfe/spaces/rotation.hh @@ -3,7 +3,7 @@ /** \file \brief Define rotations in Euclidean spaces -*/ + */ #include <array> @@ -20,1233 +20,1231 @@ template <class T, int dim> class Rotation -{ - -}; +{}; /** \brief Specialization for dim==2 \tparam T The type used for coordinates -*/ + */ template <class T> class Rotation<T,2> { public: - /** \brief The type used for coordinates */ - typedef T ctype; + /** \brief The type used for coordinates */ + typedef T ctype; - /** \brief Dimension of the manifold formed by the 2d rotations */ - static const int dim = 1; + /** \brief Dimension of the manifold formed by the 2d rotations */ + static const int dim = 1; - /** \brief Coordinates are embedded in the euclidean space */ - static const int embeddedDim = 1; + /** \brief Coordinates are embedded in the euclidean space */ + static const int embeddedDim = 1; - /** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix */ - typedef Dune::FieldVector<T,1> TangentVector; + /** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix */ + typedef Dune::FieldVector<T,1> TangentVector; - /** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix + /** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix - This vector is not really embedded in anything. I have to make my notation more consistent! */ - typedef Dune::FieldVector<T,1> EmbeddedTangentVector; + This vector is not really embedded in anything. I have to make my notation more consistent! */ + typedef Dune::FieldVector<T,1> EmbeddedTangentVector; - /** \brief The global convexity radius of the rotation group */ - static constexpr double convexityRadius = 0.5 * M_PI; + /** \brief The global convexity radius of the rotation group */ + static constexpr double convexityRadius = 0.5 * M_PI; - /** \brief Default constructor, create the identity rotation */ - Rotation() - : angle_(0) - {} + /** \brief Default constructor, create the identity rotation */ + Rotation() + : angle_(0) + {} - Rotation(const T& angle) - : angle_(angle) - {} + Rotation(const T& angle) + : angle_(angle) + {} - /** \brief Return the identity element */ - static Rotation<T,2> identity() { - // Default constructor creates an identity - Rotation<T,2> id; - return id; - } + /** \brief Return the identity element */ + static Rotation<T,2> identity() { + // Default constructor creates an identity + Rotation<T,2> id; + return id; + } - static T distance(const Rotation<T,2>& a, const Rotation<T,2>& b) { - T dist = a.angle_ - b.angle_; - while (dist < 0) - dist += 2*M_PI; - while (dist > 2*M_PI) - dist -= 2*M_PI; + static T distance(const Rotation<T,2>& a, const Rotation<T,2>& b) { + T dist = a.angle_ - b.angle_; + while (dist < 0) + dist += 2*M_PI; + while (dist > 2*M_PI) + dist -= 2*M_PI; - return (dist <= M_PI) ? dist : 2*M_PI - dist; - } + return (dist <= M_PI) ? dist : 2*M_PI - dist; + } - /** \brief The exponential map from a given point $p \in SO(3)$. */ - static Rotation<T,2> exp(const Rotation<T,2>& p, const TangentVector& v) { - Rotation<T,2> result = p; - result.angle_ += v; - return result; - } + /** \brief The exponential map from a given point $p \in SO(3)$. */ + static Rotation<T,2> exp(const Rotation<T,2>& p, const TangentVector& v) { + Rotation<T,2> result = p; + result.angle_ += v; + return result; + } - /** \brief The exponential map from \f$ \mathfrak{so}(2) \f$ to \f$ SO(2) \f$ - */ - static Rotation<T,2> exp(const Dune::FieldVector<T,1>& v) { - Rotation<T,2> result; - result.angle_ = v[0]; - return result; - } + /** \brief The exponential map from \f$ \mathfrak{so}(2) \f$ to \f$ SO(2) \f$ + */ + static Rotation<T,2> exp(const Dune::FieldVector<T,1>& v) { + Rotation<T,2> result; + result.angle_ = v[0]; + return result; + } - static TangentVector derivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,2>& a, - const Rotation<T,2>& b) { - // This assertion is here to remind me of the following laziness: - // The difference has to be computed modulo 2\pi - assert( std::abs(a.angle_ - b.angle_) <= M_PI ); - return -2 * (a.angle_ - b.angle_); - } + static TangentVector derivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,2>& a, + const Rotation<T,2>& b) { + // This assertion is here to remind me of the following laziness: + // The difference has to be computed modulo 2\pi + assert( std::abs(a.angle_ - b.angle_) <= M_PI ); + return -2 * (a.angle_ - b.angle_); + } - static Dune::FieldMatrix<T,1,1> secondDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,2>& a, - const Rotation<T,2>& b) { - return 2; - } + static Dune::FieldMatrix<T,1,1> secondDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,2>& a, + const Rotation<T,2>& b) { + return 2; + } - /** \brief Right multiplication */ - Rotation<T,2> mult(const Rotation<T,2>& other) const { - Rotation<T,2> q = *this; - q.angle_ += other.angle_; - return q; - } + /** \brief Right multiplication */ + Rotation<T,2> mult(const Rotation<T,2>& other) const { + Rotation<T,2> q = *this; + q.angle_ += other.angle_; + return q; + } - /** \brief Compute an orthonormal basis of the tangent space of SO(3). + /** \brief Compute an orthonormal basis of the tangent space of SO(3). - This basis is of course not globally continuous. - */ - Dune::FieldMatrix<T,1,1> orthonormalFrame() const { - return Dune::FieldMatrix<T,1,1>(1); - } + This basis is of course not globally continuous. + */ + Dune::FieldMatrix<T,1,1> orthonormalFrame() const { + return Dune::FieldMatrix<T,1,1>(1); + } - //private: + //private: - // We store the rotation as an angle - T angle_; + // We store the rotation as an angle + T angle_; }; //! Send configuration to output stream template <class T> std::ostream& operator<< (std::ostream& s, const Rotation<T,2>& c) - { - return s << "[" << c.angle_ << " (" << std::sin(c.angle_) << " " << std::cos(c.angle_) << ") ]"; - } +{ + return s << "[" << c.angle_ << " (" << std::sin(c.angle_) << " " << std::cos(c.angle_) << ") ]"; +} /** \brief Specialization for dim==3 -Uses unit quaternion coordinates. -\todo Reimplement the method inverse() such that it returns a Rotation instead of a Quaternion. -Then remove the cast in the method setRotation, file averageinterface.hh -*/ + Uses unit quaternion coordinates. + \todo Reimplement the method inverse() such that it returns a Rotation instead of a Quaternion. + Then remove the cast in the method setRotation, file averageinterface.hh + */ template <class T> class Rotation<T,3> : public Quaternion<T> { - /** \brief Computes sin(x/2) / x without getting unstable for small x */ - static T sincHalf(const T& x) { - using std::sin; - return (x < 1e-1) ? 0.5 - (x*x/48) + Dune::power(x,4)/3840 - Dune::power(x,6)/645120 : sin(x/2)/x; - } + /** \brief Computes sin(x/2) / x without getting unstable for small x */ + static T sincHalf(const T& x) { + using std::sin; + return (x < 1e-1) ? 0.5 - (x*x/48) + Dune::power(x,4)/3840 - Dune::power(x,6)/645120 : sin(x/2)/x; + } - /** \brief Computes sin(sqrt(x)/2) / sqrt(x) without getting unstable for small x - * - * Using this somewhat exotic series allows to avoid a few calls to std::sqrt near 0, - * which ADOL-C doesn't like. - */ - static T sincHalfOfSquare(const T& x) { - using std::sin; - using std::sqrt; - return (x < 1e-15) ? 0.5 - (x/48) + (x*x)/(120*32) : sin(sqrt(x)/2)/sqrt(x); - } + /** \brief Computes sin(sqrt(x)/2) / sqrt(x) without getting unstable for small x + * + * Using this somewhat exotic series allows to avoid a few calls to std::sqrt near 0, + * which ADOL-C doesn't like. + */ + static T sincHalfOfSquare(const T& x) { + using std::sin; + using std::sqrt; + return (x < 1e-15) ? 0.5 - (x/48) + (x*x)/(120*32) : sin(sqrt(x)/2)/sqrt(x); + } - /** \brief Computes sin(sqrt(x)) / sqrt(x) without getting unstable for small x - * - * Using this somewhat exotic series allows to avoid a few calls to std::sqrt near 0, - * which ADOL-C doesn't like. - */ - static T sincOfSquare(const T& x) { - using std::sin; - using std::sqrt; - // we need here lots of terms to be sure that the numerical derivatives are also within maschine precision - return (x < 1e-2) ? - 1-x/6 - +x*x/120 - -Dune::power(x,3)/5040 - +Dune::power(x,4)/362880 - -Dune::power(x,5)/39916800 - +Dune::power(x,6)/6227020800 - -Dune::power(x,7)/1307674368000: sin(sqrt(x))/sqrt(x); - } + /** \brief Computes sin(sqrt(x)) / sqrt(x) without getting unstable for small x + * + * Using this somewhat exotic series allows to avoid a few calls to std::sqrt near 0, + * which ADOL-C doesn't like. + */ + static T sincOfSquare(const T& x) { + using std::sin; + using std::sqrt; + // we need here lots of terms to be sure that the numerical derivatives are also within maschine precision + return (x < 1e-2) ? + 1-x/6 + +x*x/120 + -Dune::power(x,3)/5040 + +Dune::power(x,4)/362880 + -Dune::power(x,5)/39916800 + +Dune::power(x,6)/6227020800 + -Dune::power(x,7)/1307674368000: sin(sqrt(x))/sqrt(x); + } public: - /** \brief The type used for coordinates */ - typedef T ctype; + /** \brief The type used for coordinates */ + typedef T ctype; - /** \brief The type used for global coordinates */ - typedef Dune::FieldVector<T,4> CoordinateType; + /** \brief The type used for global coordinates */ + typedef Dune::FieldVector<T,4> CoordinateType; - /** \brief Dimension of the manifold formed by the 3d rotations */ - static const int dim = 3; + /** \brief Dimension of the manifold formed by the 3d rotations */ + static const int dim = 3; - /** \brief Coordinates are embedded into a four-dimension Euclidean space */ - static const int embeddedDim = 4; + /** \brief Coordinates are embedded into a four-dimension Euclidean space */ + static const int embeddedDim = 4; - /** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix */ - typedef Dune::FieldVector<T,3> TangentVector; + /** \brief Member of the corresponding Lie algebra. This really is a skew-symmetric matrix */ + typedef Dune::FieldVector<T,3> TangentVector; - /** \brief A tangent vector as a vector in the surrounding coordinate space */ - typedef Quaternion<T> EmbeddedTangentVector; + /** \brief A tangent vector as a vector in the surrounding coordinate space */ + typedef Quaternion<T> EmbeddedTangentVector; - /** \brief The global convexity radius of the rotation group */ - static constexpr double convexityRadius = 0.5 * M_PI; + /** \brief The global convexity radius of the rotation group */ + static constexpr double convexityRadius = 0.5 * M_PI; - /** \brief Default constructor creates the identity element */ - Rotation() - : Quaternion<T>(0,0,0,1) - {} + /** \brief Default constructor creates the identity element */ + Rotation() + : Quaternion<T>(0,0,0,1) + {} - explicit Rotation(const std::array<T,4>& c) - { - for (int i=0; i<4; i++) - (*this)[i] = c[i]; + explicit Rotation(const std::array<T,4>& c) + { + for (int i=0; i<4; i++) + (*this)[i] = c[i]; - *this /= this->two_norm(); - } + *this /= this->two_norm(); + } - explicit Rotation(const Dune::FieldVector<T,4>& c) - : Quaternion<T>(c) - { - *this /= this->two_norm(); - } + explicit Rotation(const Dune::FieldVector<T,4>& c) + : Quaternion<T>(c) + { + *this /= this->two_norm(); + } - Rotation(Dune::FieldVector<T,3> axis, T angle) - { - axis /= axis.two_norm(); - axis *= std::sin(angle/2); - (*this)[0] = axis[0]; - (*this)[1] = axis[1]; - (*this)[2] = axis[2]; - (*this)[3] = std::cos(angle/2); - } + Rotation(Dune::FieldVector<T,3> axis, T angle) + { + axis /= axis.two_norm(); + axis *= std::sin(angle/2); + (*this)[0] = axis[0]; + (*this)[1] = axis[1]; + (*this)[2] = axis[2]; + (*this)[3] = std::cos(angle/2); + } - /** \brief Rebind the Rotation to another coordinate type */ - template<class U> - struct rebind - { - typedef Rotation<U,3> other; - }; + /** \brief Rebind the Rotation to another coordinate type */ + template<class U> + struct rebind + { + typedef Rotation<U,3> other; + }; - Rotation& operator= (const Dune::FieldVector<T,4>& other) - { - for (int i=0; i<4; i++) - (*this)[i] = other[i]; - *this /= this->two_norm(); - return *this; - } + Rotation& operator= (const Dune::FieldVector<T,4>& other) + { + for (int i=0; i<4; i++) + (*this)[i] = other[i]; + *this /= this->two_norm(); + return *this; + } - /** \brief Assignment from Rotation with different type -- used for automatic differentiation with ADOL-C */ - template <class T2> - Rotation& operator <<= (const Rotation<T2,3>& other) { - for (int i=0; i<4; i++) - (*this)[i] <<= other[i]; - return *this; - } + /** \brief Assignment from Rotation with different type -- used for automatic differentiation with ADOL-C */ + template <class T2> + Rotation& operator <<= (const Rotation<T2,3>& other) { + for (int i=0; i<4; i++) + (*this)[i] <<= other[i]; + return *this; + } - /** \brief Return the identity element */ - static Rotation<T,3> identity() { - // Default constructor creates an identity - Rotation<T,3> id; - return id; - } + /** \brief Return the identity element */ + static Rotation<T,3> identity() { + // Default constructor creates an identity + Rotation<T,3> id; + return id; + } - /** \brief Right multiplication */ - Rotation<T,3> mult(const Rotation<T,3>& other) const { - Rotation<T,3> q; - q[0] = (*this)[3]*other[0] - (*this)[2]*other[1] + (*this)[1]*other[2] + (*this)[0]*other[3]; - q[1] = (*this)[2]*other[0] + (*this)[3]*other[1] - (*this)[0]*other[2] + (*this)[1]*other[3]; - q[2] = - (*this)[1]*other[0] + (*this)[0]*other[1] + (*this)[3]*other[2] + (*this)[2]*other[3]; - q[3] = - (*this)[0]*other[0] - (*this)[1]*other[1] - (*this)[2]*other[2] + (*this)[3]*other[3]; + /** \brief Right multiplication */ + Rotation<T,3> mult(const Rotation<T,3>& other) const { + Rotation<T,3> q; + q[0] = (*this)[3]*other[0] - (*this)[2]*other[1] + (*this)[1]*other[2] + (*this)[0]*other[3]; + q[1] = (*this)[2]*other[0] + (*this)[3]*other[1] - (*this)[0]*other[2] + (*this)[1]*other[3]; + q[2] = - (*this)[1]*other[0] + (*this)[0]*other[1] + (*this)[3]*other[2] + (*this)[2]*other[3]; + q[3] = - (*this)[0]*other[0] - (*this)[1]*other[1] - (*this)[2]*other[2] + (*this)[3]*other[3]; - return q; - } + return q; + } - /** \brief The exponential map from \f$ \mathfrak{so}(3) \f$ to \f$ SO(3) \f$ - */ - static Rotation<T,3> exp(const SkewMatrix<T,3>& v) { - using std::cos; - using std::sqrt; + /** \brief The exponential map from \f$ \mathfrak{so}(3) \f$ to \f$ SO(3) \f$ + */ + static Rotation<T,3> exp(const SkewMatrix<T,3>& v) { + using std::cos; + using std::sqrt; - Rotation<T,3> q; + Rotation<T,3> q; - Dune::FieldVector<T,3> vAxial = v.axial(); - T normV2 = vAxial.two_norm2(); + Dune::FieldVector<T,3> vAxial = v.axial(); + T normV2 = vAxial.two_norm2(); - // Stabilization for small |v| - T sin = sincHalfOfSquare(normV2); + // Stabilization for small |v| + T sin = sincHalfOfSquare(normV2); - assert(!std::isnan(sin)); + assert(!std::isnan(sin)); - q[0] = sin * vAxial[0]; - q[1] = sin * vAxial[1]; - q[2] = sin * vAxial[2]; + q[0] = sin * vAxial[0]; + q[1] = sin * vAxial[1]; + q[2] = sin * vAxial[2]; - // The series expansion of cos(x) at x=0 is - // 1 - x*x/2 + x*x*x*x/24 - ... - // hence the series of cos(x/2) is - // 1 - x*x/8 + x*x*x*x/384 - ... - q[3] = (normV2 < 1e-4) + // The series expansion of cos(x) at x=0 is + // 1 - x*x/2 + x*x*x*x/24 - ... + // hence the series of cos(x/2) is + // 1 - x*x/8 + x*x*x*x/384 - ... + q[3] = (normV2 < 1e-4) ? 1 - normV2/8 + normV2*normV2 / 384-Dune::power(normV2,3)/46080 + Dune::power(normV2,4)/10321920 : cos(sqrt(normV2)/2); - return q; - } + return q; + } - /** \brief The exponential map from a given point $p \in SO(3)$. + /** \brief The exponential map from a given point $p \in SO(3)$. - * \param v A tangent vector *at the identity*! - */ - static Rotation<T,3> exp(const Rotation<T,3>& p, const SkewMatrix<T,3>& v) { - Rotation<T,3> corr = exp(v); - return p.mult(corr); - } + * \param v A tangent vector *at the identity*! + */ + static Rotation<T,3> exp(const Rotation<T,3>& p, const SkewMatrix<T,3>& v) { + Rotation<T,3> corr = exp(v); + return p.mult(corr); + } - /** \brief The exponential map from a given point $p \in SO(3)$. + /** \brief The exponential map from a given point $p \in SO(3)$. - There may be a more direct way to implement this + There may be a more direct way to implement this - \param v A tangent vector in quaternion coordinates - */ - static Rotation<T,3> exp(const Rotation<T,3>& p, const Dune::FieldVector<T,4>& v) { + \param v A tangent vector in quaternion coordinates + */ + static Rotation<T,3> exp(const Rotation<T,3>& p, const Dune::FieldVector<T,4>& v) { - using std::abs; - using std::cos; - using std::sqrt; - assert( abs(p*v) < 1e-8 ); + using std::abs; + using std::cos; + using std::sqrt; + assert( abs(p*v) < 1e-8 ); - const T norm2 = v.two_norm2(); + const T norm2 = v.two_norm2(); - Rotation<T,3> result = p; + Rotation<T,3> result = p; - // The series expansion of cos(x) at x=0 is - // 1 - x*x/2 + x*x*x*x/24 - ... - T cosValue = (norm2 < 1e-5) + // The series expansion of cos(x) at x=0 is + // 1 - x*x/2 + x*x*x*x/24 - ... + T cosValue = (norm2 < 1e-5) ? 1 - norm2/2 + norm2*norm2 / 24 - Dune::power(norm2,3)/720+Dune::power(norm2,4)/40320 : cos(sqrt(norm2)); - result *= cosValue; + result *= cosValue; - result.axpy(sincOfSquare(norm2), v); - return result; - } + result.axpy(sincOfSquare(norm2), v); + return result; + } - /** \brief The exponential map from a given point $p \in SO(3)$. + /** \brief The exponential map from a given point $p \in SO(3)$. - \param v A tangent vector. - */ - static Rotation<T,3> exp(const Rotation<T,3>& p, const TangentVector& v) { + \param v A tangent vector. + */ + static Rotation<T,3> exp(const Rotation<T,3>& p, const TangentVector& v) { - // embedded tangent vector - Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame(); - Quaternion<T> embeddedTangent; - basis.mtv(v, embeddedTangent); + // embedded tangent vector + Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame(); + Quaternion<T> embeddedTangent; + basis.mtv(v, embeddedTangent); - return exp(p,embeddedTangent); + return exp(p,embeddedTangent); - } + } - /** \brief Compute tangent vector from given basepoint and skew symmetric matrix. */ - static TangentVector skewToTangentVector(const Rotation<T,3>& p, const SkewMatrix<T,3>& v ) { + /** \brief Compute tangent vector from given basepoint and skew symmetric matrix. */ + static TangentVector skewToTangentVector(const Rotation<T,3>& p, const SkewMatrix<T,3>& v ) { - // embedded tangent vector at identity - Quaternion<T> vAtIdentity(0); - vAtIdentity[0] = 0.5*v.axial()[0]; - vAtIdentity[1] = 0.5*v.axial()[1]; - vAtIdentity[2] = 0.5*v.axial()[2]; + // embedded tangent vector at identity + Quaternion<T> vAtIdentity(0); + vAtIdentity[0] = 0.5*v.axial()[0]; + vAtIdentity[1] = 0.5*v.axial()[1]; + vAtIdentity[2] = 0.5*v.axial()[2]; - // multiply with base point to get real embedded tangent vector - Quaternion<T> vQuat = ((Quaternion<T>) p).mult(vAtIdentity); + // multiply with base point to get real embedded tangent vector + Quaternion<T> vQuat = ((Quaternion<T>)p).mult(vAtIdentity); - //get basis of the tangent space - Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame(); + //get basis of the tangent space + Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame(); - // transform coordinates - TangentVector tang; - basis.mv(vQuat,tang); + // transform coordinates + TangentVector tang; + basis.mv(vQuat,tang); - return tang; - } + return tang; + } - /** \brief Compute skew matrix from given basepoint and tangent vector. */ - static SkewMatrix<T,3> tangentToSkew(const Rotation<T,3>& p, const TangentVector& tangent) { + /** \brief Compute skew matrix from given basepoint and tangent vector. */ + static SkewMatrix<T,3> tangentToSkew(const Rotation<T,3>& p, const TangentVector& tangent) { - // embedded tangent vector - Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame(); - Quaternion<T> embeddedTangent; - basis.mtv(tangent, embeddedTangent); + // embedded tangent vector + Dune::FieldMatrix<T,3,4> basis = p.orthonormalFrame(); + Quaternion<T> embeddedTangent; + basis.mtv(tangent, embeddedTangent); - return tangentToSkew(p,embeddedTangent); - } + return tangentToSkew(p,embeddedTangent); + } - /** \brief Compute skew matrix from given basepoint and an embedded tangent vector. */ - static SkewMatrix<T,3> tangentToSkew(const Rotation<T,3>& p, const EmbeddedTangentVector& q) { + /** \brief Compute skew matrix from given basepoint and an embedded tangent vector. */ + static SkewMatrix<T,3> tangentToSkew(const Rotation<T,3>& p, const EmbeddedTangentVector& q) { - // left multiplication by the inverse base point yields a tangent vector at the identity - Quaternion<T> vAtIdentity = p.inverse().mult(q); - assert( std::abs(vAtIdentity[3]) < 1e-8 ); + // left multiplication by the inverse base point yields a tangent vector at the identity + Quaternion<T> vAtIdentity = p.inverse().mult(q); + assert( std::abs(vAtIdentity[3]) < 1e-8 ); - SkewMatrix<T,3> skew; - skew.axial()[0] = 2*vAtIdentity[0]; - skew.axial()[1] = 2*vAtIdentity[1]; - skew.axial()[2] = 2*vAtIdentity[2]; + SkewMatrix<T,3> skew; + skew.axial()[0] = 2*vAtIdentity[0]; + skew.axial()[1] = 2*vAtIdentity[1]; + skew.axial()[2] = 2*vAtIdentity[2]; - return skew; - } + return skew; + } - /** \brief Derivative of the exponential map at the identity - * - * The exponential map at the identity is a map from a neighborhood of zero to the neighborhood of the identity rotation. - * - * \param v Where to evaluate the derivative of the (exponential map at the identity) - */ - static Dune::FieldMatrix<T,4,3> Dexp(const SkewMatrix<T,3>& v) { + /** \brief Derivative of the exponential map at the identity + * + * The exponential map at the identity is a map from a neighborhood of zero to the neighborhood of the identity rotation. + * + * \param v Where to evaluate the derivative of the (exponential map at the identity) + */ + static Dune::FieldMatrix<T,4,3> Dexp(const SkewMatrix<T,3>& v) { - using std::cos; + using std::cos; - Dune::FieldMatrix<T,4,3> result(0); - Dune::FieldVector<T,3> vAxial = v.axial(); - T norm = vAxial.two_norm(); + Dune::FieldMatrix<T,4,3> result(0); + Dune::FieldVector<T,3> vAxial = v.axial(); + T norm = vAxial.two_norm(); - for (int i=0; i<3; i++) { + for (int i=0; i<3; i++) { - for (int m=0; m<3; m++) { + for (int m=0; m<3; m++) { - result[m][i] = (norm<1e-10) - /** \todo Isn't there a better way to implement this stably? */ + result[m][i] = (norm<1e-10) + /** \todo Isn't there a better way to implement this stably? */ ? T(0.5) * (i==m) : 0.5 * cos(norm/2) * vAxial[i] * vAxial[m] / (norm*norm) + sincHalf(norm) * ( (i==m) - vAxial[i]*vAxial[m]/(norm*norm)); - } + } - result[3][i] = - 0.5 * sincHalf(norm) * vAxial[i]; + result[3][i] = - 0.5 * sincHalf(norm) * vAxial[i]; - } - return result; } + return result; + } - static void DDexp(const Dune::FieldVector<T,3>& v, - std::array<Dune::FieldMatrix<T,3,3>, 4>& result) { - - using std::cos; - using std::sin; - - T norm = v.two_norm(); - if (norm<=1e-10) { + static void DDexp(const Dune::FieldVector<T,3>& v, + std::array<Dune::FieldMatrix<T,3,3>, 4>& result) { - for (int m=0; m<4; m++) - result[m] = 0; + using std::cos; + using std::sin; - for (int i=0; i<3; i++) - result[3][i][i] = -0.25; + T norm = v.two_norm(); + if (norm<=1e-10) { + for (int m=0; m<4; m++) + result[m] = 0; - } else { + for (int i=0; i<3; i++) + result[3][i][i] = -0.25; - for (int i=0; i<3; i++) { - for (int j=0; j<3; j++) { + } else { - for (int m=0; m<3; m++) { + for (int i=0; i<3; i++) { - result[m][i][j] = -0.25*sin(norm/2)*v[i]*v[j]*v[m]/(norm*norm*norm) - + ((i==j)*v[m] + (j==m)*v[i] + (i==m)*v[j] - 3*v[i]*v[j]*v[m]/(norm*norm)) - * (0.5*cos(norm/2) - sincHalf(norm)) / (norm*norm); + for (int j=0; j<3; j++) { + for (int m=0; m<3; m++) { - } + result[m][i][j] = -0.25*sin(norm/2)*v[i]*v[j]*v[m]/(norm*norm*norm) + + ((i==j)*v[m] + (j==m)*v[i] + (i==m)*v[j] - 3*v[i]*v[j]*v[m]/(norm*norm)) + * (0.5*cos(norm/2) - sincHalf(norm)) / (norm*norm); - result[3][i][j] = -0.5/(norm*norm) - * ( 0.5*cos(norm/2)*v[i]*v[j] + sin(norm/2) * ((i==j)*norm - v[i]*v[j]/norm)); - } + } - } + result[3][i][j] = -0.5/(norm*norm) + * ( 0.5*cos(norm/2)*v[i]*v[j] + sin(norm/2) * ((i==j)*norm - v[i]*v[j]/norm)); } + } + } - /** \brief The inverse of the exponential map */ - static Dune::FieldVector<T,3> expInv(const Rotation<T,3>& q) { - // Compute v = exp^{-1} q - // Due to numerical dirt, q[3] may be larger than 1. - // In that case, use 1 instead of q[3]. - Dune::FieldVector<T,3> v; - if (q[3] > 1.0) { + } - v = 0; + /** \brief The inverse of the exponential map */ + static Dune::FieldVector<T,3> expInv(const Rotation<T,3>& q) { + // Compute v = exp^{-1} q + // Due to numerical dirt, q[3] may be larger than 1. + // In that case, use 1 instead of q[3]. + Dune::FieldVector<T,3> v; + if (q[3] > 1.0) { - } else { + v = 0; - using std::acos; - T invSinc = 1/sincHalf(2*acos(q[3])); + } else { - v[0] = q[0] * invSinc; - v[1] = q[1] * invSinc; - v[2] = q[2] * invSinc; + using std::acos; + T invSinc = 1/sincHalf(2*acos(q[3])); + + v[0] = q[0] * invSinc; + v[1] = q[1] * invSinc; + v[2] = q[2] * invSinc; - } - return v; } + return v; + } - /** \brief The derivative of the inverse of the exponential map, evaluated at q */ - static Dune::FieldMatrix<T,3,4> DexpInv(const Rotation<T,3>& q) { + /** \brief The derivative of the inverse of the exponential map, evaluated at q */ + static Dune::FieldMatrix<T,3,4> DexpInv(const Rotation<T,3>& q) { - // Compute v = exp^{-1} q - Dune::FieldVector<T,3> v = expInv(q); + // Compute v = exp^{-1} q + Dune::FieldVector<T,3> v = expInv(q); - // The derivative of exp at v - Dune::FieldMatrix<T,4,3> A = Dexp(SkewMatrix<T,3>(v)); + // The derivative of exp at v + Dune::FieldMatrix<T,4,3> A = Dexp(SkewMatrix<T,3>(v)); - // Compute the Moore-Penrose pseudo inverse A^+ = (A^T A)^{-1} A^T - Dune::FieldMatrix<T,3,3> ATA; + // Compute the Moore-Penrose pseudo inverse A^+ = (A^T A)^{-1} A^T + Dune::FieldMatrix<T,3,3> ATA; - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) { - ATA[i][j] = 0; - for (int k=0; k<4; k++) - ATA[i][j] += A[k][i] * A[k][j]; - } + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) { + ATA[i][j] = 0; + for (int k=0; k<4; k++) + ATA[i][j] += A[k][i] * A[k][j]; + } - ATA.invert(); + ATA.invert(); - Dune::FieldMatrix<T,3,4> APseudoInv; - for (int i=0; i<3; i++) - for (int j=0; j<4; j++) { - APseudoInv[i][j] = 0; - for (int k=0; k<3; k++) - APseudoInv[i][j] += ATA[i][k] * A[j][k]; - } + Dune::FieldMatrix<T,3,4> APseudoInv; + for (int i=0; i<3; i++) + for (int j=0; j<4; j++) { + APseudoInv[i][j] = 0; + for (int k=0; k<3; k++) + APseudoInv[i][j] += ATA[i][k] * A[j][k]; + } - return APseudoInv; - } + return APseudoInv; + } - /** \brief The cayley mapping from \f$ \mathfrak{so}(3) \f$ to \f$ SO(3) \f$. - * - * The formula is taken from 'J.C.Simo, N.Tarnom, M.Doblare - Non-linear dynamics of - * three-dimensional rods:Exact energy and momentum conserving algorithms' - * (but the corrected version with 0.25 instead of 0.5 in the denominator) - */ - static Rotation<T,3> cayley(const SkewMatrix<T,3>& s) { - Rotation<T,3> q; - - Dune::FieldVector<T,3> vAxial = s.axial(); - T norm = 0.25*vAxial.two_norm2() + 1; - - Dune::FieldMatrix<T,3,3> mat = s.toMatrix(); - mat *= 0.5; - Dune::FieldMatrix<T,3,3> skewSquare = mat; - skewSquare.rightmultiply(mat); - mat += skewSquare; - mat *= 2/norm; - - for (int i=0;i<3;i++) - mat[i][i] += 1; - - q.set(mat); - return q; - } + /** \brief The cayley mapping from \f$ \mathfrak{so}(3) \f$ to \f$ SO(3) \f$. + * + * The formula is taken from 'J.C.Simo, N.Tarnom, M.Doblare - Non-linear dynamics of + * three-dimensional rods:Exact energy and momentum conserving algorithms' + * (but the corrected version with 0.25 instead of 0.5 in the denominator) + */ + static Rotation<T,3> cayley(const SkewMatrix<T,3>& s) { + Rotation<T,3> q; + + Dune::FieldVector<T,3> vAxial = s.axial(); + T norm = 0.25*vAxial.two_norm2() + 1; + + Dune::FieldMatrix<T,3,3> mat = s.toMatrix(); + mat *= 0.5; + Dune::FieldMatrix<T,3,3> skewSquare = mat; + skewSquare.rightmultiply(mat); + mat += skewSquare; + mat *= 2/norm; + + for (int i=0; i<3; i++) + mat[i][i] += 1; + + q.set(mat); + return q; + } - /** \brief The inverse of the Cayley mapping. - * - * The formula is taken from J.M.Selig - Cayley Maps for SE(3). - */ - static SkewMatrix<T,3> cayleyInv(const Rotation<T,3> q) { + /** \brief The inverse of the Cayley mapping. + * + * The formula is taken from J.M.Selig - Cayley Maps for SE(3). + */ + static SkewMatrix<T,3> cayleyInv(const Rotation<T,3> q) { - Dune::FieldMatrix<T,3,3> mat; + Dune::FieldMatrix<T,3,3> mat; - // compute the trace of the rotation matrix - T trace = -q[0]*q[0] -q[1]*q[1] -q[2]*q[2]+3*q[3]*q[3]; + // compute the trace of the rotation matrix + T trace = -q[0]*q[0] -q[1]*q[1] -q[2]*q[2]+3*q[3]*q[3]; - if ( (trace+1)>1e-6 || (trace+1)<-1e-6) { // if this term doesn't vanish we can use a direct formula + if ( (trace+1)>1e-6 || (trace+1)<-1e-6) { // if this term doesn't vanish we can use a direct formula - q.matrix(mat); - Dune::FieldMatrix<T,3,3> matT; - Rotation<T,3>(q.inverse()).matrix(matT); - mat -= matT; - mat *= 2/(1+trace); - } - else { // use the formula that involves the computation of an inverse - Dune::FieldMatrix<T,3,3> inv; - q.matrix(inv); - Dune::FieldMatrix<T,3,3> notInv = inv; - - for (int i=0;i<3;i++) { - inv[i][i] +=1; - notInv[i][i] -=1; - } - inv.invert(); - mat = notInv.leftmultiply(inv); - mat *= 2; - } - - // result is a skew symmetric matrix - SkewMatrix<T,3> res; - res.axial()[0] = mat[2][1]; - res.axial()[1] = mat[0][2]; - res.axial()[2] = mat[1][0]; + q.matrix(mat); + Dune::FieldMatrix<T,3,3> matT; + Rotation<T,3>(q.inverse()).matrix(matT); + mat -= matT; + mat *= 2/(1+trace); + } + else { // use the formula that involves the computation of an inverse + Dune::FieldMatrix<T,3,3> inv; + q.matrix(inv); + Dune::FieldMatrix<T,3,3> notInv = inv; + + for (int i=0; i<3; i++) { + inv[i][i] +=1; + notInv[i][i] -=1; + } + inv.invert(); + mat = notInv.leftmultiply(inv); + mat *= 2; + } - return res; + // result is a skew symmetric matrix + SkewMatrix<T,3> res; + res.axial()[0] = mat[2][1]; + res.axial()[1] = mat[0][2]; + res.axial()[2] = mat[1][0]; - } + return res; - static T distance(const Rotation<T,3>& a, const Rotation<T,3>& b) { + } - // Distance in the unit quaternions: 2*arccos( ((a^{-1) b)_3 ) - // But note that (a^{-1}b)_3 is actually <a,b> (in R^4) - T sp = a.globalCoordinates() * b.globalCoordinates(); + static T distance(const Rotation<T,3>& a, const Rotation<T,3>& b) { - // Scalar product may be larger than 1.0, due to numerical dirt - using std::acos; - using std::min; - T dist = 2*acos( min(sp,T(1.0)) ); + // Distance in the unit quaternions: 2*arccos( ((a^{-1) b)_3 ) + // But note that (a^{-1}b)_3 is actually <a,b> (in R^4) + T sp = a.globalCoordinates() * b.globalCoordinates(); - // Make sure we do the right thing if a and b are not in the same sheet - // of the double covering of the unit quaternions over SO(3) - return (dist>=M_PI) ? (2*M_PI - dist) : dist; - } + // Scalar product may be larger than 1.0, due to numerical dirt + using std::acos; + using std::min; + T dist = 2*acos( min(sp,T(1.0)) ); - /** \brief Compute the vector in T_aSO(3) that is mapped by the exponential map - to the geodesic from a to b - */ - static EmbeddedTangentVector log(const Rotation<T,3>& a, const Rotation<T,3>& b) { + // Make sure we do the right thing if a and b are not in the same sheet + // of the double covering of the unit quaternions over SO(3) + return (dist>=M_PI) ? (2*M_PI - dist) : dist; + } - // embedded tangent vector at identity - Quaternion<T> v; + /** \brief Compute the vector in T_aSO(3) that is mapped by the exponential map + to the geodesic from a to b + */ + static EmbeddedTangentVector log(const Rotation<T,3>& a, const Rotation<T,3>& b) { - Quaternion<T> diff = a; - diff.invert(); - diff = diff.mult(b); + // embedded tangent vector at identity + Quaternion<T> v; - // Compute the geodesical distance between a and b on SO(3) - // Due to numerical dirt, diff[3] may be larger than 1. - // In that case, use 1 instead of diff[3]. - if (diff[3] > 1.0) { + Quaternion<T> diff = a; + diff.invert(); + diff = diff.mult(b); - v = 0; + // Compute the geodesical distance between a and b on SO(3) + // Due to numerical dirt, diff[3] may be larger than 1. + // In that case, use 1 instead of diff[3]. + if (diff[3] > 1.0) { - } else { + v = 0; - // TODO: ADOL-C does not like this part of the code, - // because arccos is not differentiable at -1 and 1. - // (Even though the overall 'log' function is differentiable.) - using std::acos; - T dist = 2*acos( diff[3] ); + } else { - // Make sure we do the right thing if a and b are not in the same sheet - // of the double covering of the unit quaternions over SO(3) - if (dist>=M_PI) { - dist = 2*M_PI - dist; - diff *= -1; - } + // TODO: ADOL-C does not like this part of the code, + // because arccos is not differentiable at -1 and 1. + // (Even though the overall 'log' function is differentiable.) + using std::acos; + T dist = 2*acos( diff[3] ); - T invSinc = 1/sincHalf(dist); + // Make sure we do the right thing if a and b are not in the same sheet + // of the double covering of the unit quaternions over SO(3) + if (dist>=M_PI) { + dist = 2*M_PI - dist; + diff *= -1; + } - // Compute log on T_a SO(3) - v[0] = 0.5 * diff[0] * invSinc; - v[1] = 0.5 * diff[1] * invSinc; - v[2] = 0.5 * diff[2] * invSinc; - v[3] = 0; - } + T invSinc = 1/sincHalf(dist); - // multiply with base point to get real embedded tangent vector - return ((Quaternion<T>) a).mult(v); + // Compute log on T_a SO(3) + v[0] = 0.5 * diff[0] * invSinc; + v[1] = 0.5 * diff[1] * invSinc; + v[2] = 0.5 * diff[2] * invSinc; + v[3] = 0; } - /** \brief Compute the derivatives of the director vectors with respect to the quaternion coordinates - * - * Let \f$ d_k(q) = (d_{k,1}, d_{k,2}, d_{k,3})\f$ be the k-th director vector at \f$ q \f$. - * Then the return value of this method is - * \f[ A_{ijk} = \frac{\partial d_{i,j}}{\partial q_k} \f] - */ - void getFirstDerivativesOfDirectors(Tensor3<T,3, 3, 4>& dd_dq) const - { - const Quaternion<T>& q = (*this); + // multiply with base point to get real embedded tangent vector + return ((Quaternion<T>)a).mult(v); + } - dd_dq[0][0] = { 2*q[0], -2*q[1], -2*q[2], 2*q[3]}; - dd_dq[0][1] = { 2*q[1], 2*q[0], 2*q[3], 2*q[2]}; - dd_dq[0][2] = { 2*q[2], -2*q[3], 2*q[0], -2*q[1]}; + /** \brief Compute the derivatives of the director vectors with respect to the quaternion coordinates + * + * Let \f$ d_k(q) = (d_{k,1}, d_{k,2}, d_{k,3})\f$ be the k-th director vector at \f$ q \f$. + * Then the return value of this method is + * \f[ A_{ijk} = \frac{\partial d_{i,j}}{\partial q_k} \f] + */ + void getFirstDerivativesOfDirectors(Tensor3<T,3, 3, 4>& dd_dq) const + { + const Quaternion<T>& q = (*this); - dd_dq[1][0] = { 2*q[1], 2*q[0], -2*q[3], -2*q[2]}; - dd_dq[1][1] = {-2*q[0], 2*q[1], -2*q[2], 2*q[3]}; - dd_dq[1][2] = { 2*q[3], 2*q[2], 2*q[1], 2*q[0]}; + dd_dq[0][0] = { 2*q[0], -2*q[1], -2*q[2], 2*q[3]}; + dd_dq[0][1] = { 2*q[1], 2*q[0], 2*q[3], 2*q[2]}; + dd_dq[0][2] = { 2*q[2], -2*q[3], 2*q[0], -2*q[1]}; - dd_dq[2][0] = { 2*q[2], 2*q[3], 2*q[0], 2*q[1]}; - dd_dq[2][1] = {-2*q[3], 2*q[2], 2*q[1], -2*q[0]}; - dd_dq[2][2] = {-2*q[0], -2*q[1], 2*q[2], 2*q[3]}; + dd_dq[1][0] = { 2*q[1], 2*q[0], -2*q[3], -2*q[2]}; + dd_dq[1][1] = {-2*q[0], 2*q[1], -2*q[2], 2*q[3]}; + dd_dq[1][2] = { 2*q[3], 2*q[2], 2*q[1], 2*q[0]}; - } + dd_dq[2][0] = { 2*q[2], 2*q[3], 2*q[0], 2*q[1]}; + dd_dq[2][1] = {-2*q[3], 2*q[2], 2*q[1], -2*q[0]}; + dd_dq[2][2] = {-2*q[0], -2*q[1], 2*q[2], 2*q[3]}; - /** \brief Compute the second derivatives of the director vectors with respect to the quaternion coordinates - * - * Let \f$ d_k(q) = (d_{k,1}, d_{k,2}, d_{k,3})\f$ be the k-th director vector at \f$ q \f$. - * Then the return value of this method is - * \f[ A_{ijkl} = \frac{\partial^2 d_{i,j}}{\partial q_k \partial q_l} \f] - */ - static void getSecondDerivativesOfDirectors(std::array<Tensor3<T,3, 4, 4>, 3>& dd_dq_dq) - { - for (int i=0; i<3; i++) - dd_dq_dq[i] = T(0); + } - dd_dq_dq[0][0][0][0] = 2; dd_dq_dq[0][0][1][1] = -2; dd_dq_dq[0][0][2][2] = -2; dd_dq_dq[0][0][3][3] = 2; - dd_dq_dq[0][1][0][1] = 2; dd_dq_dq[0][1][1][0] = 2; dd_dq_dq[0][1][2][3] = 2; dd_dq_dq[0][1][3][2] = 2; - dd_dq_dq[0][2][0][2] = 2; dd_dq_dq[0][2][1][3] = -2; dd_dq_dq[0][2][2][0] = 2; dd_dq_dq[0][2][3][1] = -2; + /** \brief Compute the second derivatives of the director vectors with respect to the quaternion coordinates + * + * Let \f$ d_k(q) = (d_{k,1}, d_{k,2}, d_{k,3})\f$ be the k-th director vector at \f$ q \f$. + * Then the return value of this method is + * \f[ A_{ijkl} = \frac{\partial^2 d_{i,j}}{\partial q_k \partial q_l} \f] + */ + static void getSecondDerivativesOfDirectors(std::array<Tensor3<T,3, 4, 4>, 3>& dd_dq_dq) + { + for (int i=0; i<3; i++) + dd_dq_dq[i] = T(0); - dd_dq_dq[1][0][0][1] = 2; dd_dq_dq[1][0][1][0] = 2; dd_dq_dq[1][0][2][3] = -2; dd_dq_dq[1][0][3][2] = -2; - dd_dq_dq[1][1][0][0] = -2; dd_dq_dq[1][1][1][1] = 2; dd_dq_dq[1][1][2][2] = -2; dd_dq_dq[1][1][3][3] = 2; - dd_dq_dq[1][2][0][3] = 2; dd_dq_dq[1][2][1][2] = 2; dd_dq_dq[1][2][2][1] = 2; dd_dq_dq[1][2][3][0] = 2; + dd_dq_dq[0][0][0][0] = 2; dd_dq_dq[0][0][1][1] = -2; dd_dq_dq[0][0][2][2] = -2; dd_dq_dq[0][0][3][3] = 2; + dd_dq_dq[0][1][0][1] = 2; dd_dq_dq[0][1][1][0] = 2; dd_dq_dq[0][1][2][3] = 2; dd_dq_dq[0][1][3][2] = 2; + dd_dq_dq[0][2][0][2] = 2; dd_dq_dq[0][2][1][3] = -2; dd_dq_dq[0][2][2][0] = 2; dd_dq_dq[0][2][3][1] = -2; - dd_dq_dq[2][0][0][2] = 2; dd_dq_dq[2][0][1][3] = 2; dd_dq_dq[2][0][2][0] = 2; dd_dq_dq[2][0][3][1] = 2; - dd_dq_dq[2][1][0][3] = -2; dd_dq_dq[2][1][1][2] = 2; dd_dq_dq[2][1][2][1] = 2; dd_dq_dq[2][1][3][0] = -2; - dd_dq_dq[2][2][0][0] = -2; dd_dq_dq[2][2][1][1] = -2; dd_dq_dq[2][2][2][2] = 2; dd_dq_dq[2][2][3][3] = 2; + dd_dq_dq[1][0][0][1] = 2; dd_dq_dq[1][0][1][0] = 2; dd_dq_dq[1][0][2][3] = -2; dd_dq_dq[1][0][3][2] = -2; + dd_dq_dq[1][1][0][0] = -2; dd_dq_dq[1][1][1][1] = 2; dd_dq_dq[1][1][2][2] = -2; dd_dq_dq[1][1][3][3] = 2; + dd_dq_dq[1][2][0][3] = 2; dd_dq_dq[1][2][1][2] = 2; dd_dq_dq[1][2][2][1] = 2; dd_dq_dq[1][2][3][0] = 2; - } + dd_dq_dq[2][0][0][2] = 2; dd_dq_dq[2][0][1][3] = 2; dd_dq_dq[2][0][2][0] = 2; dd_dq_dq[2][0][3][1] = 2; + dd_dq_dq[2][1][0][3] = -2; dd_dq_dq[2][1][1][2] = 2; dd_dq_dq[2][1][2][1] = 2; dd_dq_dq[2][1][3][0] = -2; + dd_dq_dq[2][2][0][0] = -2; dd_dq_dq[2][2][1][1] = -2; dd_dq_dq[2][2][2][2] = 2; dd_dq_dq[2][2][3][3] = 2; - /** \brief Transform tangent vectors from quaternion representation to matrix representation - * - * This class represents rotations as unit quaternions, and therefore variations of rotations - * (i.e., tangent vector) are represented in quaternion space, too. However, some applications - * require the tangent vectors as matrices. To obtain matrix coordinates we use the - * chain rule, which means that we have to multiply the given derivative with - * the derivative of the embedding of the unit quaternion into the space of 3x3 matrices. - * This second derivative is almost given by the method getFirstDerivativesOfDirectors. - * However, since the directors of a given unit quaternion are the _columns_ of the - * corresponding orthogonal matrix, we need to invert the i and j indices - * - * As a typical GFE assembler will require this for several tangent vectors at once, - * the implementation here is a vector one: It allows to treat several tangent vectors - * together, which have to come as the columns of the `derivative` parameter. - * - * So, if I am not mistaken, result[i][j][k] contains \partial R_ij / \partial k - * - * \param derivative Tangent vector in quaternion coordinates - * \returns DR Tangent vector in matrix coordinates - */ - template <int blocksize> - Tensor3<T,3,3,blocksize> quaternionTangentToMatrixTangent(const Dune::FieldMatrix<T,4,blocksize>& derivative) const - { - Tensor3<T,3,3,blocksize> result = T(0); + } - Tensor3<T,3 , 3, 4> dd_dq; - getFirstDerivativesOfDirectors(dd_dq); + /** \brief Transform tangent vectors from quaternion representation to matrix representation + * + * This class represents rotations as unit quaternions, and therefore variations of rotations + * (i.e., tangent vector) are represented in quaternion space, too. However, some applications + * require the tangent vectors as matrices. To obtain matrix coordinates we use the + * chain rule, which means that we have to multiply the given derivative with + * the derivative of the embedding of the unit quaternion into the space of 3x3 matrices. + * This second derivative is almost given by the method getFirstDerivativesOfDirectors. + * However, since the directors of a given unit quaternion are the _columns_ of the + * corresponding orthogonal matrix, we need to invert the i and j indices + * + * As a typical GFE assembler will require this for several tangent vectors at once, + * the implementation here is a vector one: It allows to treat several tangent vectors + * together, which have to come as the columns of the `derivative` parameter. + * + * So, if I am not mistaken, result[i][j][k] contains \partial R_ij / \partial k + * + * \param derivative Tangent vector in quaternion coordinates + * \returns DR Tangent vector in matrix coordinates + */ + template <int blocksize> + Tensor3<T,3,3,blocksize> quaternionTangentToMatrixTangent(const Dune::FieldMatrix<T,4,blocksize>& derivative) const + { + Tensor3<T,3,3,blocksize> result = T(0); - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) - for (int k=0; k<blocksize; k++) - for (int l=0; l<4; l++) - result[i][j][k] += dd_dq[j][i][l] * derivative[l][k]; + Tensor3<T,3 , 3, 4> dd_dq; + getFirstDerivativesOfDirectors(dd_dq); - return result; - } + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + for (int k=0; k<blocksize; k++) + for (int l=0; l<4; l++) + result[i][j][k] += dd_dq[j][i][l] * derivative[l][k]; - /** \brief Compute the derivative of the squared distance function with respect to the second argument - * - * The squared distance function is - * \f[ 4 \arccos^2 (|<p,q>|) \f] - * Deriving this with respect to the second coordinate gives - * \f[ 4 (\arccos^2)'(x)|_{x = |<p,q>|} * P_qp \f] - * The whole thing has to be multiplied by -1 if \f$ <p,q> \f$ is negative - */ - static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p, - const Rotation<T,3>& q) { - T sp = p.globalCoordinates() * q.globalCoordinates(); - - // Compute the projection of p onto the tangent space of q - EmbeddedTangentVector result = p; - result.axpy(-1*(q*p), q); - - // The ternary operator comes from the derivative of the absolute value function - using std::abs; - result *= 4 * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * ( (sp<0) ? -1 : 1 ); - - return result; - } + return result; + } - /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed */ - static Dune::SymmetricMatrix<T,4> secondDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) { + /** \brief Compute the derivative of the squared distance function with respect to the second argument + * + * The squared distance function is + * \f[ 4 \arccos^2 (|<p,q>|) \f] + * Deriving this with respect to the second coordinate gives + * \f[ 4 (\arccos^2)'(x)|_{x = |<p,q>|} * P_qp \f] + * The whole thing has to be multiplied by -1 if \f$ <p,q> \f$ is negative + */ + static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p, + const Rotation<T,3>& q) { + T sp = p.globalCoordinates() * q.globalCoordinates(); + + // Compute the projection of p onto the tangent space of q + EmbeddedTangentVector result = p; + result.axpy(-1*(q*p), q); + + // The ternary operator comes from the derivative of the absolute value function + using std::abs; + result *= 4 * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * ( (sp<0) ? -1 : 1 ); + + return result; + } - T sp = p.globalCoordinates() * q.globalCoordinates(); + /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed */ + static Dune::SymmetricMatrix<T,4> secondDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) { - EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); + T sp = p.globalCoordinates() * q.globalCoordinates(); - Dune::SymmetricMatrix<T,4> A; - for (int i=0; i<4; i++) - for (int j=0; j<=i; j++) - A(i,j) = pProjected[i]*pProjected[j]; + EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); - using std::abs; - A *= 4*UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)); + Dune::SymmetricMatrix<T,4> A; + for (int i=0; i<4; i++) + for (int j=0; j<=i; j++) + A(i,j) = pProjected[i]*pProjected[j]; - // Compute matrix B (see notes) - Dune::SymmetricMatrix<T,4> Pq; - for (int i=0; i<4; i++) - for (int j=0; j<=i; j++) - Pq(i,j) = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; + using std::abs; + A *= 4*UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)); - // Bring it all together - A.axpy(-4* ((sp<0) ? -1 : 1) * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp))*sp, Pq); + // Compute matrix B (see notes) + Dune::SymmetricMatrix<T,4> Pq; + for (int i=0; i<4; i++) + for (int j=0; j<=i; j++) + Pq(i,j) = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; - return A; - } + // Bring it all together + A.axpy(-4* ((sp<0) ? -1 : 1) * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp))*sp, Pq); - /** \brief Compute the mixed second derivate \partial d^2 / \partial dp dq */ - static Dune::FieldMatrix<T,4,4> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) { + return A; + } - T sp = p.globalCoordinates() * q.globalCoordinates(); + /** \brief Compute the mixed second derivate \partial d^2 / \partial dp dq */ + static Dune::FieldMatrix<T,4,4> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) { - // Compute vector A (see notes) - Dune::FieldMatrix<T,1,4> row; - row[0] = q.projectOntoTangentSpace(p.globalCoordinates()); + T sp = p.globalCoordinates() * q.globalCoordinates(); - EmbeddedTangentVector tmp = p.projectOntoTangentSpace(q.globalCoordinates()); - Dune::FieldMatrix<T,4,1> column; - for (int i=0; i<4; i++) // turn row vector into column vector - column[i] = tmp[i]; + // Compute vector A (see notes) + Dune::FieldMatrix<T,1,4> row; + row[0] = q.projectOntoTangentSpace(p.globalCoordinates()); - Dune::FieldMatrix<T,4,4> A; - // A = row * column - Dune::FMatrixHelp::multMatrix(column,row,A); - using std::abs; - A *= 4 * UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)); + EmbeddedTangentVector tmp = p.projectOntoTangentSpace(q.globalCoordinates()); + Dune::FieldMatrix<T,4,1> column; + for (int i=0; i<4; i++) // turn row vector into column vector + column[i] = tmp[i]; - // Compute matrix B (see notes) - Dune::FieldMatrix<T,4,4> Pp, Pq; - for (int i=0; i<4; i++) - for (int j=0; j<4; j++) { - Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j]; - Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; - } + Dune::FieldMatrix<T,4,4> A; + // A = row * column + Dune::FMatrixHelp::multMatrix(column,row,A); + using std::abs; + A *= 4 * UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)); - Dune::FieldMatrix<T,4,4> B; - Dune::FMatrixHelp::multMatrix(Pp,Pq,B); + // Compute matrix B (see notes) + Dune::FieldMatrix<T,4,4> Pp, Pq; + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) { + Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j]; + Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; + } - // Bring it all together - A.axpy(4 * ( (sp<0) ? -1 : 1) * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)), B); + Dune::FieldMatrix<T,4,4> B; + Dune::FMatrixHelp::multMatrix(Pp,Pq,B); - return A; - } + // Bring it all together + A.axpy(4 * ( (sp<0) ? -1 : 1) * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)), B); - /** \brief Compute the third derivative \partial d^3 / \partial dq^3 + return A; + } - Unlike the distance itself the squared distance is differentiable at zero - */ - static Tensor3<T,4,4,4> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) { + /** \brief Compute the third derivative \partial d^3 / \partial dq^3 - Tensor3<T,4,4,4> result; + Unlike the distance itself the squared distance is differentiable at zero + */ + static Tensor3<T,4,4,4> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const Rotation<T,3>& p, const Rotation<T,3>& q) { - T sp = p.globalCoordinates() * q.globalCoordinates(); + Tensor3<T,4,4,4> result; - // The projection matrix onto the tangent space at p and q - Dune::FieldMatrix<T,4,4> Pq; - for (int i=0; i<4; i++) - for (int j=0; j<4; j++) - Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; + T sp = p.globalCoordinates() * q.globalCoordinates(); - EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); + // The projection matrix onto the tangent space at p and q + Dune::FieldMatrix<T,4,4> Pq; + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) + Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; - double plusMinus = (sp < 0) ? -1 : 1; + EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); - using std::abs; - for (int i=0; i<4; i++) - for (int j=0; j<4; j++) - for (int k=0; k<4; k++) { + double plusMinus = (sp < 0) ? -1 : 1; - result[i][j][k] = plusMinus * UnitVector<T,4>::thirdDerivativeOfArcCosSquared(abs(sp)) * pProjected[i] * pProjected[j] * pProjected[k] - - UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * ((i==j)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[j])*pProjected[k] - - UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * ((i==k)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[k])*pProjected[j] - - UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * pProjected[i] * Pq[j][k] * sp - + plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * ((i==j)*q.globalCoordinates()[k] + (i==k)*q.globalCoordinates()[j]) * sp - - plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * p.globalCoordinates()[i] * Pq[j][k]; - } + using std::abs; + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) + for (int k=0; k<4; k++) { - Pq *= 4; - result = Pq * result; + result[i][j][k] = plusMinus * UnitVector<T,4>::thirdDerivativeOfArcCosSquared(abs(sp)) * pProjected[i] * pProjected[j] * pProjected[k] + - UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * ((i==j)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[j])*pProjected[k] + - UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * ((i==k)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[k])*pProjected[j] + - UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * pProjected[i] * Pq[j][k] * sp + + plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * ((i==j)*q.globalCoordinates()[k] + (i==k)*q.globalCoordinates()[j]) * sp + - plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * p.globalCoordinates()[i] * Pq[j][k]; + } - return result; - } + Pq *= 4; + result = Pq * result; - /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2 + return result; + } - Unlike the distance itself the squared distance is differentiable at zero - */ - static Tensor3<T,4,4,4> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const Rotation<T,3>& p, const Rotation<T,3>& q) { + /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2 - Tensor3<T,4,4,4> result; + Unlike the distance itself the squared distance is differentiable at zero + */ + static Tensor3<T,4,4,4> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const Rotation<T,3>& p, const Rotation<T,3>& q) { - T sp = p.globalCoordinates() * q.globalCoordinates(); + Tensor3<T,4,4,4> result; - // The projection matrix onto the tangent space at p and q - Dune::FieldMatrix<T,4,4> Pp, Pq; - for (int i=0; i<4; i++) - for (int j=0; j<4; j++) { - Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j]; - Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; - } + T sp = p.globalCoordinates() * q.globalCoordinates(); - EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); - EmbeddedTangentVector qProjected = p.projectOntoTangentSpace(q.globalCoordinates()); + // The projection matrix onto the tangent space at p and q + Dune::FieldMatrix<T,4,4> Pp, Pq; + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) { + Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j]; + Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; + } - Tensor3<T,4,4,4> derivativeOfPqOTimesPq; - for (int i=0; i<4; i++) - for (int j=0; j<4; j++) - for (int k=0; k<4; k++) { - derivativeOfPqOTimesPq[i][j][k] = 0; - for (int l=0; l<4; l++) - derivativeOfPqOTimesPq[i][j][k] += Pp[i][l] * (Pq[j][l]*pProjected[k] + pProjected[j]*Pq[k][l]); - } + EmbeddedTangentVector pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); + EmbeddedTangentVector qProjected = p.projectOntoTangentSpace(q.globalCoordinates()); - double plusMinus = (sp < 0) ? -1 : 1; + Tensor3<T,4,4,4> derivativeOfPqOTimesPq; + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) + for (int k=0; k<4; k++) { + derivativeOfPqOTimesPq[i][j][k] = 0; + for (int l=0; l<4; l++) + derivativeOfPqOTimesPq[i][j][k] += Pp[i][l] * (Pq[j][l]*pProjected[k] + pProjected[j]*Pq[k][l]); + } - using std::abs; - result = plusMinus * UnitVector<T,4>::thirdDerivativeOfArcCosSquared(abs(sp)) * Tensor3<T,4,4,4>::product(qProjected,pProjected,pProjected) - + UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * derivativeOfPqOTimesPq - - UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * sp * Tensor3<T,4,4,4>::product(qProjected,Pq) - - plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * Tensor3<T,4,4,4>::product(qProjected,Pq); + double plusMinus = (sp < 0) ? -1 : 1; - result *= 4; + using std::abs; + result = plusMinus * UnitVector<T,4>::thirdDerivativeOfArcCosSquared(abs(sp)) * Tensor3<T,4,4,4>::product(qProjected,pProjected,pProjected) + + UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * derivativeOfPqOTimesPq + - UnitVector<T,4>::secondDerivativeOfArcCosSquared(abs(sp)) * sp * Tensor3<T,4,4,4>::product(qProjected,Pq) + - plusMinus * UnitVector<T,4>::derivativeOfArcCosSquared(abs(sp)) * Tensor3<T,4,4,4>::product(qProjected,Pq); - return result; + result *= 4; - } + return result; + } - /** \brief Interpolate between two rotations */ - static Rotation<T,3> interpolate(const Rotation<T,3>& a, const Rotation<T,3>& b, T omega) { - // Compute log on T_a SO(3) - EmbeddedTangentVector v = log(a,b); - return exp(a, omega*v); - } + /** \brief Interpolate between two rotations */ + static Rotation<T,3> interpolate(const Rotation<T,3>& a, const Rotation<T,3>& b, T omega) { - /** \brief Interpolate between two rotations - \param omega must be between 0 and 1 - */ - static Quaternion<T> interpolateDerivative(const Rotation<T,3>& a, const Rotation<T,3>& b, - T omega) { - Quaternion<T> result(0); + // Compute log on T_a SO(3) + EmbeddedTangentVector v = log(a,b); + return exp(a, omega*v); + } - // Compute log on T_a SO(3) - SkewMatrix<T,3> xi = log(a,b); + /** \brief Interpolate between two rotations + \param omega must be between 0 and 1 + */ + static Quaternion<T> interpolateDerivative(const Rotation<T,3>& a, const Rotation<T,3>& b, + T omega) { + Quaternion<T> result(0); - SkewMatrix<T,3> v = xi; - v *= omega; + // Compute log on T_a SO(3) + SkewMatrix<T,3> xi = log(a,b); - // ////////////////////////////////////////////////////////////// - // v now contains the derivative at 'a'. The derivative at - // the requested site is v pushed forward by Dexp. - // ///////////////////////////////////////////////////////////// + SkewMatrix<T,3> v = xi; + v *= omega; - Dune::FieldMatrix<T,4,3> diffExp = Dexp(v); + // ////////////////////////////////////////////////////////////// + // v now contains the derivative at 'a'. The derivative at + // the requested site is v pushed forward by Dexp. + // ///////////////////////////////////////////////////////////// - diffExp.umv(xi.axial(),result); + Dune::FieldMatrix<T,4,3> diffExp = Dexp(v); - return a.Quaternion<T>::mult(result); - } + diffExp.umv(xi.axial(),result); - /** \brief Return the corresponding orthogonal matrix */ - void matrix(Dune::FieldMatrix<T,3,3>& m) const { + return a.Quaternion<T>::mult(result); + } - m[0][0] = (*this)[0]*(*this)[0] - (*this)[1]*(*this)[1] - (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3]; - m[0][1] = 2 * ( (*this)[0]*(*this)[1] - (*this)[2]*(*this)[3] ); - m[0][2] = 2 * ( (*this)[0]*(*this)[2] + (*this)[1]*(*this)[3] ); + /** \brief Return the corresponding orthogonal matrix */ + void matrix(Dune::FieldMatrix<T,3,3>& m) const { - m[1][0] = 2 * ( (*this)[0]*(*this)[1] + (*this)[2]*(*this)[3] ); - m[1][1] = - (*this)[0]*(*this)[0] + (*this)[1]*(*this)[1] - (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3]; - m[1][2] = 2 * ( -(*this)[0]*(*this)[3] + (*this)[1]*(*this)[2] ); + m[0][0] = (*this)[0]*(*this)[0] - (*this)[1]*(*this)[1] - (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3]; + m[0][1] = 2 * ( (*this)[0]*(*this)[1] - (*this)[2]*(*this)[3] ); + m[0][2] = 2 * ( (*this)[0]*(*this)[2] + (*this)[1]*(*this)[3] ); - m[2][0] = 2 * ( (*this)[0]*(*this)[2] - (*this)[1]*(*this)[3] ); - m[2][1] = 2 * ( (*this)[0]*(*this)[3] + (*this)[1]*(*this)[2] ); - m[2][2] = - (*this)[0]*(*this)[0] - (*this)[1]*(*this)[1] + (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3]; + m[1][0] = 2 * ( (*this)[0]*(*this)[1] + (*this)[2]*(*this)[3] ); + m[1][1] = - (*this)[0]*(*this)[0] + (*this)[1]*(*this)[1] - (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3]; + m[1][2] = 2 * ( -(*this)[0]*(*this)[3] + (*this)[1]*(*this)[2] ); - } + m[2][0] = 2 * ( (*this)[0]*(*this)[2] - (*this)[1]*(*this)[3] ); + m[2][1] = 2 * ( (*this)[0]*(*this)[3] + (*this)[1]*(*this)[2] ); + m[2][2] = - (*this)[0]*(*this)[0] - (*this)[1]*(*this)[1] + (*this)[2]*(*this)[2] + (*this)[3]*(*this)[3]; - /** \brief Derivative of the map from unit quaternions to orthogonal matrices - */ - static Tensor3<T,3,3,4> derivativeOfQuaternionToMatrix(const Dune::FieldVector<T,4>& p) - { - Tensor3<T,3,3,4> result; + } - result[0][0] = { 2*p[0], -2*p[1], -2*p[2], 2*p[3]}; - result[0][1] = { 2*p[1], 2*p[0], -2*p[3], -2*p[2]}; - result[0][2] = { 2*p[2], 2*p[3], 2*p[0], 2*p[1]}; + /** \brief Derivative of the map from unit quaternions to orthogonal matrices + */ + static Tensor3<T,3,3,4> derivativeOfQuaternionToMatrix(const Dune::FieldVector<T,4>& p) + { + Tensor3<T,3,3,4> result; - result[1][0] = { 2*p[1], 2*p[0], 2*p[3], 2*p[2]}; - result[1][1] = {-2*p[0], 2*p[1], -2*p[2], 2*p[3]}; - result[1][2] = {-2*p[3], 2*p[2], 2*p[1], -2*p[0]}; + result[0][0] = { 2*p[0], -2*p[1], -2*p[2], 2*p[3]}; + result[0][1] = { 2*p[1], 2*p[0], -2*p[3], -2*p[2]}; + result[0][2] = { 2*p[2], 2*p[3], 2*p[0], 2*p[1]}; - result[2][0] = { 2*p[2], -2*p[3], 2*p[0], -2*p[1]}; - result[2][1] = { 2*p[3], 2*p[2], 2*p[1], 2*p[0]}; - result[2][2] = {-2*p[0], -2*p[1], 2*p[2], 2*p[3]}; + result[1][0] = { 2*p[1], 2*p[0], 2*p[3], 2*p[2]}; + result[1][1] = {-2*p[0], 2*p[1], -2*p[2], 2*p[3]}; + result[1][2] = {-2*p[3], 2*p[2], 2*p[1], -2*p[0]}; - return result; - } + result[2][0] = { 2*p[2], -2*p[3], 2*p[0], -2*p[1]}; + result[2][1] = { 2*p[3], 2*p[2], 2*p[1], 2*p[0]}; + result[2][2] = {-2*p[0], -2*p[1], 2*p[2], 2*p[3]}; - /** \brief Set rotation from orthogonal matrix + return result; + } - We tacitly assume that the matrix really is orthogonal */ - void set(const Dune::FieldMatrix<T,3,3>& m) { + /** \brief Set rotation from orthogonal matrix - using std::sqrt; + We tacitly assume that the matrix really is orthogonal */ + void set(const Dune::FieldMatrix<T,3,3>& m) { - // Easier writing - Dune::FieldVector<T,4>& p = (*this); - // The following equations for the derivation of a unit quaternion from a rotation - // matrix comes from 'E. Salamin, Application of Quaternions to Computation with - // Rotations, Technical Report, Stanford, 1974' + using std::sqrt; - p[0] = (1 + m[0][0] - m[1][1] - m[2][2]) / 4; - p[1] = (1 - m[0][0] + m[1][1] - m[2][2]) / 4; - p[2] = (1 - m[0][0] - m[1][1] + m[2][2]) / 4; - p[3] = (1 + m[0][0] + m[1][1] + m[2][2]) / 4; + // Easier writing + Dune::FieldVector<T,4>& p = (*this); + // The following equations for the derivation of a unit quaternion from a rotation + // matrix comes from 'E. Salamin, Application of Quaternions to Computation with + // Rotations, Technical Report, Stanford, 1974' - // avoid rounding problems - if (p[0] >= p[1] && p[0] >= p[2] && p[0] >= p[3]) { + p[0] = (1 + m[0][0] - m[1][1] - m[2][2]) / 4; + p[1] = (1 - m[0][0] + m[1][1] - m[2][2]) / 4; + p[2] = (1 - m[0][0] - m[1][1] + m[2][2]) / 4; + p[3] = (1 + m[0][0] + m[1][1] + m[2][2]) / 4; - p[0] = sqrt(p[0]); + // avoid rounding problems + if (p[0] >= p[1] && p[0] >= p[2] && p[0] >= p[3]) { - // r_x r_y = (R_12 + R_21) / 4 - p[1] = (m[0][1] + m[1][0]) / 4 / p[0]; + p[0] = sqrt(p[0]); - // r_x r_z = (R_13 + R_31) / 4 - p[2] = (m[0][2] + m[2][0]) / 4 / p[0]; + // r_x r_y = (R_12 + R_21) / 4 + p[1] = (m[0][1] + m[1][0]) / 4 / p[0]; - // r_0 r_x = (R_32 - R_23) / 4 - p[3] = (m[2][1] - m[1][2]) / 4 / p[0]; + // r_x r_z = (R_13 + R_31) / 4 + p[2] = (m[0][2] + m[2][0]) / 4 / p[0]; - } else if (p[1] >= p[0] && p[1] >= p[2] && p[1] >= p[3]) { + // r_0 r_x = (R_32 - R_23) / 4 + p[3] = (m[2][1] - m[1][2]) / 4 / p[0]; - p[1] = sqrt(p[1]); + } else if (p[1] >= p[0] && p[1] >= p[2] && p[1] >= p[3]) { - // r_x r_y = (R_12 + R_21) / 4 - p[0] = (m[0][1] + m[1][0]) / 4 / p[1]; + p[1] = sqrt(p[1]); - // r_y r_z = (R_23 + R_32) / 4 - p[2] = (m[1][2] + m[2][1]) / 4 / p[1]; + // r_x r_y = (R_12 + R_21) / 4 + p[0] = (m[0][1] + m[1][0]) / 4 / p[1]; - // r_0 r_y = (R_13 - R_31) / 4 - p[3] = (m[0][2] - m[2][0]) / 4 / p[1]; + // r_y r_z = (R_23 + R_32) / 4 + p[2] = (m[1][2] + m[2][1]) / 4 / p[1]; - } else if (p[2] >= p[0] && p[2] >= p[1] && p[2] >= p[3]) { + // r_0 r_y = (R_13 - R_31) / 4 + p[3] = (m[0][2] - m[2][0]) / 4 / p[1]; - p[2] = sqrt(p[2]); + } else if (p[2] >= p[0] && p[2] >= p[1] && p[2] >= p[3]) { - // r_x r_z = (R_13 + R_31) / 4 - p[0] = (m[0][2] + m[2][0]) / 4 / p[2]; + p[2] = sqrt(p[2]); - // r_y r_z = (R_23 + R_32) / 4 - p[1] = (m[1][2] + m[2][1]) / 4 / p[2]; + // r_x r_z = (R_13 + R_31) / 4 + p[0] = (m[0][2] + m[2][0]) / 4 / p[2]; - // r_0 r_z = (R_21 - R_12) / 4 - p[3] = (m[1][0] - m[0][1]) / 4 / p[2]; + // r_y r_z = (R_23 + R_32) / 4 + p[1] = (m[1][2] + m[2][1]) / 4 / p[2]; - } else { + // r_0 r_z = (R_21 - R_12) / 4 + p[3] = (m[1][0] - m[0][1]) / 4 / p[2]; - p[3] = sqrt(p[3]); + } else { - // r_0 r_x = (R_32 - R_23) / 4 - p[0] = (m[2][1] - m[1][2]) / 4 / p[3]; + p[3] = sqrt(p[3]); - // r_0 r_y = (R_13 - R_31) / 4 - p[1] = (m[0][2] - m[2][0]) / 4 / p[3]; + // r_0 r_x = (R_32 - R_23) / 4 + p[0] = (m[2][1] - m[1][2]) / 4 / p[3]; - // r_0 r_z = (R_21 - R_12) / 4 - p[2] = (m[1][0] - m[0][1]) / 4 / p[3]; + // r_0 r_y = (R_13 - R_31) / 4 + p[1] = (m[0][2] - m[2][0]) / 4 / p[3]; - } + // r_0 r_z = (R_21 - R_12) / 4 + p[2] = (m[1][0] - m[0][1]) / 4 / p[3]; } - /** \brief Derivative of the map from orthogonal matrices to unit quaternions + } - We tacitly assume that the matrix really is orthogonal */ - static Tensor3<T,4,3,3> derivativeOfMatrixToQuaternion(const Dune::FieldMatrix<T,3,3>& m) - { - using std::pow; - using std::sqrt; + /** \brief Derivative of the map from orthogonal matrices to unit quaternions - Tensor3<T,4,3,3> result; + We tacitly assume that the matrix really is orthogonal */ + static Tensor3<T,4,3,3> derivativeOfMatrixToQuaternion(const Dune::FieldMatrix<T,3,3>& m) + { + using std::pow; + using std::sqrt; - Dune::FieldVector<T,4> p; + Tensor3<T,4,3,3> result; - // The following equations for the derivation of a unit quaternion from a rotation - // matrix comes from 'E. Salamin, Application of Quaternions to Computation with - // Rotations, Technical Report, Stanford, 1974' + Dune::FieldVector<T,4> p; - p[0] = (1 + m[0][0] - m[1][1] - m[2][2]) / 4; - p[1] = (1 - m[0][0] + m[1][1] - m[2][2]) / 4; - p[2] = (1 - m[0][0] - m[1][1] + m[2][2]) / 4; - p[3] = (1 + m[0][0] + m[1][1] + m[2][2]) / 4; + // The following equations for the derivation of a unit quaternion from a rotation + // matrix comes from 'E. Salamin, Application of Quaternions to Computation with + // Rotations, Technical Report, Stanford, 1974' - // avoid rounding problems - if (p[0] >= p[1] && p[0] >= p[2] && p[0] >= p[3]) - { - result[0] = {{1,0,0},{0,-1,0},{0,0,-1}}; - result[0] *= 1.0/(8.0*sqrt(p[0])); + p[0] = (1 + m[0][0] - m[1][1] - m[2][2]) / 4; + p[1] = (1 - m[0][0] + m[1][1] - m[2][2]) / 4; + p[2] = (1 - m[0][0] - m[1][1] + m[2][2]) / 4; + p[3] = (1 + m[0][0] + m[1][1] + m[2][2]) / 4; - T denom = 32 * pow(p[0],1.5); - T offDiag = 1.0/(4*sqrt(p[0])); + // avoid rounding problems + if (p[0] >= p[1] && p[0] >= p[2] && p[0] >= p[3]) + { + result[0] = {{1,0,0},{0,-1,0},{0,0,-1}}; + result[0] *= 1.0/(8.0*sqrt(p[0])); - result[1] = { {-(m[0][1]+m[1][0]) / denom, offDiag, 0}, - {offDiag, (m[0][1]+m[1][0]) / denom, 0}, - {0, 0, (m[0][1]+m[1][0]) / denom}}; + T denom = 32 * pow(p[0],1.5); + T offDiag = 1.0/(4*sqrt(p[0])); - result[2] = { {-(m[0][2]+m[2][0]) / denom, 0, offDiag}, - {0, (m[0][2]+m[2][0]) / denom, 0}, - {offDiag, 0, (m[0][2]+m[2][0]) / denom}}; + result[1] = { {-(m[0][1]+m[1][0]) / denom, offDiag, 0}, + {offDiag, (m[0][1]+m[1][0]) / denom, 0}, + {0, 0, (m[0][1]+m[1][0]) / denom}}; - result[3] = { {-(m[2][1]-m[1][2]) / denom, 0, 0}, - {0, (m[2][1]-m[1][2]) / denom, -offDiag}, - {0, offDiag, (m[2][1]-m[1][2]) / denom}}; - } - else if (p[1] >= p[0] && p[1] >= p[2] && p[1] >= p[3]) - { - result[1] = {{-1,0,0},{0,1,0},{0,0,-1}}; - result[1] *= 1.0/(8.0*sqrt(p[1])); + result[2] = { {-(m[0][2]+m[2][0]) / denom, 0, offDiag}, + {0, (m[0][2]+m[2][0]) / denom, 0}, + {offDiag, 0, (m[0][2]+m[2][0]) / denom}}; - T denom = 32 * pow(p[1],1.5); - T offDiag = 1.0/(4*sqrt(p[1])); + result[3] = { {-(m[2][1]-m[1][2]) / denom, 0, 0}, + {0, (m[2][1]-m[1][2]) / denom, -offDiag}, + {0, offDiag, (m[2][1]-m[1][2]) / denom}}; + } + else if (p[1] >= p[0] && p[1] >= p[2] && p[1] >= p[3]) + { + result[1] = {{-1,0,0},{0,1,0},{0,0,-1}}; + result[1] *= 1.0/(8.0*sqrt(p[1])); - result[0] = { {(m[0][1]+m[1][0]) / denom, offDiag, 0}, - {offDiag, -(m[0][1]+m[1][0]) / denom, 0}, - {0, 0, (m[0][1]+m[1][0]) / denom}}; + T denom = 32 * pow(p[1],1.5); + T offDiag = 1.0/(4*sqrt(p[1])); - result[2] = { {(m[1][2]+m[2][1]) / denom, 0 , 0}, - {0, -(m[1][2]+m[2][1]) / denom, offDiag}, - {0, offDiag, (m[1][2]+m[2][1]) / denom}}; + result[0] = { {(m[0][1]+m[1][0]) / denom, offDiag, 0}, + {offDiag, -(m[0][1]+m[1][0]) / denom, 0}, + {0, 0, (m[0][1]+m[1][0]) / denom}}; - result[3] = { {(m[0][2]-m[2][0]) / denom, 0, offDiag}, - {0, -(m[0][2]-m[2][0]) / denom, 0}, - {-offDiag, 0, (m[0][2]-m[2][0]) / denom}}; - } - else if (p[2] >= p[0] && p[2] >= p[1] && p[2] >= p[3]) - { - result[2] = {{-1,0,0},{0,-1,0},{0,0,1}}; - result[2] *= 1.0/(8.0*sqrt(p[2])); + result[2] = { {(m[1][2]+m[2][1]) / denom, 0 , 0}, + {0, -(m[1][2]+m[2][1]) / denom, offDiag}, + {0, offDiag, (m[1][2]+m[2][1]) / denom}}; - T denom = 32 * pow(p[2],1.5); - T offDiag = 1.0/(4*sqrt(p[2])); + result[3] = { {(m[0][2]-m[2][0]) / denom, 0, offDiag}, + {0, -(m[0][2]-m[2][0]) / denom, 0}, + {-offDiag, 0, (m[0][2]-m[2][0]) / denom}}; + } + else if (p[2] >= p[0] && p[2] >= p[1] && p[2] >= p[3]) + { + result[2] = {{-1,0,0},{0,-1,0},{0,0,1}}; + result[2] *= 1.0/(8.0*sqrt(p[2])); - result[0] = { {(m[0][2]+m[2][0]) / denom, 0, offDiag}, - {0, (m[0][2]+m[2][0]) / denom, 0}, - {offDiag, 0, -(m[0][2]+m[2][0]) / denom}}; + T denom = 32 * pow(p[2],1.5); + T offDiag = 1.0/(4*sqrt(p[2])); - result[1] = { {(m[1][2]+m[2][1]) / denom, 0 , 0}, - {0, (m[1][2]+m[2][1]) / denom, offDiag}, - {0, offDiag, -(m[1][2]+m[2][1]) / denom}}; + result[0] = { {(m[0][2]+m[2][0]) / denom, 0, offDiag}, + {0, (m[0][2]+m[2][0]) / denom, 0}, + {offDiag, 0, -(m[0][2]+m[2][0]) / denom}}; - result[3] = { {(m[1][0]-m[0][1]) / denom, -offDiag, 0}, - {offDiag, (m[1][0]-m[0][1]) / denom, 0}, - {0, 0, -(m[1][0]-m[0][1]) / denom}}; - } - else - { - result[3] = {{1,0,0},{0,1,0},{0,0,1}}; - result[3] *= 1.0/(8.0*sqrt(p[3])); + result[1] = { {(m[1][2]+m[2][1]) / denom, 0 , 0}, + {0, (m[1][2]+m[2][1]) / denom, offDiag}, + {0, offDiag, -(m[1][2]+m[2][1]) / denom}}; - T denom = 32 * pow(p[3],1.5); - T offDiag = 1.0/(4*sqrt(p[3])); + result[3] = { {(m[1][0]-m[0][1]) / denom, -offDiag, 0}, + {offDiag, (m[1][0]-m[0][1]) / denom, 0}, + {0, 0, -(m[1][0]-m[0][1]) / denom}}; + } + else + { + result[3] = {{1,0,0},{0,1,0},{0,0,1}}; + result[3] *= 1.0/(8.0*sqrt(p[3])); - result[0] = { {-(m[2][1]-m[1][2]) / denom, 0, 0}, - {0, -(m[2][1]-m[1][2]) / denom, -offDiag}, - {0, offDiag, -(m[2][1]-m[1][2]) / denom}}; + T denom = 32 * pow(p[3],1.5); + T offDiag = 1.0/(4*sqrt(p[3])); - result[1] = { {-(m[0][2]-m[2][0]) / denom, 0, offDiag}, - {0, -(m[0][2]-m[2][0]) / denom, 0}, - {-offDiag, 0, -(m[0][2]-m[2][0]) / denom}}; + result[0] = { {-(m[2][1]-m[1][2]) / denom, 0, 0}, + {0, -(m[2][1]-m[1][2]) / denom, -offDiag}, + {0, offDiag, -(m[2][1]-m[1][2]) / denom}}; - result[2] = { {-(m[1][0]-m[0][1]) / denom, -offDiag, 0}, - {offDiag, -(m[1][0]-m[0][1]) / denom, 0}, - {0, 0, -(m[1][0]-m[0][1]) / denom}}; - } - return result; - } + result[1] = { {-(m[0][2]-m[2][0]) / denom, 0, offDiag}, + {0, -(m[0][2]-m[2][0]) / denom, 0}, + {-offDiag, 0, -(m[0][2]-m[2][0]) / denom}}; - /** \brief Project tangent vector of R^n onto the tangent space */ - EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const { - EmbeddedTangentVector result = v; - EmbeddedTangentVector data = *this; - result.axpy(-1*(data*result), data); - return result; + result[2] = { {-(m[1][0]-m[0][1]) / denom, -offDiag, 0}, + {offDiag, -(m[1][0]-m[0][1]) / denom, 0}, + {0, 0, -(m[1][0]-m[0][1]) / denom}}; } + return result; + } - /** \brief Project tangent vector of R^n onto the normal space space */ - EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const { - Dune::FieldVector<T,4> data = *this; - T sp = v*data; - EmbeddedTangentVector result = *this; - result *= sp; - return result; - } + /** \brief Project tangent vector of R^n onto the tangent space */ + EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const { + EmbeddedTangentVector result = v; + EmbeddedTangentVector data = *this; + result.axpy(-1*(data*result), data); + return result; + } - /** \brief The Weingarten map */ - EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const { + /** \brief Project tangent vector of R^n onto the normal space space */ + EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const { + Dune::FieldVector<T,4> data = *this; + T sp = v*data; + EmbeddedTangentVector result = *this; + result *= sp; + return result; + } - EmbeddedTangentVector result; + /** \brief The Weingarten map */ + EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const { - T sp = v*(*this); + EmbeddedTangentVector result; - for (int i=0; i<embeddedDim; i++) - result[i] = -sp * z[i]; + T sp = v*(*this); - return result; - } + for (int i=0; i<embeddedDim; i++) + result[i] = -sp * z[i]; - /** \brief Project a vector in R^4 onto the unit quaternions - * - * \warning This is NOT the standard projection from R^{3 \times 3} onto SO(3)! - */ - static Rotation<T,3> projectOnto(const CoordinateType& p) - { - Rotation<T,3> result(p); - result /= result.two_norm(); - return result; - } + return result; + } - /** \brief Derivative of the projection of a vector in R^4 onto the unit quaternions - * - * \warning This is NOT the standard projection from R^{3 \times 3} onto SO(3)! - */ - static auto derivativeOfProjection(const Dune::FieldVector<T,4>& p) - { - Dune::FieldMatrix<T,4,4> result; - for (int i=0; i<4; i++) - for (int j=0; j<4; j++) - result[i][j] = ( (i==j) - p[i]*p[j] / p.two_norm2() ) / p.two_norm(); - return result; - } + /** \brief Project a vector in R^4 onto the unit quaternions + * + * \warning This is NOT the standard projection from R^{3 \times 3} onto SO(3)! + */ + static Rotation<T,3> projectOnto(const CoordinateType& p) + { + Rotation<T,3> result(p); + result /= result.two_norm(); + return result; + } - /** \brief The global coordinates, if you really want them */ - const CoordinateType& globalCoordinates() const { - return *this; - } + /** \brief Derivative of the projection of a vector in R^4 onto the unit quaternions + * + * \warning This is NOT the standard projection from R^{3 \times 3} onto SO(3)! + */ + static auto derivativeOfProjection(const Dune::FieldVector<T,4>& p) + { + Dune::FieldMatrix<T,4,4> result; + for (int i=0; i<4; i++) + for (int j=0; j<4; j++) + result[i][j] = ( (i==j) - p[i]*p[j] / p.two_norm2() ) / p.two_norm(); + return result; + } - /** \brief Compute an orthonormal basis of the tangent space of the unit quaternions - * - * Since the unit quaternions are an odd-dimensional sphere, there exists a globally - * continuous orthonormal frame field. I took the expression here from - * "Dichmann, Li, Maddocks, 'Hamiltonian Formulations and Symmetries in Rod Mechanics', page 83" - */ - Dune::FieldMatrix<T,3,4> orthonormalFrame() const { - return {{ (*this)[3], (*this)[2], -(*this)[1], -(*this)[0]}, - {-(*this)[2], (*this)[3], (*this)[0], -(*this)[1]}, - { (*this)[1], -(*this)[0], (*this)[3], -(*this)[2]}}; + /** \brief The global coordinates, if you really want them */ + const CoordinateType& globalCoordinates() const { + return *this; + } - } + /** \brief Compute an orthonormal basis of the tangent space of the unit quaternions + * + * Since the unit quaternions are an odd-dimensional sphere, there exists a globally + * continuous orthonormal frame field. I took the expression here from + * "Dichmann, Li, Maddocks, 'Hamiltonian Formulations and Symmetries in Rod Mechanics', page 83" + */ + Dune::FieldMatrix<T,3,4> orthonormalFrame() const { + return {{ (*this)[3], (*this)[2], -(*this)[1], -(*this)[0]}, + {-(*this)[2], (*this)[3], (*this)[0], -(*this)[1]}, + { (*this)[1], -(*this)[0], (*this)[3], -(*this)[2]}}; + + } }; diff --git a/dune/gfe/spaces/unitvector.hh b/dune/gfe/spaces/unitvector.hh index d3d19dd3..9c6d3e07 100644 --- a/dune/gfe/spaces/unitvector.hh +++ b/dune/gfe/spaces/unitvector.hh @@ -16,538 +16,538 @@ class Rotation; \tparam N Dimension of the embedding space \tparam T The type used for individual coordinates -*/ + */ template <class T, int N> class UnitVector { - // Rotation<T,3> is friend, because it needs the various derivatives of the arccos - friend class Rotation<T,3>; - - /** \brief Computes sin(x) / x without getting unstable for small x */ - static T sinc(const T& x) { - using std::sin; - return (x < 1e-2) ? 1.0-x*x/6.0+ Dune::power(x,4)/120.0-Dune::power(x,6)/5040.0+Dune::power(x,8)/362880.0 : sin(x)/x; - } - - /** \brief Compute arccos^2 without using the (at 1) nondifferentiable function acos x close to 1 */ - static T arcCosSquared(const T& x) { - using std::acos; - const T eps = 1e-2; - if (x > 1-eps) { // acos is not differentiable, use the series expansion instead, - // we need here lots of terms to be sure that the numerical derivatives are also within maschine precision - //return -2 * (x-1) + 1.0/3 * (x-1)*(x-1) - 4.0/45 * (x-1)*(x-1)*(x-1); - return 11665028.0/4729725.0 - -141088.0/45045.0*x - + 413.0/429.0*x*x - - 5344.0/12285.0*Dune::power(x,3) - + 245.0/1287.0*Dune::power(x,4) - - 1632.0/25025.0*Dune::power(x,5) - + 56.0/3861.0*Dune::power(x,6) - - 32.0/21021.0*Dune::power(x,7); - } else { - return Dune::power(acos(x),2); - } + // Rotation<T,3> is friend, because it needs the various derivatives of the arccos + friend class Rotation<T,3>; + + /** \brief Computes sin(x) / x without getting unstable for small x */ + static T sinc(const T& x) { + using std::sin; + return (x < 1e-2) ? 1.0-x*x/6.0+ Dune::power(x,4)/120.0-Dune::power(x,6)/5040.0+Dune::power(x,8)/362880.0 : sin(x)/x; + } + + /** \brief Compute arccos^2 without using the (at 1) nondifferentiable function acos x close to 1 */ + static T arcCosSquared(const T& x) { + using std::acos; + const T eps = 1e-2; + if (x > 1-eps) { // acos is not differentiable, use the series expansion instead, + // we need here lots of terms to be sure that the numerical derivatives are also within maschine precision + //return -2 * (x-1) + 1.0/3 * (x-1)*(x-1) - 4.0/45 * (x-1)*(x-1)*(x-1); + return 11665028.0/4729725.0 + -141088.0/45045.0*x + + 413.0/429.0*x*x + - 5344.0/12285.0*Dune::power(x,3) + + 245.0/1287.0*Dune::power(x,4) + - 1632.0/25025.0*Dune::power(x,5) + + 56.0/3861.0*Dune::power(x,6) + - 32.0/21021.0*Dune::power(x,7); + } else { + return Dune::power(acos(x),2); } - - /** \brief Compute the derivative of arccos^2 without getting unstable for x close to 1 */ - static T derivativeOfArcCosSquared(const T& x) { - using std::acos; - using std::sqrt; - const T eps = 1e-2; - if (x > 1-eps) { // regular expression is unstable, use the series expansion instead - // we need here lots of terms to be sure that the numerical derivatives are also within maschine precision - //return -2 + 2*(x-1)/3 - 4/15*(x-1)*(x-1); - return -47104.0/15015.0 - +12614.0/6435.0*x - -63488.0/45045.0*x*x - + 1204.0/1287.0*Dune::power(x,3) - - 2048.0/4095.0*Dune::power(x,4) - + 112.0/585.0*Dune::power(x,5) - -2048.0/45045.0*Dune::power(x,6) - + 32.0/6435.0*Dune::power(x,7); - - } else if (x < -1+eps) { // The function is not differentiable - DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!"); - } else - return -2*acos(x) / sqrt(1-x*x); + } + + /** \brief Compute the derivative of arccos^2 without getting unstable for x close to 1 */ + static T derivativeOfArcCosSquared(const T& x) { + using std::acos; + using std::sqrt; + const T eps = 1e-2; + if (x > 1-eps) { // regular expression is unstable, use the series expansion instead + // we need here lots of terms to be sure that the numerical derivatives are also within maschine precision + //return -2 + 2*(x-1)/3 - 4/15*(x-1)*(x-1); + return -47104.0/15015.0 + +12614.0/6435.0*x + -63488.0/45045.0*x*x + + 1204.0/1287.0*Dune::power(x,3) + - 2048.0/4095.0*Dune::power(x,4) + + 112.0/585.0*Dune::power(x,5) + -2048.0/45045.0*Dune::power(x,6) + + 32.0/6435.0*Dune::power(x,7); + + } else if (x < -1+eps) { // The function is not differentiable + DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!"); + } else + return -2*acos(x) / sqrt(1-x*x); + } + + /** \brief Compute the second derivative of arccos^2 without getting unstable for x close to 1 */ + static T secondDerivativeOfArcCosSquared(const T& x) { + using std::acos; + using std::pow; + const T eps = 1e-2; + if (x > 1-eps) { // regular expression is unstable, use the series expansion instead + // we need here lots of terms to be sure that the numerical derivatives are also within maschine precision + //return 2.0/3 - 8*(x-1)/15; + return 1350030.0/676039.0+5632.0/2028117.0*Dune::power(x,10) + -1039056896.0/334639305.0*x + +150876.0/39767.0*x*x + -445186048.0/111546435.0*Dune::power(x,3) + + 343728.0/96577.0*Dune::power(x,4) + - 57769984.0/22309287.0*Dune::power(x,5) + + 710688.0/482885.0*Dune::power(x,6) + - 41615360.0/66927861.0*Dune::power(x,7) + + 616704.0/3380195.0*Dune::power(x,8) + - 245760.0/7436429.0*Dune::power(x,9); + } else if (x < -1+eps) { // The function is not differentiable + DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!"); + } else + return 2/(1-x*x) - 2*x*acos(x) / pow(1-x*x,1.5); + } + + /** \brief Compute the third derivative of arccos^2 without getting unstable for x close to 1 */ + static T thirdDerivativeOfArcCosSquared(const T& x) { + using std::acos; + using std::sqrt; + const T eps = 1e-2; + if (x > 1-eps) { // regular expression is unstable, use the series expansion instead + // we need here lots of terms to be sure that the numerical derivatives are also within maschine precision + //return -8.0/15 + 24*(x-1)/35; + return -1039056896.0/334639305.0 + +301752.0/39767.0*x + -445186048.0/37182145.0*x*x + +1374912.0/96577.0*Dune::power(x,3) + -288849920.0/22309287.0*Dune::power(x,4) + +4264128.0/482885.0*Dune::power(x,5) + -41615360.0/9561123.0*Dune::power(x,6) + +4933632.0/3380195.0*Dune::power(x,7) + -2211840.0/7436429.0*Dune::power(x,8) + +56320.0/2028117.0*Dune::power(x,9); + } else if (x < -1+eps) { // The function is not differentiable + + DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!"); + } else { + T d = 1-x*x; + return 6*x/(d*d) - 6*x*x*acos(x)/(d*d*sqrt(d)) - 2*acos(x)/(d*sqrt(d)); } + } - /** \brief Compute the second derivative of arccos^2 without getting unstable for x close to 1 */ - static T secondDerivativeOfArcCosSquared(const T& x) { - using std::acos; - using std::pow; - const T eps = 1e-2; - if (x > 1-eps) { // regular expression is unstable, use the series expansion instead - // we need here lots of terms to be sure that the numerical derivatives are also within maschine precision - //return 2.0/3 - 8*(x-1)/15; - return 1350030.0/676039.0+5632.0/2028117.0*Dune::power(x,10) - -1039056896.0/334639305.0*x - +150876.0/39767.0*x*x - -445186048.0/111546435.0*Dune::power(x,3) - + 343728.0/96577.0*Dune::power(x,4) - - 57769984.0/22309287.0*Dune::power(x,5) - + 710688.0/482885.0*Dune::power(x,6) - - 41615360.0/66927861.0*Dune::power(x,7) - + 616704.0/3380195.0*Dune::power(x,8) - - 245760.0/7436429.0*Dune::power(x,9); - } else if (x < -1+eps) { // The function is not differentiable - DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!"); - } else - return 2/(1-x*x) - 2*x*acos(x) / pow(1-x*x,1.5); - } - - /** \brief Compute the third derivative of arccos^2 without getting unstable for x close to 1 */ - static T thirdDerivativeOfArcCosSquared(const T& x) { - using std::acos; - using std::sqrt; - const T eps = 1e-2; - if (x > 1-eps) { // regular expression is unstable, use the series expansion instead - // we need here lots of terms to be sure that the numerical derivatives are also within maschine precision - //return -8.0/15 + 24*(x-1)/35; - return -1039056896.0/334639305.0 - +301752.0/39767.0*x - -445186048.0/37182145.0*x*x - +1374912.0/96577.0*Dune::power(x,3) - -288849920.0/22309287.0*Dune::power(x,4) - +4264128.0/482885.0*Dune::power(x,5) - -41615360.0/9561123.0*Dune::power(x,6) - +4933632.0/3380195.0*Dune::power(x,7) - -2211840.0/7436429.0*Dune::power(x,8) - +56320.0/2028117.0*Dune::power(x,9); - } else if (x < -1+eps) { // The function is not differentiable - - DUNE_THROW(Dune::Exception, "arccos^2 is not differentiable at x==-1!"); - } else { - T d = 1-x*x; - return 6*x/(d*d) - 6*x*x*acos(x)/(d*d*sqrt(d)) - 2*acos(x)/(d*sqrt(d)); - } - } - - template <class T2, int N2> - friend class UnitVector; + template <class T2, int N2> + friend class UnitVector; public: - /** \brief The type used for coordinates */ - typedef T ctype; - typedef T field_type; - - /** \brief The type used for global coordinates */ - typedef Dune::FieldVector<T,N> CoordinateType; + /** \brief The type used for coordinates */ + typedef T ctype; + typedef T field_type; + + /** \brief The type used for global coordinates */ + typedef Dune::FieldVector<T,N> CoordinateType; + + /** \brief Dimension of the manifold formed by unit vectors */ + static const int dim = N-1; - /** \brief Dimension of the manifold formed by unit vectors */ - static const int dim = N-1; + /** \brief Dimension of the Euclidean space the manifold is embedded in */ + static const int embeddedDim = N; + + typedef Dune::FieldVector<T,N-1> TangentVector; + + typedef Dune::FieldVector<T,N> EmbeddedTangentVector; - /** \brief Dimension of the Euclidean space the manifold is embedded in */ - static const int embeddedDim = N; + /** \brief The global convexity radius of the unit sphere */ + static constexpr double convexityRadius = 0.5*M_PI; - typedef Dune::FieldVector<T,N-1> TangentVector; + /** \brief The return type of the derivativeOfProjection method */ + typedef Dune::FieldMatrix<T, N, N> DerivativeOfProjection; - typedef Dune::FieldVector<T,N> EmbeddedTangentVector; + /** \brief Default constructor */ + UnitVector() + {} - /** \brief The global convexity radius of the unit sphere */ - static constexpr double convexityRadius = 0.5*M_PI; + /** \brief Constructor from a vector. The vector gets normalized! */ + UnitVector(const Dune::FieldVector<T,N>& vector) + : data_(vector) + { + data_ /= data_.two_norm(); + } - /** \brief The return type of the derivativeOfProjection method */ - typedef Dune::FieldMatrix<T, N, N> DerivativeOfProjection; + /** \brief Constructor from an array. The array gets normalized! */ + UnitVector(const std::array<T,N>& vector) + { + for (int i=0; i<N; i++) + data_[i] = vector[i]; + data_ /= data_.two_norm(); + } - /** \brief Default constructor */ - UnitVector() - {} + /** \brief Assignment from UnitVector with different type -- used for automatic differentiation with ADOL-C */ + template <class T2> + UnitVector& operator <<= (const UnitVector<T2,N>& other) { + for (int i=0; i<N; i++) + data_[i] <<= other.data_[i]; + return *this; + } - /** \brief Constructor from a vector. The vector gets normalized! */ - UnitVector(const Dune::FieldVector<T,N>& vector) - : data_(vector) - { - data_ /= data_.two_norm(); - } - - /** \brief Constructor from an array. The array gets normalized! */ - UnitVector(const std::array<T,N>& vector) - { - for (int i=0; i<N; i++) - data_[i] = vector[i]; - data_ /= data_.two_norm(); - } + /** \brief Rebind the UnitVector to another coordinate type */ + template<class U> + struct rebind + { + typedef UnitVector<U,N> other; + }; - /** \brief Assignment from UnitVector with different type -- used for automatic differentiation with ADOL-C */ - template <class T2> - UnitVector& operator <<= (const UnitVector<T2,N>& other) { - for (int i=0; i<N; i++) - data_[i] <<= other.data_[i]; - return *this; - } - /** \brief Rebind the UnitVector to another coordinate type */ - template<class U> - struct rebind - { - typedef UnitVector<U,N> other; - }; + UnitVector<T,N>& operator=(const Dune::FieldVector<T,N>& vector) + { + data_ = vector; + data_ /= data_.two_norm(); + return *this; + } + /** \brief The exponential map */ + static UnitVector exp(const UnitVector& p, const TangentVector& v) { - UnitVector<T,N>& operator=(const Dune::FieldVector<T,N>& vector) - { - data_ = vector; - data_ /= data_.two_norm(); - return *this; - } + Dune::FieldMatrix<T,N-1,N> frame = p.orthonormalFrame(); - /** \brief The exponential map */ - static UnitVector exp(const UnitVector& p, const TangentVector& v) { + EmbeddedTangentVector ev; + frame.mtv(v,ev); - Dune::FieldMatrix<T,N-1,N> frame = p.orthonormalFrame(); + return exp(p,ev); + } - EmbeddedTangentVector ev; - frame.mtv(v,ev); + /** \brief The exponential map */ + static UnitVector exp(const UnitVector& p, const EmbeddedTangentVector& v) { - return exp(p,ev); - } + using std::abs; + using std::cos; + assert( abs(p.data_*v) < 1e-5 ); - /** \brief The exponential map */ - static UnitVector exp(const UnitVector& p, const EmbeddedTangentVector& v) { - - using std::abs; - using std::cos; - assert( abs(p.data_*v) < 1e-5 ); - - const T norm = v.two_norm(); - UnitVector result = p; - result.data_ *= cos(norm); - result.data_.axpy(sinc(norm), v); - return result; - } - - /** \brief The inverse of the exponential map - * - * \results A vector in the tangent space of p - */ - static EmbeddedTangentVector log(const UnitVector& p, const UnitVector& q) - { - EmbeddedTangentVector result = p.projectOntoTangentSpace(q.data_-p.data_); - if (result.two_norm() > 1e-10) - result *= distance(p,q) / result.two_norm(); - return result; - } + const T norm = v.two_norm(); + UnitVector result = p; + result.data_ *= cos(norm); + result.data_.axpy(sinc(norm), v); + return result; + } - /** \brief Length of the great arc connecting the two points */ - static T distance(const UnitVector& a, const UnitVector& b) { + /** \brief The inverse of the exponential map + * + * \results A vector in the tangent space of p + */ + static EmbeddedTangentVector log(const UnitVector& p, const UnitVector& q) + { + EmbeddedTangentVector result = p.projectOntoTangentSpace(q.data_-p.data_); + if (result.two_norm() > 1e-10) + result *= distance(p,q) / result.two_norm(); + return result; + } - using std::acos; - using std::min; + /** \brief Length of the great arc connecting the two points */ + static T distance(const UnitVector& a, const UnitVector& b) { - // Not nice: we are in a class for unit vectors, but the class is actually - // supposed to handle perturbations of unit vectors as well. Therefore - // we normalize here. - T x = a.data_ * b.data_/a.data_.two_norm()/b.data_.two_norm(); - - // paranoia: if the argument is just eps larger than 1 acos returns NaN - x = min(x,1.0); - - return acos(x); - } + using std::acos; + using std::min; + + // Not nice: we are in a class for unit vectors, but the class is actually + // supposed to handle perturbations of unit vectors as well. Therefore + // we normalize here. + T x = a.data_ * b.data_/a.data_.two_norm()/b.data_.two_norm(); + + // paranoia: if the argument is just eps larger than 1 acos returns NaN + x = min(x,1.0); + + return acos(x); + } #if ADOLC_ADOUBLE_H - /** \brief Squared length of the great arc connecting the two points */ - static adouble distanceSquared(const UnitVector<double,N>& a, const UnitVector<adouble,N>& b) - { - // Not nice: we are in a class for unit vectors, but the class is actually - // supposed to handle perturbations of unit vectors as well. Therefore - // we normalize here. - adouble x = a.data_ * b.data_ / (a.data_.two_norm()*b.data_.two_norm()); - - // paranoia: if the argument is just eps larger than 1 acos returns NaN - using std::min; - x = min(x,1.0); - - // Special implementation that remains AD-differentiable near x==1 - return arcCosSquared(x); - } + /** \brief Squared length of the great arc connecting the two points */ + static adouble distanceSquared(const UnitVector<double,N>& a, const UnitVector<adouble,N>& b) + { + // Not nice: we are in a class for unit vectors, but the class is actually + // supposed to handle perturbations of unit vectors as well. Therefore + // we normalize here. + adouble x = a.data_ * b.data_ / (a.data_.two_norm()*b.data_.two_norm()); + + // paranoia: if the argument is just eps larger than 1 acos returns NaN + using std::min; + x = min(x,1.0); + + // Special implementation that remains AD-differentiable near x==1 + return arcCosSquared(x); + } #endif - /** \brief Compute the gradient of the squared distance function keeping the first argument fixed + /** \brief Compute the gradient of the squared distance function keeping the first argument fixed - Unlike the distance itself the squared distance is differentiable at zero - */ - static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& a, const UnitVector& b) { - T x = a.data_ * b.data_; + Unlike the distance itself the squared distance is differentiable at zero + */ + static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& a, const UnitVector& b) { + T x = a.data_ * b.data_; - EmbeddedTangentVector result = a.data_; + EmbeddedTangentVector result = a.data_; - result *= derivativeOfArcCosSquared(x); + result *= derivativeOfArcCosSquared(x); - // Project gradient onto the tangent plane at b in order to obtain the surface gradient - result = b.projectOntoTangentSpace(result); + // Project gradient onto the tangent plane at b in order to obtain the surface gradient + result = b.projectOntoTangentSpace(result); - // Gradient must be a tangent vector at b, in other words, orthogonal to it - using std::abs; - assert(abs(b.data_ * result) < 1e-5); + // Gradient must be a tangent vector at b, in other words, orthogonal to it + using std::abs; + assert(abs(b.data_ * result) < 1e-5); - return result; - } + return result; + } - /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed + /** \brief Compute the Hessian of the squared distance function keeping the first argument fixed - Unlike the distance itself the squared distance is differentiable at zero - */ - static Dune::SymmetricMatrix<T,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& p, const UnitVector& q) { + Unlike the distance itself the squared distance is differentiable at zero + */ + static Dune::SymmetricMatrix<T,N> secondDerivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& p, const UnitVector& q) { - T sp = p.data_ * q.data_; + T sp = p.data_ * q.data_; - Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); + Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); - Dune::SymmetricMatrix<T,N> A; - for (int i=0; i<N; i++) - for (int j=0; j<=i; j++) - A(i,j) = pProjected[i]*pProjected[j]; + Dune::SymmetricMatrix<T,N> A; + for (int i=0; i<N; i++) + for (int j=0; j<=i; j++) + A(i,j) = pProjected[i]*pProjected[j]; - A *= secondDerivativeOfArcCosSquared(sp); + A *= secondDerivativeOfArcCosSquared(sp); - // Compute matrix B (see notes) - Dune::SymmetricMatrix<T,N> Pq; - for (int i=0; i<N; i++) - for (int j=0; j<=i; j++) - Pq(i,j) = (i==j) - q.data_[i]*q.data_[j]; + // Compute matrix B (see notes) + Dune::SymmetricMatrix<T,N> Pq; + for (int i=0; i<N; i++) + for (int j=0; j<=i; j++) + Pq(i,j) = (i==j) - q.data_[i]*q.data_[j]; - // Bring it all together - A.axpy(-1*derivativeOfArcCosSquared(sp)*sp, Pq); + // Bring it all together + A.axpy(-1*derivativeOfArcCosSquared(sp)*sp, Pq); - return A; - } + return A; + } - /** \brief Compute the mixed second derivate \partial d^2 / \partial da db + /** \brief Compute the mixed second derivate \partial d^2 / \partial da db - Unlike the distance itself the squared distance is differentiable at zero - */ - static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const UnitVector& a, const UnitVector& b) { + Unlike the distance itself the squared distance is differentiable at zero + */ + static Dune::FieldMatrix<T,N,N> secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(const UnitVector& a, const UnitVector& b) { - T sp = a.data_ * b.data_; + T sp = a.data_ * b.data_; - // Compute vector A (see notes) - Dune::FieldMatrix<T,1,N> row; - row[0] = b.projectOntoTangentSpace(a.globalCoordinates()); + // Compute vector A (see notes) + Dune::FieldMatrix<T,1,N> row; + row[0] = b.projectOntoTangentSpace(a.globalCoordinates()); - Dune::FieldVector<T,N> tmp = a.projectOntoTangentSpace(b.globalCoordinates()); - Dune::FieldMatrix<T,N,1> column; - for (int i=0; i<N; i++) // turn row vector into column vector - column[i] = tmp[i]; + Dune::FieldVector<T,N> tmp = a.projectOntoTangentSpace(b.globalCoordinates()); + Dune::FieldMatrix<T,N,1> column; + for (int i=0; i<N; i++) // turn row vector into column vector + column[i] = tmp[i]; - Dune::FieldMatrix<T,N,N> A; - // A = row * column - Dune::FMatrixHelp::multMatrix(column,row,A); - A *= secondDerivativeOfArcCosSquared(sp); + Dune::FieldMatrix<T,N,N> A; + // A = row * column + Dune::FMatrixHelp::multMatrix(column,row,A); + A *= secondDerivativeOfArcCosSquared(sp); - // Compute matrix B (see notes) - Dune::FieldMatrix<T,N,N> Pp, Pq; - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) { - Pp[i][j] = (i==j) - a.data_[i]*a.data_[j]; - Pq[i][j] = (i==j) - b.data_[i]*b.data_[j]; - } + // Compute matrix B (see notes) + Dune::FieldMatrix<T,N,N> Pp, Pq; + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) { + Pp[i][j] = (i==j) - a.data_[i]*a.data_[j]; + Pq[i][j] = (i==j) - b.data_[i]*b.data_[j]; + } - Dune::FieldMatrix<T,N,N> B; - Dune::FMatrixHelp::multMatrix(Pp,Pq,B); + Dune::FieldMatrix<T,N,N> B; + Dune::FMatrixHelp::multMatrix(Pp,Pq,B); - // Bring it all together - A.axpy(derivativeOfArcCosSquared(sp), B); + // Bring it all together + A.axpy(derivativeOfArcCosSquared(sp), B); - return A; - } + return A; + } - /** \brief Compute the third derivative \partial d^3 / \partial dq^3 + /** \brief Compute the third derivative \partial d^3 / \partial dq^3 - Unlike the distance itself the squared distance is differentiable at zero - */ - static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& p, const UnitVector& q) { + Unlike the distance itself the squared distance is differentiable at zero + */ + static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTSecondArgument(const UnitVector& p, const UnitVector& q) { - Tensor3<T,N,N,N> result; + Tensor3<T,N,N,N> result; - T sp = p.data_ * q.data_; + T sp = p.data_ * q.data_; - // The projection matrix onto the tangent space at p and q - Dune::FieldMatrix<T,N,N> Pq; - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; + // The projection matrix onto the tangent space at p and q + Dune::FieldMatrix<T,N,N> Pq; + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; - Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); + Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - for (int k=0; k<N; k++) { + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + for (int k=0; k<N; k++) { - result[i][j][k] = thirdDerivativeOfArcCosSquared(sp) * pProjected[i] * pProjected[j] * pProjected[k] - - secondDerivativeOfArcCosSquared(sp) * ((i==j)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[j])*pProjected[k] - - secondDerivativeOfArcCosSquared(sp) * ((i==k)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[k])*pProjected[j] - - secondDerivativeOfArcCosSquared(sp) * pProjected[i] * Pq[j][k] * sp - + derivativeOfArcCosSquared(sp) * ((i==j)*q.globalCoordinates()[k] + (i==k)*q.globalCoordinates()[j]) * sp - - derivativeOfArcCosSquared(sp) * p.globalCoordinates()[i] * Pq[j][k]; - } + result[i][j][k] = thirdDerivativeOfArcCosSquared(sp) * pProjected[i] * pProjected[j] * pProjected[k] + - secondDerivativeOfArcCosSquared(sp) * ((i==j)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[j])*pProjected[k] + - secondDerivativeOfArcCosSquared(sp) * ((i==k)*sp + p.globalCoordinates()[i]*q.globalCoordinates()[k])*pProjected[j] + - secondDerivativeOfArcCosSquared(sp) * pProjected[i] * Pq[j][k] * sp + + derivativeOfArcCosSquared(sp) * ((i==j)*q.globalCoordinates()[k] + (i==k)*q.globalCoordinates()[j]) * sp + - derivativeOfArcCosSquared(sp) * p.globalCoordinates()[i] * Pq[j][k]; + } - result = Pq * result; + result = Pq * result; - return result; - } + return result; + } - /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2 + /** \brief Compute the mixed third derivative \partial d^3 / \partial da db^2 - Unlike the distance itself the squared distance is differentiable at zero - */ - static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const UnitVector& p, const UnitVector& q) { + Unlike the distance itself the squared distance is differentiable at zero + */ + static Tensor3<T,N,N,N> thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(const UnitVector& p, const UnitVector& q) { - Tensor3<T,N,N,N> result; + Tensor3<T,N,N,N> result; - T sp = p.data_ * q.data_; + T sp = p.data_ * q.data_; - // The projection matrix onto the tangent space at p and q - Dune::FieldMatrix<T,N,N> Pp, Pq; - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) { - Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j]; - Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; - } + // The projection matrix onto the tangent space at p and q + Dune::FieldMatrix<T,N,N> Pp, Pq; + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) { + Pp[i][j] = (i==j) - p.globalCoordinates()[i]*p.globalCoordinates()[j]; + Pq[i][j] = (i==j) - q.globalCoordinates()[i]*q.globalCoordinates()[j]; + } - Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); - Dune::FieldVector<T,N> qProjected = p.projectOntoTangentSpace(q.globalCoordinates()); + Dune::FieldVector<T,N> pProjected = q.projectOntoTangentSpace(p.globalCoordinates()); + Dune::FieldVector<T,N> qProjected = p.projectOntoTangentSpace(q.globalCoordinates()); - Tensor3<T,N,N,N> derivativeOfPqOTimesPq; - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - for (int k=0; k<N; k++) { - derivativeOfPqOTimesPq[i][j][k] = 0; - for (int l=0; l<N; l++) - derivativeOfPqOTimesPq[i][j][k] += Pp[i][l] * (Pq[j][l]*pProjected[k] + pProjected[j]*Pq[k][l]); - } + Tensor3<T,N,N,N> derivativeOfPqOTimesPq; + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + for (int k=0; k<N; k++) { + derivativeOfPqOTimesPq[i][j][k] = 0; + for (int l=0; l<N; l++) + derivativeOfPqOTimesPq[i][j][k] += Pp[i][l] * (Pq[j][l]*pProjected[k] + pProjected[j]*Pq[k][l]); + } - result = thirdDerivativeOfArcCosSquared(sp) * Tensor3<T,N,N,N>::product(qProjected,pProjected,pProjected) - + secondDerivativeOfArcCosSquared(sp) * derivativeOfPqOTimesPq - - secondDerivativeOfArcCosSquared(sp) * sp * Tensor3<T,N,N,N>::product(qProjected,Pq) - - derivativeOfArcCosSquared(sp) * Tensor3<T,N,N,N>::product(qProjected,Pq); + result = thirdDerivativeOfArcCosSquared(sp) * Tensor3<T,N,N,N>::product(qProjected,pProjected,pProjected) + + secondDerivativeOfArcCosSquared(sp) * derivativeOfPqOTimesPq + - secondDerivativeOfArcCosSquared(sp) * sp * Tensor3<T,N,N,N>::product(qProjected,Pq) + - derivativeOfArcCosSquared(sp) * Tensor3<T,N,N,N>::product(qProjected,Pq); - return result; - } + return result; + } - /** \brief Project tangent vector of R^n onto the tangent space */ - EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const { - EmbeddedTangentVector result = v; - result.axpy(-1*(data_*result), data_); - return result; - } + /** \brief Project tangent vector of R^n onto the tangent space */ + EmbeddedTangentVector projectOntoTangentSpace(const EmbeddedTangentVector& v) const { + EmbeddedTangentVector result = v; + result.axpy(-1*(data_*result), data_); + return result; + } - /** \brief Project tangent vector of R^n onto the normal space space */ - EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const { + /** \brief Project tangent vector of R^n onto the normal space space */ + EmbeddedTangentVector projectOntoNormalSpace(const EmbeddedTangentVector& v) const { - EmbeddedTangentVector result; + EmbeddedTangentVector result; - T sp = 0; - for (int i=0; i<N; i++) - sp += v[i] * data_[i]; + T sp = 0; + for (int i=0; i<N; i++) + sp += v[i] * data_[i]; - for (int i=0; i<N; i++) - result[i] = sp * data_[i]; + for (int i=0; i<N; i++) + result[i] = sp * data_[i]; - return result; - } + return result; + } - /** \brief The Weingarten map */ - EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const { + /** \brief The Weingarten map */ + EmbeddedTangentVector weingarten(const EmbeddedTangentVector& z, const EmbeddedTangentVector& v) const { - EmbeddedTangentVector result; + EmbeddedTangentVector result; - T sp = 0; - for (int i=0; i<N; i++) - sp += v[i] * data_[i]; + T sp = 0; + for (int i=0; i<N; i++) + sp += v[i] * data_[i]; - for (int i=0; i<N; i++) - result[i] = -sp * z[i]; + for (int i=0; i<N; i++) + result[i] = -sp * z[i]; - return result; - } + return result; + } - static UnitVector<T,N> projectOnto(const CoordinateType& p) - { - UnitVector<T,N> result(p); - result.data_ /= result.data_.two_norm(); - return result; - } + static UnitVector<T,N> projectOnto(const CoordinateType& p) + { + UnitVector<T,N> result(p); + result.data_ /= result.data_.two_norm(); + return result; + } - static DerivativeOfProjection derivativeOfProjection(const Dune::FieldVector<T,N>& p) - { - auto normSquared = p.two_norm2(); - auto norm = std::sqrt(normSquared); + static DerivativeOfProjection derivativeOfProjection(const Dune::FieldVector<T,N>& p) + { + auto normSquared = p.two_norm2(); + auto norm = std::sqrt(normSquared); - Dune::FieldMatrix<T,N,N> result; - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - result[i][j] = ( (i==j) - p[i]*p[j] / normSquared ) / norm; - return result; - } + Dune::FieldMatrix<T,N,N> result; + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + result[i][j] = ( (i==j) - p[i]*p[j] / normSquared ) / norm; + return result; + } - /** \brief The global coordinates, if you really want them */ - const CoordinateType& globalCoordinates() const { - return data_; - } + /** \brief The global coordinates, if you really want them */ + const CoordinateType& globalCoordinates() const { + return data_; + } - /** \brief Compute an orthonormal basis of the tangent space of S^n. + /** \brief Compute an orthonormal basis of the tangent space of S^n. - This basis is of course not globally continuous. - */ - Dune::FieldMatrix<T,N-1,N> orthonormalFrame() const { + This basis is of course not globally continuous. + */ + Dune::FieldMatrix<T,N-1,N> orthonormalFrame() const { - Dune::FieldMatrix<T,N-1,N> result; + Dune::FieldMatrix<T,N-1,N> result; - // Coordinates of the stereographic projection - Dune::FieldVector<T,N-1> X; + // Coordinates of the stereographic projection + Dune::FieldVector<T,N-1> X; - if (data_[N-1] <= 0) { + if (data_[N-1] <= 0) { - // Stereographic projection from the north pole onto R^{N-1} - for (size_t i=0; i<N-1; i++) - X[i] = data_[i] / (1-data_[N-1]); + // Stereographic projection from the north pole onto R^{N-1} + for (size_t i=0; i<N-1; i++) + X[i] = data_[i] / (1-data_[N-1]); - } else { + } else { - // Stereographic projection from the south pole onto R^{N-1} - for (size_t i=0; i<N-1; i++) - X[i] = data_[i] / (1+data_[N-1]); + // Stereographic projection from the south pole onto R^{N-1} + for (size_t i=0; i<N-1; i++) + X[i] = data_[i] / (1+data_[N-1]); - } + } - T RSquared = X.two_norm2(); + T RSquared = X.two_norm2(); - for (size_t i=0; i<N-1; i++) - for (size_t j=0; j<N-1; j++) - // Note: the matrix is the transpose of the one in the paper - result[j][i] = 2*(i==j)*(1+RSquared) - 4*X[i]*X[j]; + for (size_t i=0; i<N-1; i++) + for (size_t j=0; j<N-1; j++) + // Note: the matrix is the transpose of the one in the paper + result[j][i] = 2*(i==j)*(1+RSquared) - 4*X[i]*X[j]; - for (size_t j=0; j<N-1; j++) - result[j][N-1] = 4*X[j]; + for (size_t j=0; j<N-1; j++) + result[j][N-1] = 4*X[j]; - // Upper hemisphere: adapt formulas so it is the stereographic projection from the south pole - if (data_[N-1] > 0) - for (size_t j=0; j<N-1; j++) - result[j][N-1] *= -1; + // Upper hemisphere: adapt formulas so it is the stereographic projection from the south pole + if (data_[N-1] > 0) + for (size_t j=0; j<N-1; j++) + result[j][N-1] *= -1; - // normalize the rows to make the orthogonal basis orthonormal - for (size_t i=0; i<N-1; i++) - result[i] /= result[i].two_norm(); + // normalize the rows to make the orthogonal basis orthonormal + for (size_t i=0; i<N-1; i++) + result[i] /= result[i].two_norm(); - return result; - } + return result; + } - /** \brief Write unit vector object to output stream */ - friend std::ostream& operator<< (std::ostream& s, const UnitVector& unitVector) - { - return s << unitVector.data_; - } + /** \brief Write unit vector object to output stream */ + friend std::ostream& operator<< (std::ostream& s, const UnitVector& unitVector) + { + return s << unitVector.data_; + } private: - Dune::FieldVector<T,N> data_; + Dune::FieldVector<T,N> data_; }; #endif diff --git a/dune/gfe/svd.hh b/dune/gfe/svd.hh index 763ab070..711093d2 100644 --- a/dune/gfe/svd.hh +++ b/dune/gfe/svd.hh @@ -1,6 +1,6 @@ /** \file \brief Singular value decomposition code taken from 'Numerical Recipes in C' -*/ + */ #ifndef SVD_HH #define SVD_HH @@ -16,12 +16,12 @@ template <class T> T pythag(T a, T b) { - T absa=std::fabs(a); - T absb=std::fabs(b); - if (absa > absb) - return absa*std::sqrt(T(1.0)+(absb/absa)*(absb/absa)); - else - return (absb == 0.0) ? T(0.0) : absb*sqrt(T(1.0)+(absa/absb)*(absa/absb)); + T absa=std::fabs(a); + T absb=std::fabs(b); + if (absa > absb) + return absa*std::sqrt(T(1.0)+(absb/absa)*(absb/absa)); + else + return (absb == 0.0) ? T(0.0) : absb*sqrt(T(1.0)+(absa/absb)*(absa/absb)); } @@ -29,236 +29,236 @@ T pythag(T a, T b) Given a matrix a[1..m][1..n], this routine computes its singular value decomposition, A = U W V^T . The matrix U replaces a on output. The diagonal matrix of singular values W is out- put as a vector w[1..n]. The matrix V (not the transpose V T ) is output as v[1..n][1..n]. -*/ + */ template <class T, int m, int n> void svdcmp(Dune::FieldMatrix<T,m,n>& a_, Dune::FieldVector<T,n>& w, Dune::FieldMatrix<T,n,n>& v_) { - Dune::FieldMatrix<T,m+1,n+1> a(0); - Dune::FieldMatrix<T,n+1,n+1> v(0); + Dune::FieldMatrix<T,m+1,n+1> a(0); + Dune::FieldMatrix<T,n+1,n+1> v(0); - for (int i=0; i<m; i++) - for (int j=0; j<n; j++) - a[i+1][j+1] = a_[i][j]; + for (int i=0; i<m; i++) + for (int j=0; j<n; j++) + a[i+1][j+1] = a_[i][j]; - int flag,i,its,j,jj,k,l,nm; - T anorm,c,f,g,h,s,scale,x,y,z; - T rv1[n+1]; // 1 too large to accommodate fortran numbering + int flag,i,its,j,jj,k,l,nm; + T anorm,c,f,g,h,s,scale,x,y,z; + T rv1[n+1]; // 1 too large to accommodate fortran numbering - //Householder reduction to bidiagonal form. - g=scale=anorm=0.0; + //Householder reduction to bidiagonal form. + g=scale=anorm=0.0; - for (i=1;i<=n;i++) { + for (i=1; i<=n; i++) { - l=i+1; - rv1[i]=scale*g; - g=s=scale=0.0; + l=i+1; + rv1[i]=scale*g; + g=s=scale=0.0; - if (i <= m) { - for (k=i;k<=m;k++) - scale += std::abs(a[k][i]); - if (scale!=0) { - for (k=i;k<=m;k++) { - a[k][i] /= scale; - s += a[k][i]*a[k][i]; - } - f=a[i][i]; - g = -SIGN(std::sqrt(s),f); - h=f*g-s; - a[i][i]=f-g; - for (j=l;j<=n;j++) { - for (s=0.0,k=i;k<=m;k++) - s += a[k][i]*a[k][j]; - f=s/h; - for (k=i;k<=m;k++) - a[k][j] += f*a[k][i]; - } - for (k=i;k<=m;k++) - a[k][i] *= scale; - } + if (i <= m) { + for (k=i; k<=m; k++) + scale += std::abs(a[k][i]); + if (scale!=0) { + for (k=i; k<=m; k++) { + a[k][i] /= scale; + s += a[k][i]*a[k][i]; } + f=a[i][i]; + g = -SIGN(std::sqrt(s),f); + h=f*g-s; + a[i][i]=f-g; + for (j=l; j<=n; j++) { + for (s=0.0,k=i; k<=m; k++) + s += a[k][i]*a[k][j]; + f=s/h; + for (k=i; k<=m; k++) + a[k][j] += f*a[k][i]; + } + for (k=i; k<=m; k++) + a[k][i] *= scale; + } + } - w[i-1]=scale *g; - g=s=scale=0.0; + w[i-1]=scale *g; + g=s=scale=0.0; - if (i <= m && i != n) { + if (i <= m && i != n) { - for (k=l;k<=n;k++) scale += fabs(a[i][k]); - if (scale!=0) { + for (k=l; k<=n; k++) scale += fabs(a[i][k]); + if (scale!=0) { - for (k=l;k<=n;k++) { - a[i][k] /= scale; - s += a[i][k]*a[i][k]; - } - f=a[i][l]; - g = -SIGN(std::sqrt(s),f); - h=f*g-s; - a[i][l]=f-g; - for (k=l;k<=n;k++) rv1[k]=a[i][k]/h; - for (j=l;j<=m;j++) { - for (s=0.0,k=l;k<=n;k++) - s += a[j][k]*a[i][k]; - for (k=l;k<=n;k++) - a[j][k] += s*rv1[k]; - } - for (k=l;k<=n;k++) - a[i][k] *= scale; - } + for (k=l; k<=n; k++) { + a[i][k] /= scale; + s += a[i][k]*a[i][k]; } + f=a[i][l]; + g = -SIGN(std::sqrt(s),f); + h=f*g-s; + a[i][l]=f-g; + for (k=l; k<=n; k++) rv1[k]=a[i][k]/h; + for (j=l; j<=m; j++) { + for (s=0.0,k=l; k<=n; k++) + s += a[j][k]*a[i][k]; + for (k=l; k<=n; k++) + a[j][k] += s*rv1[k]; + } + for (k=l; k<=n; k++) + a[i][k] *= scale; + } + } - anorm = std::max(anorm,(std::abs(w[i-1])+std::abs(rv1[i]))); + anorm = std::max(anorm,(std::abs(w[i-1])+std::abs(rv1[i]))); - } + } - // Accumulation of right-hand transformations. - for (i=n;i>=1;i--) { - if (i < n) { - if (g!=0) { - // Double division to avoid possible underflow. - for (j=l;j<=n;j++) - v[j][i]=(a[i][j]/a[i][l])/g; - for (j=l;j<=n;j++) { - for (s=0.0,k=l;k<=n;k++) s += a[i][k]*v[k][j]; - for (k=l;k<=n;k++) v[k][j] += s*v[k][i]; - } - } - for (j=l;j<=n;j++) v[i][j]=v[j][i]=0.0; + // Accumulation of right-hand transformations. + for (i=n; i>=1; i--) { + if (i < n) { + if (g!=0) { + // Double division to avoid possible underflow. + for (j=l; j<=n; j++) + v[j][i]=(a[i][j]/a[i][l])/g; + for (j=l; j<=n; j++) { + for (s=0.0,k=l; k<=n; k++) s += a[i][k]*v[k][j]; + for (k=l; k<=n; k++) v[k][j] += s*v[k][i]; } - v[i][i]=1.0; - g=rv1[i]; - l=i; + } + for (j=l; j<=n; j++) v[i][j]=v[j][i]=0.0; } + v[i][i]=1.0; + g=rv1[i]; + l=i; + } - // Accumulation of left-hand transformations. - //for (i=IMIN(m,n);i>=1;i--) { - for (i=std::min(m,n);i>=1;i--) { - l=i+1; - g=w[i-1]; - for (j=l;j<=n;j++) a[i][j]=0.0; - if (g!=0) { - g=1.0/g; - for (j=l;j<=n;j++) { - for (s=0.0,k=l;k<=m;k++) s += a[k][i]*a[k][j]; - f=(s/a[i][i])*g; - for (k=i;k<=m;k++) a[k][j] += f*a[k][i]; - } - for (j=i;j<=m;j++) a[j][i] *= g; - } else for (j=i;j<=m;j++) a[j][i]=0.0; - ++a[i][i]; - } + // Accumulation of left-hand transformations. + //for (i=IMIN(m,n);i>=1;i--) { + for (i=std::min(m,n); i>=1; i--) { + l=i+1; + g=w[i-1]; + for (j=l; j<=n; j++) a[i][j]=0.0; + if (g!=0) { + g=1.0/g; + for (j=l; j<=n; j++) { + for (s=0.0,k=l; k<=m; k++) s += a[k][i]*a[k][j]; + f=(s/a[i][i])*g; + for (k=i; k<=m; k++) a[k][j] += f*a[k][i]; + } + for (j=i; j<=m; j++) a[j][i] *= g; + } else for (j=i; j<=m; j++) a[j][i]=0.0; + ++a[i][i]; + } - // Diagonalization of the bidiagonal form: Loop over - //singular values, and over allowed iterations. - for (k=n;k>=1;k--) { + // Diagonalization of the bidiagonal form: Loop over + //singular values, and over allowed iterations. + for (k=n; k>=1; k--) { - for (its=1;its<=30;its++) { - flag=1; - // Test for splitting. - for (l=k;l>=1;l--) { - // Note that rv1[1] is always zero. - nm=l-1; - if ((T)(fabs(rv1[l])+anorm) == anorm) { - flag=0; - break; - } - if ((T)(fabs(w[nm-1])+anorm) == anorm) break; - } - if (flag) { - // Cancellation of rv1[l], if l > 1. - c=0.0; - s=1.0; - for (i=l;i<=k;i++) { + for (its=1; its<=30; its++) { + flag=1; + // Test for splitting. + for (l=k; l>=1; l--) { + // Note that rv1[1] is always zero. + nm=l-1; + if ((T)(fabs(rv1[l])+anorm) == anorm) { + flag=0; + break; + } + if ((T)(fabs(w[nm-1])+anorm) == anorm) break; + } + if (flag) { + // Cancellation of rv1[l], if l > 1. + c=0.0; + s=1.0; + for (i=l; i<=k; i++) { - f=s*rv1[i]; - rv1[i]=c*rv1[i]; - if ((T)(fabs(f)+anorm) == anorm) break; - g=w[i-1]; - h=pythag(f,g); - w[i-1]=h; - h=1.0/h; - c=g*h; - s = -f*h; - for (j=1;j<=m;j++) { - y=a[j][nm]; - z=a[j][i]; - a[j][nm]=y*c+z*s; - a[j][i]=z*c-y*s; - } - } - } - z=w[k-1]; - //Convergence. - if (l == k) { - // Singular value is made nonnegative. - if (z < 0.0) { - w[k-1] = -z; - for (j=1;j<=n;j++) v[j][k] = -v[j][k]; - } - break; - } - if (its == 30) - std::cerr << "no convergence in 30 svdcmp iterations" << std::endl; - // Shift from bottom 2-by-2 minor. - x=w[l-1]; - nm=k-1; - y=w[nm-1]; - g=rv1[nm]; - h=rv1[k]; - f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y); - g=pythag(f,T(1.0)); - f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x; - // Next QR transformation: - c=s=1.0; - for (j=l;j<=nm;j++) { - i=j+1; - g=rv1[i]; - y=w[i-1]; - h=s*g; - g=c*g; - z=pythag(f,h); - rv1[j]=z; - c=f/z; - s=h/z; - f=x*c+g*s; - g = g*c-x*s; - h=y*s; - y *= c; - for (jj=1;jj<=n;jj++) { - x=v[jj][j]; - z=v[jj][i]; - v[jj][j]=x*c+z*s; - v[jj][i]=z*c-x*s; - } - z=pythag(f,h); - // Rotation can be arbitrary if z = 0. - w[j-1]=z; - if (z!=0) { - z=1.0/z; - c=f*z; - s=h*z; - } - f=c*g+s*y; - x=c*y-s*g; + f=s*rv1[i]; + rv1[i]=c*rv1[i]; + if ((T)(fabs(f)+anorm) == anorm) break; + g=w[i-1]; + h=pythag(f,g); + w[i-1]=h; + h=1.0/h; + c=g*h; + s = -f*h; + for (j=1; j<=m; j++) { + y=a[j][nm]; + z=a[j][i]; + a[j][nm]=y*c+z*s; + a[j][i]=z*c-y*s; + } + } + } + z=w[k-1]; + //Convergence. + if (l == k) { + // Singular value is made nonnegative. + if (z < 0.0) { + w[k-1] = -z; + for (j=1; j<=n; j++) v[j][k] = -v[j][k]; + } + break; + } + if (its == 30) + std::cerr << "no convergence in 30 svdcmp iterations" << std::endl; + // Shift from bottom 2-by-2 minor. + x=w[l-1]; + nm=k-1; + y=w[nm-1]; + g=rv1[nm]; + h=rv1[k]; + f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y); + g=pythag(f,T(1.0)); + f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x; + // Next QR transformation: + c=s=1.0; + for (j=l; j<=nm; j++) { + i=j+1; + g=rv1[i]; + y=w[i-1]; + h=s*g; + g=c*g; + z=pythag(f,h); + rv1[j]=z; + c=f/z; + s=h/z; + f=x*c+g*s; + g = g*c-x*s; + h=y*s; + y *= c; + for (jj=1; jj<=n; jj++) { + x=v[jj][j]; + z=v[jj][i]; + v[jj][j]=x*c+z*s; + v[jj][i]=z*c-x*s; + } + z=pythag(f,h); + // Rotation can be arbitrary if z = 0. + w[j-1]=z; + if (z!=0) { + z=1.0/z; + c=f*z; + s=h*z; + } + f=c*g+s*y; + x=c*y-s*g; - for (jj=1;jj<=m;jj++) { - y=a[jj][j]; - z=a[jj][i]; - a[jj][j]=y*c+z*s; - a[jj][i]=z*c-y*s; - } - } - rv1[l]=0.0; - rv1[k]=f; - w[k-1]=x; + for (jj=1; jj<=m; jj++) { + y=a[jj][j]; + z=a[jj][i]; + a[jj][j]=y*c+z*s; + a[jj][i]=z*c-y*s; } + } + rv1[l]=0.0; + rv1[k]=f; + w[k-1]=x; } + } - for (int i=0; i<m; i++) - for (int j=0; j<n; j++) - a_[i][j] = a[i+1][j+1]; + for (int i=0; i<m; i++) + for (int j=0; j<n; j++) + a_[i][j] = a[i+1][j+1]; - for (int i=0; i<n; i++) - for (int j=0; j<n; j++) - v_[i][j] = v[i+1][j+1]; + for (int i=0; i<n; i++) + for (int j=0; j<n; j++) + v_[i][j] = v[i+1][j+1]; } #endif diff --git a/dune/gfe/symmetricmatrix.hh b/dune/gfe/symmetricmatrix.hh index a7b48021..7bc860ec 100644 --- a/dune/gfe/symmetricmatrix.hh +++ b/dune/gfe/symmetricmatrix.hh @@ -6,21 +6,21 @@ namespace Dune { -/** \brief A class implementing a symmetric matrix with compile-time size - * - * A \f$ dim\times dim \f$ matrix is stored internally as a <tt>Dune::FieldVector<double, dim*(dim+1)/2></tt> - * The components are assumed to be \f$ [ E(1,1),\ E(2,1),\ E(2,2),\ E(3,1),\ E(3,2),\ E(3,3) ]\f$ - * and analogous for other dimensions - * \tparam dim number of lines/columns of the matrix - */ -template <class T, int N> -class SymmetricMatrix -{ -public: - - /** \brief The type used for scalars + /** \brief A class implementing a symmetric matrix with compile-time size + * + * A \f$ dim\times dim \f$ matrix is stored internally as a <tt>Dune::FieldVector<double, dim*(dim+1)/2></tt> + * The components are assumed to be \f$ [ E(1,1),\ E(2,1),\ E(2,2),\ E(3,1),\ E(3,2),\ E(3,3) ]\f$ + * and analogous for other dimensions + * \tparam dim number of lines/columns of the matrix */ - typedef T field_type; + template <class T, int N> + class SymmetricMatrix + { + public: + + /** \brief The type used for scalars + */ + typedef T field_type; /** \brief Default constructor, creates uninitialized matrix */ @@ -68,7 +68,7 @@ public: T result = 0; for (size_t i=0; i<N; i++) for (size_t j=0; j<=i; j++) - result += (1-0.5*(i==j)) * operator()(i,j) * (v1[i] * v2[j] + v1[j] * v2[i]); + result += (1-0.5*(i==j)) * operator()(i,j) * (v1[i] * v2[j] + v1[j] * v2[i]); return result; } @@ -78,7 +78,7 @@ public: data_.axpy(a,other.data_); } - /** \brief Return the FieldMatrix representation of the symmetric tensor.*/ + /** \brief Return the FieldMatrix representation of the symmetric tensor.*/ Dune::FieldMatrix<T,N,N> matrix() const { Dune::FieldMatrix<T,N,N> mat; @@ -89,9 +89,9 @@ public: return mat; } -private: + private: Dune::FieldVector<T,N*(N+1)/2> data_; -}; + }; } #endif diff --git a/dune/gfe/targetspacertrsolver.cc b/dune/gfe/targetspacertrsolver.cc index 02e317c7..9d2bc556 100644 --- a/dune/gfe/targetspacertrsolver.cc +++ b/dune/gfe/targetspacertrsolver.cc @@ -15,91 +15,91 @@ setup(const AverageDistanceAssembler<TargetSpace>* assembler, double tolerance, int maxNewtonSteps) { - assembler_ = assembler; - x_ = x; - tolerance_ = tolerance; - maxNewtonSteps_ = maxNewtonSteps; - this->verbosity_ = NumProc::QUIET; - minNumberOfIterations_ = 1; + assembler_ = assembler; + x_ = x; + tolerance_ = tolerance; + maxNewtonSteps_ = maxNewtonSteps; + this->verbosity_ = NumProc::QUIET; + minNumberOfIterations_ = 1; } template <class TargetSpace> void TargetSpaceRiemannianTRSolver<TargetSpace>::solve() { - assert(minNumberOfIterations_ > 0); - - // ///////////////////////////////////////////////////// - // Newton Solver - // ///////////////////////////////////////////////////// - for (size_t i=0; i<maxNewtonSteps_; i++) { - - if (this->verbosity_ == Solver::FULL) { - std::cout << "----------------------------------------------------" << std::endl; - std::cout << " Newton Step Number: " << i - << ", energy: " << assembler_->value(x_) << std::endl; - std::cout << "----------------------------------------------------" << std::endl; - } - - CorrectionType corr; - - MatrixType hesseMatrix; - - typename TargetSpace::EmbeddedTangentVector rhs; - assembler_->assembleEmbeddedGradient(x_, rhs); - assembler_->assembleEmbeddedHessian(x_, hesseMatrix); - - // The right hand side is the _negative_ gradient - rhs *= -1; - - // ///////////////////////////// - // Solve ! - // ///////////////////////////// - // TODO: The GramSchmidtSolver may not be the smartest choice here, because it needs - // a matrix that is positive definite on the complement of its kernel. This can limit - // the radius of convergence of the Newton solver. As an example consider interpolation - // between two points on the unit sphere that are more than pi/2 apart. There is a - // well-defined unique shortest geodesic between two such points, but depending on the - // weights, the weighted sum of squared distances does not everywhere have a positive definite - // second derivative. - // Maybe the Newton solver has to be replaced by a trust-region or line-search solver - // for such situations. - Dune::FieldMatrix<field_type,blocksize,embeddedBlocksize> basis = x_.orthonormalFrame(); - GramSchmidtSolver<field_type, blocksize, embeddedBlocksize>::solve(hesseMatrix, corr, rhs, basis); - - if (this->verbosity_ == NumProc::FULL) - std::cout << "Infinity norm of the correction: " << corr.infinity_norm() << std::endl; - - if (corr.infinity_norm() < this->tolerance_ and i>=minNumberOfIterations_-1) { - if (this->verbosity_ == NumProc::FULL) - std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; - - if (this->verbosity_ != NumProc::QUIET) - std::cout << i+1 << " Newton steps were taken." << std::endl; - break; - } - - // //////////////////////////////////////////////////// - // Check whether trust-region step can be accepted - // //////////////////////////////////////////////////// + assert(minNumberOfIterations_ > 0); + + // ///////////////////////////////////////////////////// + // Newton Solver + // ///////////////////////////////////////////////////// + for (size_t i=0; i<maxNewtonSteps_; i++) { + + if (this->verbosity_ == Solver::FULL) { + std::cout << "----------------------------------------------------" << std::endl; + std::cout << " Newton Step Number: " << i + << ", energy: " << assembler_->value(x_) << std::endl; + std::cout << "----------------------------------------------------" << std::endl; + } + + CorrectionType corr; + + MatrixType hesseMatrix; + + typename TargetSpace::EmbeddedTangentVector rhs; + assembler_->assembleEmbeddedGradient(x_, rhs); + assembler_->assembleEmbeddedHessian(x_, hesseMatrix); + + // The right hand side is the _negative_ gradient + rhs *= -1; + + // ///////////////////////////// + // Solve ! + // ///////////////////////////// + // TODO: The GramSchmidtSolver may not be the smartest choice here, because it needs + // a matrix that is positive definite on the complement of its kernel. This can limit + // the radius of convergence of the Newton solver. As an example consider interpolation + // between two points on the unit sphere that are more than pi/2 apart. There is a + // well-defined unique shortest geodesic between two such points, but depending on the + // weights, the weighted sum of squared distances does not everywhere have a positive definite + // second derivative. + // Maybe the Newton solver has to be replaced by a trust-region or line-search solver + // for such situations. + Dune::FieldMatrix<field_type,blocksize,embeddedBlocksize> basis = x_.orthonormalFrame(); + GramSchmidtSolver<field_type, blocksize, embeddedBlocksize>::solve(hesseMatrix, corr, rhs, basis); + + if (this->verbosity_ == NumProc::FULL) + std::cout << "Infinity norm of the correction: " << corr.infinity_norm() << std::endl; + + if (corr.infinity_norm() < this->tolerance_ and i>=minNumberOfIterations_-1) { + if (this->verbosity_ == NumProc::FULL) + std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; + + if (this->verbosity_ != NumProc::QUIET) + std::cout << i+1 << " Newton steps were taken." << std::endl; + break; + } + + // //////////////////////////////////////////////////// + // Check whether trust-region step can be accepted + // //////////////////////////////////////////////////// #if 0 - TargetSpace newIterate = x_; - newIterate = TargetSpace::exp(newIterate, corr); + TargetSpace newIterate = x_; + newIterate = TargetSpace::exp(newIterate, corr); - if (this->verbosity_ == NumProc::FULL) - { - field_type oldEnergy = assembler_->value(x_); - field_type energy = assembler_->value(newIterate); + if (this->verbosity_ == NumProc::FULL) + { + field_type oldEnergy = assembler_->value(x_); + field_type energy = assembler_->value(newIterate); - if (energy >= oldEnergy) - std::cout << "Direction is not a descent direction!" << std::endl; - } + if (energy >= oldEnergy) + std::cout << "Direction is not a descent direction!" << std::endl; + } - // Actually take the step - x_ = newIterate; + // Actually take the step + x_ = newIterate; #else - x_ = TargetSpace::exp(x_, corr); + x_ = TargetSpace::exp(x_, corr); #endif - } + } } diff --git a/dune/gfe/targetspacertrsolver.hh b/dune/gfe/targetspacertrsolver.hh index 568d8314..e07714ab 100644 --- a/dune/gfe/targetspacertrsolver.hh +++ b/dune/gfe/targetspacertrsolver.hh @@ -6,59 +6,59 @@ #include <dune/gfe/symmetricmatrix.hh> /** \brief Riemannian trust-region solver for geodesic finite-element problems - \tparam TargetSpace The manifold that our functions take values in + \tparam TargetSpace The manifold that our functions take values in */ template <class TargetSpace> class TargetSpaceRiemannianTRSolver - : public NumProc + : public NumProc { - const static int blocksize = TargetSpace::TangentVector::dimension; - const static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; + const static int blocksize = TargetSpace::TangentVector::dimension; + const static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; - // Centralize the field type here - typedef typename TargetSpace::ctype field_type; + // Centralize the field type here + typedef typename TargetSpace::ctype field_type; - typedef Dune::SymmetricMatrix<field_type, embeddedBlocksize> MatrixType; - typedef Dune::FieldVector<field_type, embeddedBlocksize> CorrectionType; + typedef Dune::SymmetricMatrix<field_type, embeddedBlocksize> MatrixType; + typedef Dune::FieldVector<field_type, embeddedBlocksize> CorrectionType; public: - /** \brief Set up the solver using a monotone multigrid method as the inner solver */ - void setup(const AverageDistanceAssembler<TargetSpace>* assembler, - const TargetSpace& x, - double tolerance, - int maxNewtonSteps); + /** \brief Set up the solver using a monotone multigrid method as the inner solver */ + void setup(const AverageDistanceAssembler<TargetSpace>* assembler, + const TargetSpace& x, + double tolerance, + int maxNewtonSteps); - void solve(); + void solve(); - void setInitialSolution(const TargetSpace& x) { - x_ = x; - } + void setInitialSolution(const TargetSpace& x) { + x_ = x; + } - TargetSpace getSol() const {return x_;} + TargetSpace getSol() const {return x_;} protected: - /** \brief The solution vector */ - TargetSpace x_; + /** \brief The solution vector */ + TargetSpace x_; - /** \brief Tolerance of the solver */ - double tolerance_; + /** \brief Tolerance of the solver */ + double tolerance_; - /** \brief Maximum number of Newton steps */ - size_t maxNewtonSteps_; + /** \brief Maximum number of Newton steps */ + size_t maxNewtonSteps_; - /** \brief The assembler for the average-distance functional */ - const AverageDistanceAssembler<TargetSpace>* assembler_; + /** \brief The assembler for the average-distance functional */ + const AverageDistanceAssembler<TargetSpace>* assembler_; - /** \brief Specify a minimal number of iterations the Newton solver has to do - * - * This is needed when working with automatic differentiation. While a very low - * number of iterations may be enough to precisely compute the value of a - * geodesic finite element function, a higher number may be needed to make an AD - * system compute a derivative with sufficient precision. - */ - size_t minNumberOfIterations_; + /** \brief Specify a minimal number of iterations the Newton solver has to do + * + * This is needed when working with automatic differentiation. While a very low + * number of iterations may be enough to precisely compute the value of a + * geodesic finite element function, a higher number may be needed to make an AD + * system compute a derivative with sufficient precision. + */ + size_t minNumberOfIterations_; }; diff --git a/dune/gfe/tensor3.hh b/dune/gfe/tensor3.hh index 89cd648e..5324107d 100644 --- a/dune/gfe/tensor3.hh +++ b/dune/gfe/tensor3.hh @@ -3,182 +3,182 @@ /** \file \brief A third-rank tensor - */ + */ #include <array> #include <dune/common/fmatrix.hh> /** \brief A third-rank tensor -*/ + */ template <class T, int N1, int N2, int N3> class Tensor3 - : public std::array<Dune::FieldMatrix<T,N2,N3>,N1> + : public std::array<Dune::FieldMatrix<T,N2,N3>,N1> { - public: +public: - /** \brief Default constructor */ - Tensor3() {} + /** \brief Default constructor */ + Tensor3() {} - /** \brief Constructor from a scalar */ - Tensor3(const T& c) - { - for (int i=0; i<N1; i++) - (*this)[i] = c; - } + /** \brief Constructor from a scalar */ + Tensor3(const T& c) + { + for (int i=0; i<N1; i++) + (*this)[i] = c; + } + + T infinity_norm() const + { + static_assert(N1>0, "infinity_norm not implemented for empty tensors"); + T norm = (*this)[0].infinity_norm(); + for (int i=1; i<N1; i++) + norm = std::max(norm, (*this)[i].infinity_norm()); + return norm; + } + + T frobenius_norm() const + { + T norm = 0; + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (int k=0; k<N3; k++) + norm += (*this)[i][j][k] * (*this)[i][j][k]; - T infinity_norm() const - { - static_assert(N1>0, "infinity_norm not implemented for empty tensors"); - T norm = (*this)[0].infinity_norm(); - for (int i=1; i<N1; i++) - norm = std::max(norm, (*this)[i].infinity_norm()); - return norm; - } + return std::sqrt(norm); + } - T frobenius_norm() const - { - T norm = 0; - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (int k=0; k<N3; k++) - norm += (*this)[i][j][k] * (*this)[i][j][k]; + /** \brief The squared Frobenius norm, i.e., the sum of squared entries */ + T frobenius_norm2() const + { + T norm = 0; + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (int k=0; k<N3; k++) + norm += (*this)[i][j][k] * (*this)[i][j][k]; - return std::sqrt(norm); - } + return norm; + } - /** \brief The squared Frobenius norm, i.e., the sum of squared entries */ - T frobenius_norm2() const - { - T norm = 0; - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (int k=0; k<N3; k++) - norm += (*this)[i][j][k] * (*this)[i][j][k]; + Tensor3<T,N1,N2,N3>& axpy(const T& alpha, const Tensor3<T,N1,N2,N3>& other) + { + for (int i=0; i<N1; i++) + (*this)[i].axpy(alpha,other[i]); + return *this; + } - return norm; - } + static Tensor3<T,N1,N2,N3> product(const Dune::FieldVector<T,N1>& a, const Dune::FieldVector<T,N2>& b, const Dune::FieldVector<T,N3>& c) + { + Tensor3<T,N1,N2,N3> result; - Tensor3<T,N1,N2,N3>& axpy(const T& alpha, const Tensor3<T,N1,N2,N3>& other) - { - for (int i=0; i<N1; i++) - (*this)[i].axpy(alpha,other[i]); - return *this; - } + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (int k=0; k<N3; k++) + result[i][j][k] = a[i]*b[j]*c[k]; - static Tensor3<T,N1,N2,N3> product(const Dune::FieldVector<T,N1>& a, const Dune::FieldVector<T,N2>& b, const Dune::FieldVector<T,N3>& c) - { - Tensor3<T,N1,N2,N3> result; + return result; + } - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (int k=0; k<N3; k++) - result[i][j][k] = a[i]*b[j]*c[k]; + static Tensor3<T,N1,N2,N3> product(const Dune::FieldMatrix<T,N1,N2>& ab, const Dune::FieldVector<T,N3>& c) + { + Tensor3<T,N1,N2,N3> result; - return result; - } + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (int k=0; k<N3; k++) + result[i][j][k] = ab[i][j]*c[k]; - static Tensor3<T,N1,N2,N3> product(const Dune::FieldMatrix<T,N1,N2>& ab, const Dune::FieldVector<T,N3>& c) - { - Tensor3<T,N1,N2,N3> result; + return result; + } - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (int k=0; k<N3; k++) - result[i][j][k] = ab[i][j]*c[k]; + static Tensor3<T,N1,N2,N3> product(const Dune::FieldVector<T,N1>& a, const Dune::FieldMatrix<T,N2,N3>& bc) + { + Tensor3<T,N1,N2,N3> result; - return result; - } + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (int k=0; k<N3; k++) + result[i][j][k] = a[i]*bc[j][k]; - static Tensor3<T,N1,N2,N3> product(const Dune::FieldVector<T,N1>& a, const Dune::FieldMatrix<T,N2,N3>& bc) - { - Tensor3<T,N1,N2,N3> result; + return result; + } - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (int k=0; k<N3; k++) - result[i][j][k] = a[i]*bc[j][k]; + template <int N4> + friend Tensor3<T,N1,N2,N4> operator*(const Tensor3<T,N1,N2,N3>& a, const Dune::FieldMatrix<T,N3,N4>& b) + { + Tensor3<T,N1,N2,N4> result; - return result; + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (int k=0; k<N4; k++) { + result[i][j][k] = 0; + for (int l=0; l<N3; l++) + result[i][j][k] += a[i][j][l]*b[l][k]; } - template <int N4> - friend Tensor3<T,N1,N2,N4> operator*(const Tensor3<T,N1,N2,N3>& a, const Dune::FieldMatrix<T,N3,N4>& b) - { - Tensor3<T,N1,N2,N4> result; + return result; + } - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (int k=0; k<N4; k++) { - result[i][j][k] = 0; - for (int l=0; l<N3; l++) - result[i][j][k] += a[i][j][l]*b[l][k]; - } + template <int N4> + friend Tensor3<T,N1,N3,N4> operator*(const Dune::FieldMatrix<T,N1,N2>& a, const Tensor3<T,N2,N3,N4>& b) + { + Tensor3<T,N1,N3,N4> result; - return result; + for (int i=0; i<N1; i++) + for (int j=0; j<N3; j++) + for (int k=0; k<N4; k++) { + result[i][j][k] = 0; + for (int l=0; l<N2; l++) + result[i][j][k] += a[i][l]*b[l][j][k]; } - template <int N4> - friend Tensor3<T,N1,N3,N4> operator*(const Dune::FieldMatrix<T,N1,N2>& a, const Tensor3<T,N2,N3,N4>& b) - { - Tensor3<T,N1,N3,N4> result; - - for (int i=0; i<N1; i++) - for (int j=0; j<N3; j++) - for (int k=0; k<N4; k++) { - result[i][j][k] = 0; - for (int l=0; l<N2; l++) - result[i][j][k] += a[i][l]*b[l][j][k]; - } - - return result; - } + return result; + } - friend Tensor3<T,N1,N2,N3> operator+(const Tensor3<T,N1,N2,N3>& a, const Tensor3<T,N1,N2,N3>& b) - { - Tensor3<T,N1,N2,N3> result; + friend Tensor3<T,N1,N2,N3> operator+(const Tensor3<T,N1,N2,N3>& a, const Tensor3<T,N1,N2,N3>& b) + { + Tensor3<T,N1,N2,N3> result; - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (int k=0; k<N3; k++) - result[i][j][k] = a[i][j][k] + b[i][j][k]; + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (int k=0; k<N3; k++) + result[i][j][k] = a[i][j][k] + b[i][j][k]; - return result; - } + return result; + } - friend Tensor3<T,N1,N2,N3> operator-(const Tensor3<T,N1,N2,N3>& a, const Tensor3<T,N1,N2,N3>& b) - { - Tensor3<T,N1,N2,N3> result; + friend Tensor3<T,N1,N2,N3> operator-(const Tensor3<T,N1,N2,N3>& a, const Tensor3<T,N1,N2,N3>& b) + { + Tensor3<T,N1,N2,N3> result; - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (int k=0; k<N3; k++) - result[i][j][k] = a[i][j][k] - b[i][j][k]; + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (int k=0; k<N3; k++) + result[i][j][k] = a[i][j][k] - b[i][j][k]; - return result; - } + return result; + } - friend Tensor3<T,N1,N2,N3> operator*(const T& scalar, const Tensor3<T,N1,N2,N3>& tensor) - { - Tensor3<T,N1,N2,N3> result; + friend Tensor3<T,N1,N2,N3> operator*(const T& scalar, const Tensor3<T,N1,N2,N3>& tensor) + { + Tensor3<T,N1,N2,N3> result; - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (int k=0; k<N3; k++) - result[i][j][k] = scalar * tensor[i][j][k]; + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (int k=0; k<N3; k++) + result[i][j][k] = scalar * tensor[i][j][k]; - return result; + return result; - } + } - Tensor3<T,N1,N2,N3>& operator*=(const T& scalar) - { - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (int k=0; k<N3; k++) - (*this)[i][j][k] *= scalar; + Tensor3<T,N1,N2,N3>& operator*=(const T& scalar) + { + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (int k=0; k<N3; k++) + (*this)[i][j][k] *= scalar; - return *this; - } + return *this; + } }; @@ -186,9 +186,9 @@ class Tensor3 template <class T, int N1, int N2, int N3> inline std::ostream& operator<< (std::ostream& s, const Tensor3<T,N1,N2,N3>& tensor) { - for (int i=0; i<N1; i++) - s << tensor[i]; - return s; + for (int i=0; i<N1; i++) + s << tensor[i]; + return s; } diff --git a/dune/gfe/tensorssd.hh b/dune/gfe/tensorssd.hh index 4d1a256a..0a9be67c 100644 --- a/dune/gfe/tensorssd.hh +++ b/dune/gfe/tensorssd.hh @@ -3,7 +3,7 @@ /** \file \brief A third-rank tensor with two static (SS) and one dynamic (D) dimension - */ + */ #include <array> @@ -11,120 +11,120 @@ #include <dune/istl/matrix.hh> /** \brief A third-rank tensor with two static (SS) and one dynamic (D) dimension - * + * * \tparam T Type of the entries * \tparam N1 Size of the first dimension * \tparam N2 Size of the second dimension -*/ + */ template <class T, int N1, int N2> class TensorSSD { public: - /** \brief Constructor with the third dimension */ - explicit TensorSSD(size_t N3) + /** \brief Constructor with the third dimension */ + explicit TensorSSD(size_t N3) : N3_(N3) - { - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - data_[i][j].resize(N3_); + { + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + data_[i][j].resize(N3_); + } + + size_t dim(int index) const + { + switch (index) { + case 0 : + return N1; + case 1 : + return N2; + case 2 : + return N3_; + default : + assert(false); } - - size_t dim(int index) const - { - switch (index) { - case 0: - return N1; - case 1: - return N2; - case 2: - return N3_; - default: - assert(false); + // Make compiler happy even if NDEBUG is set + return 0; + } + + /** \brief Direct access to individual entries */ + T& operator()(size_t i, size_t j, size_t k) + { + assert(i<N1 && j<N2 && k<N3_); + return data_[i][j][k]; + } + + /** \brief Direct const access to individual entries */ + const T& operator()(size_t i, size_t j, size_t k) const + { + assert(i<N1 && j<N2 && k<N3_); + return data_[i][j][k]; + } + + /** \brief Assignment from scalar */ + TensorSSD<T,N1,N2>& operator=(const T& scalar) + { + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (size_t k=0; k<dim(2); k++) + data_[i][j][k] = scalar; + + return *this; + } + + friend TensorSSD<T,N1,N2> operator*(const TensorSSD<T,N1,N2>& a, const Dune::Matrix<T>& b) + { + TensorSSD<T,N1,N2> result(b.M()); + + assert(a.dim(2)==b.N()); + size_t N4 = a.dim(2); // third dimension of a + + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (size_t k=0; k<b.M(); k++) { + result.data_[i][j][k] = 0; + for (size_t l=0; l<N4; l++) + result.data_[i][j][k] += a.data_[i][j][l]*b[l][k]; } - // Make compiler happy even if NDEBUG is set - return 0; - } - /** \brief Direct access to individual entries */ - T& operator()(size_t i, size_t j, size_t k) - { - assert(i<N1 && j<N2 && k<N3_); - return data_[i][j][k]; - } - - /** \brief Direct const access to individual entries */ - const T& operator()(size_t i, size_t j, size_t k) const - { - assert(i<N1 && j<N2 && k<N3_); - return data_[i][j][k]; - } - - /** \brief Assignment from scalar */ - TensorSSD<T,N1,N2>& operator=(const T& scalar) - { - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (size_t k=0; k<dim(2); k++) - data_[i][j][k] = scalar; - - return *this; - } + return result; + } - friend TensorSSD<T,N1,N2> operator*(const TensorSSD<T,N1,N2>& a, const Dune::Matrix<T>& b) - { - TensorSSD<T,N1,N2> result(b.M()); - - assert(a.dim(2)==b.N()); - size_t N4 = a.dim(2); // third dimension of a - - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (size_t k=0; k<b.M(); k++) { - result.data_[i][j][k] = 0; - for (size_t l=0; l<N4; l++) - result.data_[i][j][k] += a.data_[i][j][l]*b[l][k]; - } - - return result; - } + friend TensorSSD<T,N1,N2> operator+(const TensorSSD<T,N1,N2>& a, const TensorSSD<T,N1,N2>& b) + { + assert(a.dim(2)==b.dim(2)); + size_t N3 = a.dim(2); + TensorSSD<T,N1,N2> result(N3); + + for (int i=0; i<N1; i++) + for (int j=0; j<N2; j++) + for (size_t k=0; k<N3; k++) + result.data_[i][j][k] = a.data_[i][j][k] + b.data_[i][j][k]; + + return result; + } - friend TensorSSD<T,N1,N2> operator+(const TensorSSD<T,N1,N2>& a, const TensorSSD<T,N1,N2>& b) - { - assert(a.dim(2)==b.dim(2)); - size_t N3 = a.dim(2); - TensorSSD<T,N1,N2> result(N3); - - for (int i=0; i<N1; i++) - for (int j=0; j<N2; j++) - for (size_t k=0; k<N3; k++) - result.data_[i][j][k] = a.data_[i][j][k] + b.data_[i][j][k]; - - return result; - } - private: - // having the dynamic data type on the inside is kind of a stupid data layout - std::array<std::array<std::vector<T>, N2>, N1> data_; - - // size of the third dimension - size_t N3_; + // having the dynamic data type on the inside is kind of a stupid data layout + std::array<std::array<std::vector<T>, N2>, N1> data_; + + // size of the third dimension + size_t N3_; }; //! Output operator for TensorSSD template <class T, int N1, int N2> inline std::ostream& operator<< (std::ostream& s, const TensorSSD<T,N1,N2>& tensor) { - for (int i=0; i<N1; i++) { - for (int j=0; j<N2; j++) { - for (size_t k=0; k<tensor.dim(2); k++) - s << tensor(i,j,k) << " "; - s << std::endl; - } - s << std::endl; + for (int i=0; i<N1; i++) { + for (int j=0; j<N2; j++) { + for (size_t k=0; k<tensor.dim(2); k++) + s << tensor(i,j,k) << " "; + s << std::endl; } - return s; + s << std::endl; + } + return s; } diff --git a/dune/gfe/trustregionmmgbasesolver.hh b/dune/gfe/trustregionmmgbasesolver.hh index 416087a8..9001e709 100644 --- a/dune/gfe/trustregionmmgbasesolver.hh +++ b/dune/gfe/trustregionmmgbasesolver.hh @@ -10,71 +10,71 @@ #include <dune/solvers/solvers/quadraticipopt.hh> /** \brief Base solver for a monotone multigrid solver when used as the inner solver in a trust region method - * - * Monotone multigrid methods solve constrained problems even on the coarsest level. Therefore, the choice - * of possible coarse grid solvers is limited. I have tried both Gauss-Seidel and IPOpt, and both of them - * have not satisfied my. Gauss-Seidel is slow when the coarse grid is not coarse enough. The results computed - * by IPOpt appear to be imprecise, and I didn't get good trust-region convergence. I don't really understand - * this -- it may be a bug in our IPOpt wrapper code. - * Ideally, one would use a direct solver, in particular close to the solution. However, a direct solver may - * not produce admissible corrections. Also, it may produce energy increase. Therefore, the TrustRegionMMGBaseSolver - * does a pragmatic approach: a solution is first computed using UMFPack, disregarding the obstacles. - * If the result is admissible and decreases the energy we keep it. Otherwise we fall back to IPOpt. - */ + * + * Monotone multigrid methods solve constrained problems even on the coarsest level. Therefore, the choice + * of possible coarse grid solvers is limited. I have tried both Gauss-Seidel and IPOpt, and both of them + * have not satisfied my. Gauss-Seidel is slow when the coarse grid is not coarse enough. The results computed + * by IPOpt appear to be imprecise, and I didn't get good trust-region convergence. I don't really understand + * this -- it may be a bug in our IPOpt wrapper code. + * Ideally, one would use a direct solver, in particular close to the solution. However, a direct solver may + * not produce admissible corrections. Also, it may produce energy increase. Therefore, the TrustRegionMMGBaseSolver + * does a pragmatic approach: a solution is first computed using UMFPack, disregarding the obstacles. + * If the result is admissible and decreases the energy we keep it. Otherwise we fall back to IPOpt. + */ template <class MatrixType, class VectorType> class TrustRegionMMGBaseSolver -: public IterativeSolver<VectorType>, - public CanIgnore<Dune::BitSetVector<VectorType::block_type::dimension> > + : public IterativeSolver<VectorType>, + public CanIgnore<Dune::BitSetVector<VectorType::block_type::dimension> > { - typedef typename VectorType::field_type field_type; + typedef typename VectorType::field_type field_type; - // For complex-valued data - typedef typename Dune::FieldTraits<field_type>::real_type real_type; + // For complex-valued data + typedef typename Dune::FieldTraits<field_type>::real_type real_type; - constexpr static int blocksize = VectorType::value_type::dimension; - typedef Dune::BitSetVector<blocksize> BitVectorType; + constexpr static int blocksize = VectorType::value_type::dimension; + typedef Dune::BitSetVector<blocksize> BitVectorType; public: - /** \brief Constructor taking all relevant data */ - TrustRegionMMGBaseSolver(Solver::VerbosityMode verbosity) - : IterativeSolver<VectorType, BitVectorType>(100, // maxIterations - 1e-8, // tolerance - nullptr, - verbosity, - false) // false = use absolute error - {} - - void setProblem(const MatrixType& matrix, - VectorType& x, - const VectorType& rhs) { - matrix_ = &matrix; - x_ = &x; - rhs_ = &rhs; - } + /** \brief Constructor taking all relevant data */ + TrustRegionMMGBaseSolver(Solver::VerbosityMode verbosity) + : IterativeSolver<VectorType, BitVectorType>(100, // maxIterations + 1e-8, // tolerance + nullptr, + verbosity, + false) // false = use absolute error + {} + + void setProblem(const MatrixType& matrix, + VectorType& x, + const VectorType& rhs) { + matrix_ = &matrix; + x_ = &x; + rhs_ = &rhs; + } - /** \brief Checks whether all relevant member variables are set - * \exception SolverError if the iteration step is not set up properly - */ - virtual void check() const; + /** \brief Checks whether all relevant member variables are set + * \exception SolverError if the iteration step is not set up properly + */ + virtual void check() const; - virtual void preprocess(); + virtual void preprocess(); - /** \brief Loop, call the iteration procedure - * and monitor convergence - */ - virtual void solve(); + /** \brief Loop, call the iteration procedure + * and monitor convergence + */ + virtual void solve(); - //! The quadratic term in the quadratic energy, is assumed to be symmetric - const MatrixType* matrix_; + //! The quadratic term in the quadratic energy, is assumed to be symmetric + const MatrixType* matrix_; - //! Vector to store the solution - VectorType* x_; + //! Vector to store the solution + VectorType* x_; - //! The linear term in the quadratic energy - const VectorType* rhs_; + //! The linear term in the quadratic energy + const VectorType* rhs_; - std::vector<BoxConstraint<field_type,blocksize> >* obstacles_; + std::vector<BoxConstraint<field_type,blocksize> >* obstacles_; }; @@ -82,14 +82,14 @@ public: template <class MatrixType, class VectorType> void TrustRegionMMGBaseSolver<MatrixType, VectorType>::check() const { - // check base class - IterativeSolver<VectorType,BitVectorType>::check(); + // check base class + IterativeSolver<VectorType,BitVectorType>::check(); } template <class MatrixType, class VectorType> void TrustRegionMMGBaseSolver<MatrixType, VectorType>::preprocess() { - //this->iterationStep_->preprocess(); + //this->iterationStep_->preprocess(); } template <class MatrixType, class VectorType> diff --git a/dune/gfe/vtkfile.hh b/dune/gfe/vtkfile.hh index 168c5ca3..3bfcd1f3 100644 --- a/dune/gfe/vtkfile.hh +++ b/dune/gfe/vtkfile.hh @@ -72,7 +72,7 @@ namespace Dune { writer.endPoints(); for (int i=0; i<mpiHelper.size(); i++) - writer.addPiece(getParallelPieceName(filename, "", i, mpiHelper.size())); + writer.addPiece(getParallelPieceName(filename, "", i, mpiHelper.size())); // finish main section writer.endMain(); @@ -201,7 +201,7 @@ namespace Dune { pieceElement->QueryIntAttribute( "NumberOfCells", &numberOfCells ); } else // No vtu file? So let's try vtp - if ((pieceElement = doc.FirstChildElement( "VTKFile" )->FirstChildElement( "PolyData" )->FirstChildElement( "Piece" )) ) + if ((pieceElement = doc.FirstChildElement( "VTKFile" )->FirstChildElement( "PolyData" )->FirstChildElement( "Piece" )) ) { pieceElement->QueryIntAttribute( "NumberOfPolys", &numberOfCells ); } diff --git a/src/compute-disc-error.cc b/src/compute-disc-error.cc index ae1a3300..f5b5b277 100644 --- a/src/compute-disc-error.cc +++ b/src/compute-disc-error.cc @@ -39,50 +39,50 @@ using namespace Dune; /** \brief Check whether given local coordinates are contained in the reference element \todo This method exists in the Dune grid interface! But we need the eps. -*/ + */ static bool checkInside(const Dune::GeometryType& type, const Dune::FieldVector<double, dim> &loc, double eps) { switch (type.dim()) { - case 0: // vertex - return false; - - case 1: // line - return -eps <= loc[0] && loc[0] <= 1+eps; - - case 2: - - if (type.isSimplex()) { - return -eps <= loc[0] && -eps <= loc[1] && (loc[0]+loc[1])<=1+eps; - } else if (type.isCube()) { - return -eps <= loc[0] && loc[0] <= 1+eps - && -eps <= loc[1] && loc[1] <= 1+eps; - } else - DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); - - case 3: - if (type.isSimplex()) { - return -eps <= loc[0] && -eps <= loc[1] && -eps <= loc[2] - && (loc[0]+loc[1]+loc[2]) <= 1+eps; - } else if (type.isPyramid()) { - return -eps <= loc[0] && -eps <= loc[1] && -eps <= loc[2] - && (loc[0]+loc[2]) <= 1+eps - && (loc[1]+loc[2]) <= 1+eps; - } else if (type.isPrism()) { - return -eps <= loc[0] && -eps <= loc[1] - && (loc[0]+loc[1])<= 1+eps - && -eps <= loc[2] && loc[2] <= 1+eps; - } else if (type.isCube()) { - return -eps <= loc[0] && loc[0] <= 1+eps - && -eps <= loc[1] && loc[1] <= 1+eps - && -eps <= loc[2] && loc[2] <= 1+eps; - }else - DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); - - default: + case 0 : // vertex + return false; + + case 1 : // line + return -eps <= loc[0] && loc[0] <= 1+eps; + + case 2 : + + if (type.isSimplex()) { + return -eps <= loc[0] && -eps <= loc[1] && (loc[0]+loc[1])<=1+eps; + } else if (type.isCube()) { + return -eps <= loc[0] && loc[0] <= 1+eps + && -eps <= loc[1] && loc[1] <= 1+eps; + } else DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); + + case 3 : + if (type.isSimplex()) { + return -eps <= loc[0] && -eps <= loc[1] && -eps <= loc[2] + && (loc[0]+loc[1]+loc[2]) <= 1+eps; + } else if (type.isPyramid()) { + return -eps <= loc[0] && -eps <= loc[1] && -eps <= loc[2] + && (loc[0]+loc[2]) <= 1+eps + && (loc[1]+loc[2]) <= 1+eps; + } else if (type.isPrism()) { + return -eps <= loc[0] && -eps <= loc[1] + && (loc[0]+loc[1])<= 1+eps + && -eps <= loc[2] && loc[2] <= 1+eps; + } else if (type.isCube()) { + return -eps <= loc[0] && loc[0] <= 1+eps + && -eps <= loc[1] && loc[1] <= 1+eps + && -eps <= loc[2] && loc[2] <= 1+eps; + }else + DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); + + default : + DUNE_THROW(Dune::GridError, "checkInside(): ERROR: Unknown type " << type << " found!"); } } @@ -379,47 +379,47 @@ void measureDiscreteEOC(const GridView gridView, } else { - double l2ErrorSquared = 0; - double h1ErrorSquared = 0; + double l2ErrorSquared = 0; + double h1ErrorSquared = 0; - for (const auto& rElement : elements(referenceGridView)) - { - localReferenceSolution.bind(rElement); - auto localReferenceDerivative = derivative(localReferenceSolution); + for (const auto& rElement : elements(referenceGridView)) + { + localReferenceSolution.bind(rElement); + auto localReferenceDerivative = derivative(localReferenceSolution); - const auto& quadRule = QuadratureRules<double, dim>::rule(rElement.type(), 6); + const auto& quadRule = QuadratureRules<double, dim>::rule(rElement.type(), 6); - for (const auto& qp : quadRule) - { - auto integrationElement = rElement.geometry().integrationElement(qp.position()); + for (const auto& qp : quadRule) + { + auto integrationElement = rElement.geometry().integrationElement(qp.position()); - // Given a point with local coordinates qp.position() on element rElement of the reference grid - // find the element and local coordinates on the grid of the numerical simulation - auto supportingElement = findSupportingElement(referenceGridView.grid(), - gridView.grid(), - rElement, qp.position()); - auto element = std::get<0>(supportingElement); - localNumericalSolution.bind(element); - auto localNumericalDerivative = derivative(localNumericalSolution); - auto localPos = std::get<1>(supportingElement); + // Given a point with local coordinates qp.position() on element rElement of the reference grid + // find the element and local coordinates on the grid of the numerical simulation + auto supportingElement = findSupportingElement(referenceGridView.grid(), + gridView.grid(), + rElement, qp.position()); + auto element = std::get<0>(supportingElement); + localNumericalSolution.bind(element); + auto localNumericalDerivative = derivative(localNumericalSolution); + auto localPos = std::get<1>(supportingElement); - auto diff = localReferenceSolution(qp.position()) - localNumericalSolution(localPos); + auto diff = localReferenceSolution(qp.position()) - localNumericalSolution(localPos); - l2ErrorSquared += integrationElement * qp.weight() * diff.two_norm2(); + l2ErrorSquared += integrationElement * qp.weight() * diff.two_norm2(); - auto derDiff = localReferenceDerivative(qp.position()) - localNumericalDerivative(localPos); + auto derDiff = localReferenceDerivative(qp.position()) - localNumericalDerivative(localPos); - h1ErrorSquared += integrationElement * qp.weight() * derDiff.frobenius_norm2(); + h1ErrorSquared += integrationElement * qp.weight() * derDiff.frobenius_norm2(); + } } - } - std::cout << "levels: " << gridView.grid().maxLevel()+1 - << " " - << "L^2 error: " << std::sqrt(l2ErrorSquared) - << " " - << "h^1 error: " << std::sqrt(h1ErrorSquared) - << std::endl; + std::cout << "levels: " << gridView.grid().maxLevel()+1 + << " " + << "L^2 error: " << std::sqrt(l2ErrorSquared) + << " " + << "h^1 error: " << std::sqrt(h1ErrorSquared) + << std::endl; } } @@ -473,33 +473,33 @@ void measureAnalyticalEOC(const GridView gridView, // TODO: We need to use a type-erasure wrapper here // Only used if // parameterSet["interpolationMethod"] == "geodesic" auto numericalSolutionGeodesic = GFE::EmbeddedGlobalGFEFunction<FEBasis, - LocalGeodesicFEFunction<dim, double, typename FEBasis::LocalView::Tree::FiniteElement, TargetSpace>, - TargetSpace> (feBasis, x); + LocalGeodesicFEFunction<dim, double, typename FEBasis::LocalView::Tree::FiniteElement, TargetSpace>, + TargetSpace> (feBasis, x); auto localNumericalSolutionGeodesic = localFunction(numericalSolutionGeodesic); // ONly used if parameterSet["interpolationMethod"] == "projected" auto numericalSolutionProjected = GFE::EmbeddedGlobalGFEFunction<FEBasis, - GFE::LocalProjectedFEFunction<dim, double, typename FEBasis::LocalView::Tree::FiniteElement, TargetSpace>, - TargetSpace> (feBasis, x); + GFE::LocalProjectedFEFunction<dim, double, typename FEBasis::LocalView::Tree::FiniteElement, TargetSpace>, + TargetSpace> (feBasis, x); auto localNumericalSolutionProjected = localFunction(numericalSolutionProjected); #else typedef VirtualDifferentiableFunction<FieldVector<double, dimworld>, typename TargetSpace::CoordinateType> FBase; Python::Module module = Python::import(parameterSet.get<std::string>("referenceSolution")); - auto referenceSolution = module.get("fdf").toC<std::shared_ptr<FBase>>(); + auto referenceSolution = module.get("fdf").toC<std::shared_ptr<FBase> >(); // The numerical solution, as a grid function std::unique_ptr<VirtualGridViewFunction<GridView, typename TargetSpace::CoordinateType> > numericalSolution; if (parameterSet["interpolationMethod"] == "geodesic") numericalSolution = std::make_unique<GFE::EmbeddedGlobalGFEFunction<FEBasis, - LocalGeodesicFEFunction<dim, double, typename FEBasis::LocalView::Tree::FiniteElement, TargetSpace>, - TargetSpace> > (feBasis, x); + LocalGeodesicFEFunction<dim, double, typename FEBasis::LocalView::Tree::FiniteElement, TargetSpace>, + TargetSpace> > (feBasis, x); if (parameterSet["interpolationMethod"] == "projected") numericalSolution = std::make_unique<GFE::EmbeddedGlobalGFEFunction<FEBasis, - GFE::LocalProjectedFEFunction<dim, double, typename FEBasis::LocalView::Tree::FiniteElement, TargetSpace>, - TargetSpace> > (feBasis, x); + GFE::LocalProjectedFEFunction<dim, double, typename FEBasis::LocalView::Tree::FiniteElement, TargetSpace>, + TargetSpace> > (feBasis, x); #endif // QuadratureRule for the integral of the L^2 error @@ -539,9 +539,9 @@ void measureAnalyticalEOC(const GridView gridView, #if DUNE_VERSION_GT(DUNE_FUFEM, 2, 9) FieldVector<double,blocksize> numValue; if (parameterSet["interpolationMethod"] == "geodesic") - numValue = localNumericalSolutionGeodesic(quadPos); + numValue = localNumericalSolutionGeodesic(quadPos); if (parameterSet["interpolationMethod"] == "projected") - numValue = localNumericalSolutionProjected(quadPos); + numValue = localNumericalSolutionProjected(quadPos); auto refValue = referenceSolution(element.geometry().global(quadPos)); #else @@ -550,11 +550,11 @@ void measureAnalyticalEOC(const GridView gridView, // Evaluate function b. If it is a grid function use that to speed up the evaluation FieldVector<double,blocksize> refValue; - if (std::dynamic_pointer_cast<const VirtualGridViewFunction<GridView,FieldVector<double,blocksize> >>(referenceSolution)) - std::dynamic_pointer_cast<const VirtualGridViewFunction<GridView,FieldVector<double,blocksize> >>(referenceSolution)->evaluateLocal(element, - quadPos, - refValue - ); + if (std::dynamic_pointer_cast<const VirtualGridViewFunction<GridView,FieldVector<double,blocksize> > >(referenceSolution)) + std::dynamic_pointer_cast<const VirtualGridViewFunction<GridView,FieldVector<double,blocksize> > >(referenceSolution)->evaluateLocal(element, + quadPos, + refValue + ); else referenceSolution->evaluate(element.geometry().global(quadPos), refValue); #endif @@ -573,9 +573,9 @@ void measureAnalyticalEOC(const GridView gridView, FieldMatrix<double,blocksize,dimworld> num_di; if (parameterSet["interpolationMethod"] == "geodesic") - num_di = localNumericalDerivativeGeodesic(quadPos); + num_di = localNumericalDerivativeGeodesic(quadPos); if (parameterSet["interpolationMethod"] == "projected") - num_di = localNumericalDerivativeProjected(quadPos); + num_di = localNumericalDerivativeProjected(quadPos); auto ref_di = referenceDerivative(element.geometry().global(quadPos)); #else @@ -585,15 +585,15 @@ void measureAnalyticalEOC(const GridView gridView, if (dynamic_cast<const VirtualGridViewFunction<GridView,FieldVector<double,blocksize> >*>(numericalSolution.get())) dynamic_cast<const VirtualGridViewFunction<GridView,FieldVector<double,blocksize> >*>(numericalSolution.get())->evaluateDerivativeLocal(element, - quadPos, - num_di); + quadPos, + num_di); else numericalSolution->evaluateDerivative(element.geometry().global(quadPos), num_di); - if (std::dynamic_pointer_cast<const VirtualGridViewFunction<GridView,FieldVector<double,blocksize> >>(referenceSolution)) - std::dynamic_pointer_cast<const VirtualGridViewFunction<GridView,FieldVector<double,blocksize> >>(referenceSolution)->evaluateDerivativeLocal(element, - quadPos, - ref_di); + if (std::dynamic_pointer_cast<const VirtualGridViewFunction<GridView,FieldVector<double,blocksize> > >(referenceSolution)) + std::dynamic_pointer_cast<const VirtualGridViewFunction<GridView,FieldVector<double,blocksize> > >(referenceSolution)->evaluateDerivativeLocal(element, + quadPos, + ref_di); else referenceSolution->evaluateDerivative(element.geometry().global(quadPos), ref_di); #endif @@ -660,19 +660,19 @@ void measureAnalyticalEOC(const GridView gridView, << "L^2 error: " << std::sqrt(l2Error) #else auto l2Error = DiscretizationError<GridView>::computeL2Error(numericalSolution.get(), - referenceSolution.get(), - quadKey); + referenceSolution.get(), + quadKey); auto h1Error = DiscretizationError<GridView>::computeH1HalfNormDifferenceSquared(gridView, - numericalSolution.get(), - referenceSolution.get(), - quadKey); + numericalSolution.get(), + referenceSolution.get(), + quadKey); std::cout << "elements: " << gridView.size(0) << " " << "L^2 error: " << l2Error #endif - << " "; + << " "; std::cout << "h^1 error: " << std::sqrt(h1Error) << std::endl; } } @@ -690,20 +690,20 @@ void measureEOC(const std::shared_ptr<GridType> grid, switch (order) { - case 1: + case 1 : measureDiscreteEOC<typename GridType::LeafGridView,1,TargetSpace>(grid->leafGridView(), referenceGrid->leafGridView(), parameterSet); break; - case 2: + case 2 : measureDiscreteEOC<typename GridType::LeafGridView,2,TargetSpace>(grid->leafGridView(), referenceGrid->leafGridView(), parameterSet); break; - case 3: + case 3 : measureDiscreteEOC<typename GridType::LeafGridView,3,TargetSpace>(grid->leafGridView(), referenceGrid->leafGridView(), parameterSet); break; - default: - DUNE_THROW(NotImplemented, "Order '" << order << "' is not implemented"); + default : + DUNE_THROW(NotImplemented, "Order '" << order << "' is not implemented"); } return; // Success } @@ -712,20 +712,20 @@ void measureEOC(const std::shared_ptr<GridType> grid, { switch (order) { - case 1: + case 1 : measureAnalyticalEOC<typename GridType::LeafGridView,1,TargetSpace>(grid->leafGridView(), parameterSet); break; - case 2: + case 2 : measureAnalyticalEOC<typename GridType::LeafGridView,2,TargetSpace>(grid->leafGridView(), parameterSet); break; - case 3: + case 3 : measureAnalyticalEOC<typename GridType::LeafGridView,3,TargetSpace>(grid->leafGridView(), parameterSet); break; - default: - DUNE_THROW(NotImplemented, "Order '" << order << "' is not implemented"); + default : + DUNE_THROW(NotImplemented, "Order '" << order << "' is not implemented"); } return; // Success } @@ -743,9 +743,9 @@ int main (int argc, char *argv[]) try Python::run("import math"); Python::runStream() - << std::endl << "import sys" - << std::endl << "sys.path.append('/home/sander/dune/dune-gfe/problems')" - << std::endl; + << std::endl << "import sys" + << std::endl << "sys.path.append('/home/sander/dune/dune-gfe/problems')" + << std::endl; // parse data file ParameterTree parameterSet; @@ -763,10 +763,10 @@ int main (int argc, char *argv[]) try // Create the grids ///////////////////////////////////////// #if HAVE_DUNE_FOAMGRID - typedef std::conditional<dim==1 or dim!=dimworld,FoamGrid<dim,dimworld>,UGGrid<dim> >::type GridType; + typedef std::conditional<dim==1 or dim!=dimworld,FoamGrid<dim,dimworld>,UGGrid<dim> >::type GridType; #else - static_assert(dim==dimworld, "You need to have dune-foamgrid installed for dim != dimworld!"); - typedef std::conditional<dim==1,OneDGrid,UGGrid<dim> >::type GridType; + static_assert(dim==dimworld, "You need to have dune-foamgrid installed for dim != dimworld!"); + typedef std::conditional<dim==1,OneDGrid,UGGrid<dim> >::type GridType; #endif const int numLevels = parameterSet.get<int>("numLevels"); @@ -818,102 +818,102 @@ int main (int argc, char *argv[]) try switch (targetDim) { - case 1: - if (targetSpace=="RealTuple") - { - measureEOC<GridType,RealTuple<double,1> >(grid, - referenceGrid, - parameterSet); - } else if (targetSpace=="UnitVector") - { - measureEOC<GridType,UnitVector<double,1> >(grid, - referenceGrid, - parameterSet); - } else - DUNE_THROW(NotImplemented, "Target space '" << targetSpace << "' is not implemented"); - break; + case 1 : + if (targetSpace=="RealTuple") + { + measureEOC<GridType,RealTuple<double,1> >(grid, + referenceGrid, + parameterSet); + } else if (targetSpace=="UnitVector") + { + measureEOC<GridType,UnitVector<double,1> >(grid, + referenceGrid, + parameterSet); + } else + DUNE_THROW(NotImplemented, "Target space '" << targetSpace << "' is not implemented"); + break; - case 2: - if (targetSpace=="RealTuple") - { - measureEOC<GridType,RealTuple<double,2> >(grid, - referenceGrid, - parameterSet); - } else if (targetSpace=="UnitVector") - { - measureEOC<GridType,UnitVector<double,2> >(grid, - referenceGrid, - parameterSet); -#if 0 - } else if (targetSpace=="Rotation") - { - measureEOC<GridType,Rotation<double,2> >(grid, + case 2 : + if (targetSpace=="RealTuple") + { + measureEOC<GridType,RealTuple<double,2> >(grid, + referenceGrid, + parameterSet); + } else if (targetSpace=="UnitVector") + { + measureEOC<GridType,UnitVector<double,2> >(grid, referenceGrid, parameterSet); - } else if (targetSpace=="RigidBodyMotion") - { - measureEOC<GridType,RigidBodyMotion<double,2> >(grid, - referenceGrid, - parameterSet); +#if 0 + } else if (targetSpace=="Rotation") + { + measureEOC<GridType,Rotation<double,2> >(grid, + referenceGrid, + parameterSet); + } else if (targetSpace=="RigidBodyMotion") + { + measureEOC<GridType,RigidBodyMotion<double,2> >(grid, + referenceGrid, + parameterSet); #endif - } else - DUNE_THROW(NotImplemented, "Target space '" << targetSpace << "' is not implemented"); - break; + } else + DUNE_THROW(NotImplemented, "Target space '" << targetSpace << "' is not implemented"); + break; - case 3: - if (targetSpace=="RealTuple") - { - measureEOC<GridType,RealTuple<double,3> >(grid, - referenceGrid, - parameterSet); - } else if (targetSpace=="UnitVector") - { - measureEOC<GridType,UnitVector<double,3> >(grid, - referenceGrid, - parameterSet); - } else if (targetSpace=="Rotation") - { - measureEOC<GridType,Rotation<double,3> >(grid, + case 3 : + if (targetSpace=="RealTuple") + { + measureEOC<GridType,RealTuple<double,3> >(grid, + referenceGrid, + parameterSet); + } else if (targetSpace=="UnitVector") + { + measureEOC<GridType,UnitVector<double,3> >(grid, referenceGrid, parameterSet); - } else if (targetSpace=="RigidBodyMotion") - { - measureEOC<GridType,RigidBodyMotion<double,3> >(grid, - referenceGrid, - parameterSet); - } else - DUNE_THROW(NotImplemented, "Target space '" << targetSpace << "' is not implemented"); - break; + } else if (targetSpace=="Rotation") + { + measureEOC<GridType,Rotation<double,3> >(grid, + referenceGrid, + parameterSet); + } else if (targetSpace=="RigidBodyMotion") + { + measureEOC<GridType,RigidBodyMotion<double,3> >(grid, + referenceGrid, + parameterSet); + } else + DUNE_THROW(NotImplemented, "Target space '" << targetSpace << "' is not implemented"); + break; - case 4: - if (targetSpace=="RealTuple") - { - measureEOC<GridType,RealTuple<double,4> >(grid, - referenceGrid, - parameterSet); - } else if (targetSpace=="UnitVector") - { - measureEOC<GridType,UnitVector<double,4> >(grid, - referenceGrid, - parameterSet); -#if 0 - } else if (targetSpace=="Rotation") - { - measureEOC<GridType,Rotation<double,4> >(grid, + case 4 : + if (targetSpace=="RealTuple") + { + measureEOC<GridType,RealTuple<double,4> >(grid, + referenceGrid, + parameterSet); + } else if (targetSpace=="UnitVector") + { + measureEOC<GridType,UnitVector<double,4> >(grid, referenceGrid, parameterSet); - } else if (targetSpace=="RigidBodyMotion") - { - measureEOC<GridType,RigidBodyMotion<double,4> >(grid, - referenceGrid, - parameterSet); +#if 0 + } else if (targetSpace=="Rotation") + { + measureEOC<GridType,Rotation<double,4> >(grid, + referenceGrid, + parameterSet); + } else if (targetSpace=="RigidBodyMotion") + { + measureEOC<GridType,RigidBodyMotion<double,4> >(grid, + referenceGrid, + parameterSet); #endif - } else - DUNE_THROW(NotImplemented, "Target space '" << targetSpace << "' is not implemented"); - break; + } else + DUNE_THROW(NotImplemented, "Target space '" << targetSpace << "' is not implemented"); + break; - default: - DUNE_THROW(NotImplemented, "Target dimension '" << targetDim << "' is not implemented"); + default : + DUNE_THROW(NotImplemented, "Target dimension '" << targetDim << "' is not implemented"); } return 0; diff --git a/src/cosserat-continuum.cc b/src/cosserat-continuum.cc index b8428902..c81a9192 100644 --- a/src/cosserat-continuum.cc +++ b/src/cosserat-continuum.cc @@ -101,321 +101,325 @@ using namespace Dune; int main (int argc, char *argv[]) try { - // initialize MPI, finalize is done automatically on exit - Dune::MPIHelper& mpiHelper = MPIHelper::instance(argc, argv); + // initialize MPI, finalize is done automatically on exit + Dune::MPIHelper& mpiHelper = MPIHelper::instance(argc, argv); - if (mpiHelper.rank()==0) { - std::cout << "DISPLACEMENTORDER = " << displacementOrder << ", ROTATIONORDER = " << rotationOrder << std::endl; - std::cout << "dim = " << dim << ", dimworld = " << dimworld << std::endl; + if (mpiHelper.rank()==0) { + std::cout << "DISPLACEMENTORDER = " << displacementOrder << ", ROTATIONORDER = " << rotationOrder << std::endl; + std::cout << "dim = " << dim << ", dimworld = " << dimworld << std::endl; #if MIXED_SPACE - std::cout << "MIXED_SPACE = 1" << std::endl; + std::cout << "MIXED_SPACE = 1" << std::endl; #else - std::cout << "MIXED_SPACE = 0" << std::endl; + std::cout << "MIXED_SPACE = 0" << std::endl; #endif - } - // Start Python interpreter - Python::start(); - Python::Reference main = Python::import("__main__"); - Python::run("import math"); - - //feenableexcept(FE_INVALID); - Python::runStream() - << std::endl << "import sys" - << std::endl << "sys.path.append('" << argv[1] << "')" - << std::endl; - - using namespace TypeTree::Indices; - using SolutionType = TupleVector<std::vector<RealTuple<double,3> >, - std::vector<Rotation<double,3> > >; - - // parse data file - ParameterTree parameterSet; - if (argc < 3) - DUNE_THROW(Exception, "Usage: ./cosserat-continuum <python path> <parameter file>"); - - ParameterTreeParser::readINITree(argv[2], parameterSet); - - ParameterTreeParser::readOptions(argc, argv, parameterSet); - - // read solver settings - const int numLevels = parameterSet.get<int>("numLevels"); - int numHomotopySteps = parameterSet.get<int>("numHomotopySteps"); - const double tolerance = parameterSet.get<double>("tolerance"); - const int maxSolverSteps = parameterSet.get<int>("maxSolverSteps"); - const double initialRegularization = parameterSet.get<double>("initialRegularization", 1000); - const double initialTrustRegionRadius = parameterSet.get<double>("initialTrustRegionRadius"); - const int multigridIterations = parameterSet.get<int>("numIt"); - const int nu1 = parameterSet.get<int>("nu1"); - const int nu2 = parameterSet.get<int>("nu2"); - const int mu = parameterSet.get<int>("mu"); - const int baseIterations = parameterSet.get<int>("baseIt"); - const double mgTolerance = parameterSet.get<double>("mgTolerance"); - const double baseTolerance = parameterSet.get<double>("baseTolerance"); - const bool instrumented = parameterSet.get<bool>("instrumented"); - const bool adolcScalarMode = parameterSet.get<bool>("adolcScalarMode", false); - const std::string resultPath = parameterSet.get("resultPath", ""); - - // /////////////////////////////////////// - // Create the grid - // /////////////////////////////////////// + } + // Start Python interpreter + Python::start(); + Python::Reference main = Python::import("__main__"); + Python::run("import math"); + + //feenableexcept(FE_INVALID); + Python::runStream() + << std::endl << "import sys" + << std::endl << "sys.path.append('" << argv[1] << "')" + << std::endl; + + using namespace TypeTree::Indices; + using SolutionType = TupleVector<std::vector<RealTuple<double,3> >, + std::vector<Rotation<double,3> > >; + + // parse data file + ParameterTree parameterSet; + if (argc < 3) + DUNE_THROW(Exception, "Usage: ./cosserat-continuum <python path> <parameter file>"); + + ParameterTreeParser::readINITree(argv[2], parameterSet); + + ParameterTreeParser::readOptions(argc, argv, parameterSet); + + // read solver settings + const int numLevels = parameterSet.get<int>("numLevels"); + int numHomotopySteps = parameterSet.get<int>("numHomotopySteps"); + const double tolerance = parameterSet.get<double>("tolerance"); + const int maxSolverSteps = parameterSet.get<int>("maxSolverSteps"); + const double initialRegularization = parameterSet.get<double>("initialRegularization", 1000); + const double initialTrustRegionRadius = parameterSet.get<double>("initialTrustRegionRadius"); + const int multigridIterations = parameterSet.get<int>("numIt"); + const int nu1 = parameterSet.get<int>("nu1"); + const int nu2 = parameterSet.get<int>("nu2"); + const int mu = parameterSet.get<int>("mu"); + const int baseIterations = parameterSet.get<int>("baseIt"); + const double mgTolerance = parameterSet.get<double>("mgTolerance"); + const double baseTolerance = parameterSet.get<double>("baseTolerance"); + const bool instrumented = parameterSet.get<bool>("instrumented"); + const bool adolcScalarMode = parameterSet.get<bool>("adolcScalarMode", false); + const std::string resultPath = parameterSet.get("resultPath", ""); + + // /////////////////////////////////////// + // Create the grid + // /////////////////////////////////////// #if HAVE_DUNE_FOAMGRID - typedef std::conditional<dim==dimworld,UGGrid<dim>, FoamGrid<dim,dimworld> >::type GridType; + typedef std::conditional<dim==dimworld,UGGrid<dim>, FoamGrid<dim,dimworld> >::type GridType; #else - static_assert(dim==dimworld, "FoamGrid needs to be installed to allow problems with dim != dimworld."); - typedef UGGrid<dim> GridType; + static_assert(dim==dimworld, "FoamGrid needs to be installed to allow problems with dim != dimworld."); + typedef UGGrid<dim> GridType; #endif - std::shared_ptr<GridType> grid; + std::shared_ptr<GridType> grid; - GridFactory<GridType> factory; - Gmsh4::LagrangeGridCreator creator{factory}; + GridFactory<GridType> factory; + Gmsh4::LagrangeGridCreator creator{factory}; - FieldVector<double,dimworld> lower(0), upper(1); + FieldVector<double,dimworld> lower(0), upper(1); - std::string structuredGridType = parameterSet["structuredGrid"]; - if (structuredGridType == "cube") { - if (dim!=dimworld) - DUNE_THROW(GridError, "Please use FoamGrid and read in a grid for problems with dim != dimworld."); + std::string structuredGridType = parameterSet["structuredGrid"]; + if (structuredGridType == "cube") { + if (dim!=dimworld) + DUNE_THROW(GridError, "Please use FoamGrid and read in a grid for problems with dim != dimworld."); - lower = parameterSet.get<FieldVector<double,dimworld> >("lower"); - upper = parameterSet.get<FieldVector<double,dimworld> >("upper"); + lower = parameterSet.get<FieldVector<double,dimworld> >("lower"); + upper = parameterSet.get<FieldVector<double,dimworld> >("upper"); - auto elements = parameterSet.get<std::array<unsigned int,dim> >("elements"); - grid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements); + auto elements = parameterSet.get<std::array<unsigned int,dim> >("elements"); + grid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements); - } else { - std::string path = parameterSet.get<std::string>("path", ""); - std::string gridFile = parameterSet.get<std::string>("gridFile"); + } else { + std::string path = parameterSet.get<std::string>("path", ""); + std::string gridFile = parameterSet.get<std::string>("gridFile"); - // Guess the grid file format by looking at the file name suffix - auto dotPos = gridFile.rfind('.'); - if (dotPos == std::string::npos) - DUNE_THROW(IOError, "Could not determine grid input file format"); - std::string suffix = gridFile.substr(dotPos, gridFile.length()-dotPos); + // Guess the grid file format by looking at the file name suffix + auto dotPos = gridFile.rfind('.'); + if (dotPos == std::string::npos) + DUNE_THROW(IOError, "Could not determine grid input file format"); + std::string suffix = gridFile.substr(dotPos, gridFile.length()-dotPos); - if (suffix == ".msh") { - Gmsh4Reader reader{creator}; - reader.read(path + "/" + gridFile); - grid = factory.createGrid(); - } else if (suffix == ".vtu" or suffix == ".vtp") + if (suffix == ".msh") { + Gmsh4Reader reader{creator}; + reader.read(path + "/" + gridFile); + grid = factory.createGrid(); + } else if (suffix == ".vtu" or suffix == ".vtp") #if HAVE_DUNE_VTK - grid = VtkReader<GridType>::createGridFromFile(path + "/" + gridFile); + grid = VtkReader<GridType>::createGridFromFile(path + "/" + gridFile); #else - DUNE_THROW(NotImplemented, "Please install dune-vtk for VTK reading support!"); + DUNE_THROW(NotImplemented, "Please install dune-vtk for VTK reading support!"); #endif - } + } - grid->globalRefine(numLevels-1); + grid->globalRefine(numLevels-1); - grid->loadBalance(); + grid->loadBalance(); - if (mpiHelper.rank()==0) - std::cout << "There are " << grid->leafGridView().comm().size() << " processes" << std::endl; + if (mpiHelper.rank()==0) + std::cout << "There are " << grid->leafGridView().comm().size() << " processes" << std::endl; - typedef GridType::LeafGridView GridView; - GridView gridView = grid->leafGridView(); + typedef GridType::LeafGridView GridView; + GridView gridView = grid->leafGridView(); - using namespace Dune::Functions::BasisFactory; + using namespace Dune::Functions::BasisFactory; - const int dimRotation = Rotation<double,3>::embeddedDim; - auto compositeBasis = makeBasis( - gridView, - composite( - power<3>( - lagrange<displacementOrder>() + const int dimRotation = Rotation<double,3>::embeddedDim; + auto compositeBasis = makeBasis( + gridView, + composite( + power<3>( + lagrange<displacementOrder>() ), - power<dimRotation>( - lagrange<rotationOrder>() + power<dimRotation>( + lagrange<rotationOrder>() ) - )); - using CompositeBasis = decltype(compositeBasis); - - auto deformationPowerBasis = makeBasis( - gridView, - power<3>( - lagrange<displacementOrder>() - )); - - auto orientationPowerBasis = makeBasis( - gridView, - power<3>( - power<3>( - lagrange<rotationOrder>() + )); + using CompositeBasis = decltype(compositeBasis); + + auto deformationPowerBasis = makeBasis( + gridView, + power<3>( + lagrange<displacementOrder>() + )); + + auto orientationPowerBasis = makeBasis( + gridView, + power<3>( + power<3>( + lagrange<rotationOrder>() ) - )); + )); - BlockVector<FieldVector<double,dimworld> > identityDeformation(compositeBasis.size({0})); - auto identityDeformationBasis = makeBasis( - gridView, - power<dimworld>( - lagrange<displacementOrder>() - )); - Dune::Functions::interpolate(identityDeformationBasis, identityDeformation, [&](FieldVector<double,dimworld> x){ return x;}); + BlockVector<FieldVector<double,dimworld> > identityDeformation(compositeBasis.size({0})); + auto identityDeformationBasis = makeBasis( + gridView, + power<dimworld>( + lagrange<displacementOrder>() + )); + Dune::Functions::interpolate(identityDeformationBasis, identityDeformation, [&](FieldVector<double,dimworld> x){ + return x; + }); - BlockVector<FieldVector<double,dimworld> > identityRotation(compositeBasis.size({1})); - auto identityRotationBasis = makeBasis( - gridView, - power<dimworld>( - lagrange<rotationOrder>() - )); - Dune::Functions::interpolate(identityRotationBasis, identityRotation, [&](FieldVector<double,dimworld> x){ return x;}); + BlockVector<FieldVector<double,dimworld> > identityRotation(compositeBasis.size({1})); + auto identityRotationBasis = makeBasis( + gridView, + power<dimworld>( + lagrange<rotationOrder>() + )); + Dune::Functions::interpolate(identityRotationBasis, identityRotation, [&](FieldVector<double,dimworld> x){ + return x; + }); - typedef Dune::Functions::LagrangeBasis<GridView,displacementOrder> DeformationFEBasis; - typedef Dune::Functions::LagrangeBasis<GridView,rotationOrder> OrientationFEBasis; + typedef Dune::Functions::LagrangeBasis<GridView,displacementOrder> DeformationFEBasis; + typedef Dune::Functions::LagrangeBasis<GridView,rotationOrder> OrientationFEBasis; - DeformationFEBasis deformationFEBasis(gridView); - OrientationFEBasis orientationFEBasis(gridView); + DeformationFEBasis deformationFEBasis(gridView); + OrientationFEBasis orientationFEBasis(gridView); - // ///////////////////////////////////////// - // Read Dirichlet values - // ///////////////////////////////////////// + // ///////////////////////////////////////// + // Read Dirichlet values + // ///////////////////////////////////////// - BitSetVector<1> neumannVertices(gridView.size(dim), false); - BitSetVector<3> deformationDirichletDofs(deformationFEBasis.size(), false); - BitSetVector<3> orientationDirichletDofs(orientationFEBasis.size(), false); + BitSetVector<1> neumannVertices(gridView.size(dim), false); + BitSetVector<3> deformationDirichletDofs(deformationFEBasis.size(), false); + BitSetVector<3> orientationDirichletDofs(orientationFEBasis.size(), false); - const GridView::IndexSet& indexSet = gridView.indexSet(); + const GridView::IndexSet& indexSet = gridView.indexSet(); - // Make Python function that computes which vertices are on the Dirichlet boundary, based on the vertex positions. - std::string lambda = std::string("lambda x: (") + parameterSet.get<std::string>("dirichletVerticesPredicate") + std::string(")"); - auto pythonDirichletVertices = Python::make_function<FieldVector<bool,3>>(Python::evaluate(lambda)); + // Make Python function that computes which vertices are on the Dirichlet boundary, based on the vertex positions. + std::string lambda = std::string("lambda x: (") + parameterSet.get<std::string>("dirichletVerticesPredicate") + std::string(")"); + auto pythonDirichletVertices = Python::make_function<FieldVector<bool,3> >(Python::evaluate(lambda)); - lambda = std::string("lambda x: (") + parameterSet.get<std::string>("dirichletRotationVerticesPredicate") + std::string(")"); - auto pythonOrientationDirichletVertices = Python::make_function<bool>(Python::evaluate(lambda)); + lambda = std::string("lambda x: (") + parameterSet.get<std::string>("dirichletRotationVerticesPredicate") + std::string(")"); + auto pythonOrientationDirichletVertices = Python::make_function<bool>(Python::evaluate(lambda)); - // Same for the Neumann boundary - lambda = std::string("lambda x: (") + parameterSet.get<std::string>("neumannVerticesPredicate", "0") + std::string(")"); - auto pythonNeumannVertices = Python::make_function<bool>(Python::evaluate(lambda)); + // Same for the Neumann boundary + lambda = std::string("lambda x: (") + parameterSet.get<std::string>("neumannVerticesPredicate", "0") + std::string(")"); + auto pythonNeumannVertices = Python::make_function<bool>(Python::evaluate(lambda)); - for (auto&& vertex : vertices(gridView)) - { - bool isNeumann = pythonNeumannVertices(vertex.geometry().corner(0)); - neumannVertices[indexSet.index(vertex)] = isNeumann; - } + for (auto&& vertex : vertices(gridView)) + { + bool isNeumann = pythonNeumannVertices(vertex.geometry().corner(0)); + neumannVertices[indexSet.index(vertex)] = isNeumann; + } - BoundaryPatch<GridView> neumannBoundary(gridView, neumannVertices); + BoundaryPatch<GridView> neumannBoundary(gridView, neumannVertices); - std::cout << "On rank " << mpiHelper.rank() << ": Neumann boundary has " << neumannBoundary.numFaces() - << " faces and " << neumannVertices.count() << " degrees of freedom.\n"; - - BitSetVector<1> neumannNodes(deformationFEBasis.size(), false); - constructBoundaryDofs(neumannBoundary,deformationFEBasis,neumannNodes); + std::cout << "On rank " << mpiHelper.rank() << ": Neumann boundary has " << neumannBoundary.numFaces() + << " faces and " << neumannVertices.count() << " degrees of freedom.\n"; - for (size_t i=0; i<deformationFEBasis.size(); i++) { - FieldVector<bool,3> isDirichlet; - isDirichlet = pythonDirichletVertices(identityDeformation[i]); - for (size_t j=0; j<3; j++) - deformationDirichletDofs[i][j] = isDirichlet[j]; - } + BitSetVector<1> neumannNodes(deformationFEBasis.size(), false); + constructBoundaryDofs(neumannBoundary,deformationFEBasis,neumannNodes); + for (size_t i=0; i<deformationFEBasis.size(); i++) { + FieldVector<bool,3> isDirichlet; + isDirichlet = pythonDirichletVertices(identityDeformation[i]); + for (size_t j=0; j<3; j++) + deformationDirichletDofs[i][j] = isDirichlet[j]; + } - for (size_t i=0; i<orientationFEBasis.size(); i++) { - bool isDirichletOrientation = pythonOrientationDirichletVertices(identityRotation[i]); - for (size_t j=0; j<3; j++) - orientationDirichletDofs[i][j] = isDirichletOrientation; - } - std::cout << "On rank " << mpiHelper.rank() << ": Dirichlet boundary has " << deformationDirichletDofs.count() << " degrees of freedom.\n"; - std::cout << "On rank " << mpiHelper.rank() << ": Dirichlet boundary (orientation) has " << orientationDirichletDofs.count() << " degrees of freedom.\n"; + for (size_t i=0; i<orientationFEBasis.size(); i++) { + bool isDirichletOrientation = pythonOrientationDirichletVertices(identityRotation[i]); + for (size_t j=0; j<3; j++) + orientationDirichletDofs[i][j] = isDirichletOrientation; + } - // ////////////////////////// - // Initial iterate - // ////////////////////////// + std::cout << "On rank " << mpiHelper.rank() << ": Dirichlet boundary has " << deformationDirichletDofs.count() << " degrees of freedom.\n"; + std::cout << "On rank " << mpiHelper.rank() << ": Dirichlet boundary (orientation) has " << orientationDirichletDofs.count() << " degrees of freedom.\n"; - SolutionType x; + // ////////////////////////// + // Initial iterate + // ////////////////////////// - x[_0].resize(compositeBasis.size({0})); + SolutionType x; - lambda = std::string("lambda x: (") + parameterSet.get<std::string>("initialDeformation") + std::string(")"); - auto pythonInitialDeformation = Python::make_function<FieldVector<double,3> >(Python::evaluate(lambda)); + x[_0].resize(compositeBasis.size({0})); - std::vector<FieldVector<double,3> > v; - Functions::interpolate(deformationPowerBasis, v, pythonInitialDeformation); + lambda = std::string("lambda x: (") + parameterSet.get<std::string>("initialDeformation") + std::string(")"); + auto pythonInitialDeformation = Python::make_function<FieldVector<double,3> >(Python::evaluate(lambda)); - for (size_t i=0; i<x[_0].size(); i++) - x[_0][i] = v[i]; + std::vector<FieldVector<double,3> > v; + Functions::interpolate(deformationPowerBasis, v, pythonInitialDeformation); - x[_1].resize(compositeBasis.size({1})); + for (size_t i=0; i<x[_0].size(); i++) + x[_0][i] = v[i]; + + x[_1].resize(compositeBasis.size({1})); #if !MIXED_SPACE - if (parameterSet.hasKey("startFromFile")) + if (parameterSet.hasKey("startFromFile")) + { + std::shared_ptr<GridType> initialIterateGrid; + if (parameterSet.get<bool>("structuredGrid")) { - std::shared_ptr<GridType> initialIterateGrid; - if (parameterSet.get<bool>("structuredGrid")) - { - std::array<unsigned int,dim> elements = parameterSet.get<std::array<unsigned int,dim> >("elements"); - initialIterateGrid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements); - } - else - { - std::string path = parameterSet.get<std::string>("path"); - std::string initialIterateGridFilename = parameterSet.get<std::string>("initialIterateGridFilename"); + std::array<unsigned int,dim> elements = parameterSet.get<std::array<unsigned int,dim> >("elements"); + initialIterateGrid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements); + } + else + { + std::string path = parameterSet.get<std::string>("path"); + std::string initialIterateGridFilename = parameterSet.get<std::string>("initialIterateGridFilename"); - initialIterateGrid = std::shared_ptr<GridType>(Gmsh4Reader<GridType>::createGridFromFile(path + "/" + initialIterateGridFilename)); - } + initialIterateGrid = std::shared_ptr<GridType>(Gmsh4Reader<GridType>::createGridFromFile(path + "/" + initialIterateGridFilename)); + } - std::vector<TargetSpace> initialIterate; - GFE::CosseratVTKReader::read(initialIterate, parameterSet.get<std::string>("initialIterateFilename")); + std::vector<TargetSpace> initialIterate; + GFE::CosseratVTKReader::read(initialIterate, parameterSet.get<std::string>("initialIterateFilename")); - typedef Dune::Functions::LagrangeBasis<typename GridType::LeafGridView, 2> InitialBasis; - InitialBasis initialBasis(initialIterateGrid->leafGridView()); + typedef Dune::Functions::LagrangeBasis<typename GridType::LeafGridView, 2> InitialBasis; + InitialBasis initialBasis(initialIterateGrid->leafGridView()); #ifdef PROJECTED_INTERPOLATION - using LocalInterpolationRule = GFE::LocalProjectedFEFunction<dim, double, DeformationFEBasis::LocalView::Tree::FiniteElement, TargetSpace>; + using LocalInterpolationRule = GFE::LocalProjectedFEFunction<dim, double, DeformationFEBasis::LocalView::Tree::FiniteElement, TargetSpace>; #else - using LocalInterpolationRule = LocalGeodesicFEFunction<dim, double, DeformationFEBasis::LocalView::Tree::FiniteElement, TargetSpace>; + using LocalInterpolationRule = LocalGeodesicFEFunction<dim, double, DeformationFEBasis::LocalView::Tree::FiniteElement, TargetSpace>; #endif - GFE::EmbeddedGlobalGFEFunction<InitialBasis,LocalInterpolationRule,TargetSpace> initialFunction(initialBasis,initialIterate); - auto powerBasis = makeBasis( - gridView, - power<7>( - lagrange<displacementOrder>() - )); - std::vector<FieldVector<double,7> > v; - //TODO: Interpolate does not work with an GFE:EmbeddedGlobalGFEFunction - //Dune::Functions::interpolate(powerBasis,v,initialFunction); - - for (size_t i=0; i<x.size(); i++) { - auto vTargetSpace = TargetSpace(v[i]); - x[_0][i] = vTargetSpace.r; - x[_1][i] = vTargetSpace.q; - } + GFE::EmbeddedGlobalGFEFunction<InitialBasis,LocalInterpolationRule,TargetSpace> initialFunction(initialBasis,initialIterate); + auto powerBasis = makeBasis( + gridView, + power<7>( + lagrange<displacementOrder>() + )); + std::vector<FieldVector<double,7> > v; + //TODO: Interpolate does not work with an GFE:EmbeddedGlobalGFEFunction + //Dune::Functions::interpolate(powerBasis,v,initialFunction); + + for (size_t i=0; i<x.size(); i++) { + auto vTargetSpace = TargetSpace(v[i]); + x[_0][i] = vTargetSpace.r; + x[_1][i] = vTargetSpace.q; } + } #endif - //////////////////////////////////////////////////////// - // Main homotopy loop - //////////////////////////////////////////////////////// + //////////////////////////////////////////////////////// + // Main homotopy loop + //////////////////////////////////////////////////////// - // Output initial iterate (of homotopy loop) - if (dim == 2 && dimworld == 2) { + // Output initial iterate (of homotopy loop) + if (dim == 2 && dimworld == 2) { CosseratVTKWriter<GridType>::writeMixed<DeformationFEBasis,OrientationFEBasis>(deformationFEBasis,x[_0], orientationFEBasis,x[_1], resultPath + "cosserat_homotopy_0_l" + std::to_string(numLevels)); - } else if (dim == 2 && dimworld == 3) { + } else if (dim == 2 && dimworld == 3) { #if MIXED_SPACE CosseratVTKWriter<GridType>::write<DeformationFEBasis>(deformationFEBasis, x[_0], resultPath + "cosserat_homotopy_0_l" + std::to_string(numLevels)); #else CosseratVTKWriter<GridType>::write<DeformationFEBasis>(deformationFEBasis, x, resultPath + "cosserat_homotopy_0_l" + std::to_string(numLevels)); -#endif - } else if (dim == 3 && dimworld == 3) { - +#endif + } else if (dim == 3 && dimworld == 3) { + BlockVector<FieldVector<double,dimworld> > displacement(x[_0].size()); for (size_t i=0; i<x[_0].size(); i++) { - for (int j = 0; j < 3; j ++) - displacement[i][j] = x[_0][i][j] - identityDeformation[i][j]; + for (int j = 0; j < 3; j ++) + displacement[i][j] = x[_0][i][j] - identityDeformation[i][j]; } - auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(deformationPowerBasis, displacement); + auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(deformationPowerBasis, displacement); SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(displacementOrder-1)); vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dim)); vtkWriter.write(resultPath + "cosserat_homotopy_0_l" + std::to_string(numLevels)); - } - for (int i=0; i<numHomotopySteps; i++) { + } + for (int i=0; i<numHomotopySteps; i++) { - double homotopyParameter = (i+1)*(1.0/numHomotopySteps); - if (mpiHelper.rank()==0) - std::cout << "Homotopy step: " << i << ", parameter: " << homotopyParameter << std::endl; + double homotopyParameter = (i+1)*(1.0/numHomotopySteps); + if (mpiHelper.rank()==0) + std::cout << "Homotopy step: " << i << ", parameter: " << homotopyParameter << std::endl; // //////////////////////////////////////////////////////////// @@ -425,13 +429,13 @@ int main (int argc, char *argv[]) try const ParameterTree& materialParameters = parameterSet.sub("materialParameters"); FieldVector<double,3> neumannValues {0,0,0}; if (parameterSet.hasKey("neumannValues")) - neumannValues = parameterSet.get<FieldVector<double,3> >("neumannValues"); + neumannValues = parameterSet.get<FieldVector<double,3> >("neumannValues"); auto neumannFunction = [&]( FieldVector<double,dimworld> ) { - auto nV = neumannValues; - nV *= (-homotopyParameter); - return nV; - }; + auto nV = neumannValues; + nV *= (-homotopyParameter); + return nV; + }; Python::Reference volumeLoadClass = Python::import(parameterSet.get<std::string>("volumeLoadPythonFunction", "zero-volume-load")); Python::Callable volumeLoadCallable = volumeLoadClass.get("VolumeLoad"); @@ -443,66 +447,105 @@ int main (int argc, char *argv[]) try auto volumeLoad = Python::make_function<FieldVector<double,3> > (volumeLoadPythonObject.get("volumeLoad")); if (mpiHelper.rank() == 0) { - std::cout << "Material parameters:" << std::endl; - materialParameters.report(); + std::cout << "Material parameters:" << std::endl; + materialParameters.report(); } - //////////////////////////////////////////////////////// - // Set Dirichlet values - //////////////////////////////////////////////////////// - - Python::Reference dirichletValuesClass = Python::import(parameterSet.get<std::string>("problem") + "-dirichlet-values"); - - Python::Callable C = dirichletValuesClass.get("DirichletValues"); - - // Call a constructor. - Python::Reference dirichletValuesPythonObject = C(homotopyParameter); - - // Extract object member functions as Dune functions - auto deformationDirichletValues = Python::make_function<FieldVector<double,3> > (dirichletValuesPythonObject.get("deformation")); - auto orientationDirichletValues = Python::make_function<FieldMatrix<double,3,3> > (dirichletValuesPythonObject.get("orientation")); - - BlockVector<FieldVector<double,3> > ddV; - Dune::Functions::interpolate(deformationPowerBasis, ddV, deformationDirichletValues); - - BlockVector<FieldMatrix<double,3,3> > dOV; - Dune::Functions::interpolate(orientationPowerBasis, dOV, orientationDirichletValues); - - for (std::size_t i = 0; i < compositeBasis.size({0}); i++) { - FieldVector<double,3> x0i = x[_0][i].globalCoordinates(); - for (int j=0; j<3; j++) { - if (deformationDirichletDofs[i][j]) - x0i[j] = ddV[i][j]; - } - x[_0][i] = x0i; - } - for (std::size_t i = 0; i < compositeBasis.size({1}); i++) - if (orientationDirichletDofs[i][0]) - x[_1][i].set(dOV[i]); - - if (dim==dimworld) { - CosseratEnergyLocalStiffness<CompositeBasis, 3,adouble> localCosseratEnergy(materialParameters, - &neumannBoundary, - neumannFunction, - volumeLoad); - MixedLocalGFEADOLCStiffness<CompositeBasis, - RealTuple<double,3>, - Rotation<double,3> > localGFEADOLCStiffness(&localCosseratEnergy, adolcScalarMode); - MixedGFEAssembler<CompositeBasis, - RealTuple<double,3>, - Rotation<double,3> > mixedAssembler(compositeBasis, &localGFEADOLCStiffness); + //////////////////////////////////////////////////////// + // Set Dirichlet values + //////////////////////////////////////////////////////// + + Python::Reference dirichletValuesClass = Python::import(parameterSet.get<std::string>("problem") + "-dirichlet-values"); + + Python::Callable C = dirichletValuesClass.get("DirichletValues"); + + // Call a constructor. + Python::Reference dirichletValuesPythonObject = C(homotopyParameter); + + // Extract object member functions as Dune functions + auto deformationDirichletValues = Python::make_function<FieldVector<double,3> > (dirichletValuesPythonObject.get("deformation")); + auto orientationDirichletValues = Python::make_function<FieldMatrix<double,3,3> > (dirichletValuesPythonObject.get("orientation")); + + BlockVector<FieldVector<double,3> > ddV; + Dune::Functions::interpolate(deformationPowerBasis, ddV, deformationDirichletValues); + + BlockVector<FieldMatrix<double,3,3> > dOV; + Dune::Functions::interpolate(orientationPowerBasis, dOV, orientationDirichletValues); + + for (std::size_t i = 0; i < compositeBasis.size({0}); i++) { + FieldVector<double,3> x0i = x[_0][i].globalCoordinates(); + for (int j=0; j<3; j++) { + if (deformationDirichletDofs[i][j]) + x0i[j] = ddV[i][j]; + } + x[_0][i] = x0i; + } + for (std::size_t i = 0; i < compositeBasis.size({1}); i++) + if (orientationDirichletDofs[i][0]) + x[_1][i].set(dOV[i]); + + if (dim==dimworld) { + CosseratEnergyLocalStiffness<CompositeBasis, 3,adouble> localCosseratEnergy(materialParameters, + &neumannBoundary, + neumannFunction, + volumeLoad); + MixedLocalGFEADOLCStiffness<CompositeBasis, + RealTuple<double,3>, + Rotation<double,3> > localGFEADOLCStiffness(&localCosseratEnergy, adolcScalarMode); + MixedGFEAssembler<CompositeBasis, + RealTuple<double,3>, + Rotation<double,3> > mixedAssembler(compositeBasis, &localGFEADOLCStiffness); #if MIXED_SPACE - MixedRiemannianTrustRegionSolver<GridType, - CompositeBasis, - DeformationFEBasis, RealTuple<double,3>, - OrientationFEBasis, Rotation<double,3> > solver; - solver.setup(*grid, - &mixedAssembler, - deformationFEBasis, - orientationFEBasis, - x, - deformationDirichletDofs, - orientationDirichletDofs, tolerance, + MixedRiemannianTrustRegionSolver<GridType, + CompositeBasis, + DeformationFEBasis, RealTuple<double,3>, + OrientationFEBasis, Rotation<double,3> > solver; + solver.setup(*grid, + &mixedAssembler, + deformationFEBasis, + orientationFEBasis, + x, + deformationDirichletDofs, + orientationDirichletDofs, tolerance, + maxSolverSteps, + initialTrustRegionRadius, + multigridIterations, + mgTolerance, + mu, nu1, nu2, + baseIterations, + baseTolerance, + instrumented); + + solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); + + solver.setInitialIterate(x); + solver.solve(); + x = solver.getSol(); +#else + //The MixedRiemannianTrustRegionSolver can treat the Displacement and Orientation Space as separate ones + //The RiemannianTrustRegionSolver can only treat the Displacement and Rotation together in a RigidBodyMotion + //Therefore, x and the dirichletDofs are converted to a RigidBodyMotion structure, as well as the Hessian and Gradient that are returned by the assembler + std::vector<TargetSpace> xTargetSpace(compositeBasis.size({0})); + BitSetVector<TargetSpace::TangentVector::dimension> dirichletDofsTargetSpace(compositeBasis.size({0}), false); + for (std::size_t i = 0; i < compositeBasis.size({0}); i++) { + for (int j = 0; j < 3; j ++) { // Displacement part + xTargetSpace[i].r[j] = x[_0][i][j]; + dirichletDofsTargetSpace[i][j] = deformationDirichletDofs[i][j]; + } + xTargetSpace[i].q = x[_1][i]; // Rotation part + for (int j = 3; j < TargetSpace::TangentVector::dimension; j ++) + dirichletDofsTargetSpace[i][j] = orientationDirichletDofs[i][j-3]; + } + + using GFEAssemblerWrapper = Dune::GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, TargetSpace, RealTuple<double, 3>, Rotation<double,3> >; + GFEAssemblerWrapper assembler(&mixedAssembler, deformationFEBasis); + if (parameterSet.get<std::string>("solvertype", "trustRegion") == "trustRegion") { + RiemannianTrustRegionSolver<DeformationFEBasis, TargetSpace, GFEAssemblerWrapper> solver; + solver.setup(*grid, + &assembler, + xTargetSpace, + dirichletDofsTargetSpace, + tolerance, maxSolverSteps, initialTrustRegionRadius, multigridIterations, @@ -512,97 +555,97 @@ int main (int argc, char *argv[]) try baseTolerance, instrumented); - solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); - - solver.setInitialIterate(x); - solver.solve(); - x = solver.getSol(); -#else - //The MixedRiemannianTrustRegionSolver can treat the Displacement and Orientation Space as separate ones - //The RiemannianTrustRegionSolver can only treat the Displacement and Rotation together in a RigidBodyMotion - //Therefore, x and the dirichletDofs are converted to a RigidBodyMotion structure, as well as the Hessian and Gradient that are returned by the assembler - std::vector<TargetSpace> xTargetSpace(compositeBasis.size({0})); - BitSetVector<TargetSpace::TangentVector::dimension> dirichletDofsTargetSpace(compositeBasis.size({0}), false); - for (std::size_t i = 0; i < compositeBasis.size({0}); i++) { - for (int j = 0; j < 3; j ++) { // Displacement part - xTargetSpace[i].r[j] = x[_0][i][j]; - dirichletDofsTargetSpace[i][j] = deformationDirichletDofs[i][j]; - } - xTargetSpace[i].q = x[_1][i]; // Rotation part - for (int j = 3; j < TargetSpace::TangentVector::dimension; j ++) - dirichletDofsTargetSpace[i][j] = orientationDirichletDofs[i][j-3]; - } - - using GFEAssemblerWrapper = Dune::GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, TargetSpace, RealTuple<double, 3>, Rotation<double,3>>; - GFEAssemblerWrapper assembler(&mixedAssembler, deformationFEBasis); - if (parameterSet.get<std::string>("solvertype", "trustRegion") == "trustRegion") { - RiemannianTrustRegionSolver<DeformationFEBasis, TargetSpace, GFEAssemblerWrapper> solver; - solver.setup(*grid, - &assembler, - xTargetSpace, - dirichletDofsTargetSpace, - tolerance, - maxSolverSteps, - initialTrustRegionRadius, - multigridIterations, - mgTolerance, - mu, nu1, nu2, - baseIterations, - baseTolerance, - instrumented); - - solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); - solver.setInitialIterate(xTargetSpace); - solver.solve(); - xTargetSpace = solver.getSol(); - } else { - RiemannianProximalNewtonSolver<DeformationFEBasis, TargetSpace, GFEAssemblerWrapper> solver; - solver.setup(*grid, - &assembler, - xTargetSpace, - dirichletDofsTargetSpace, - tolerance, - maxSolverSteps, - initialRegularization, - instrumented); - solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); - solver.setInitialIterate(xTargetSpace); - solver.solve(); - xTargetSpace = solver.getSol(); - } - for (std::size_t i = 0; i < xTargetSpace.size(); i++) { - x[_0][i] = xTargetSpace[i].r; - x[_1][i] = xTargetSpace[i].q; - } + solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); + solver.setInitialIterate(xTargetSpace); + solver.solve(); + xTargetSpace = solver.getSol(); + } else { + RiemannianProximalNewtonSolver<DeformationFEBasis, TargetSpace, GFEAssemblerWrapper> solver; + solver.setup(*grid, + &assembler, + xTargetSpace, + dirichletDofsTargetSpace, + tolerance, + maxSolverSteps, + initialRegularization, + instrumented); + solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); + solver.setInitialIterate(xTargetSpace); + solver.solve(); + xTargetSpace = solver.getSol(); + } + for (std::size_t i = 0; i < xTargetSpace.size(); i++) { + x[_0][i] = xTargetSpace[i].r; + x[_1][i] = xTargetSpace[i].q; + } #endif - } else { //dim != dimworld - using StiffnessType = MixedLocalGFEADOLCStiffness<CompositeBasis, RealTuple<double,3>, Rotation<double,3>>; - std::shared_ptr<StiffnessType> localGFEStiffness; + } else { //dim != dimworld + using StiffnessType = MixedLocalGFEADOLCStiffness<CompositeBasis, RealTuple<double,3>, Rotation<double,3> >; + std::shared_ptr<StiffnessType> localGFEStiffness; #if HAVE_DUNE_CURVEDGEOMETRY && WORLD_DIM == 3 && GRID_DIM == 2 - NonplanarCosseratShellEnergy<CompositeBasis, 3, adouble, decltype(creator)> localCosseratEnergy(materialParameters, - &creator, - &neumannBoundary, - neumannFunction, - volumeLoad); + NonplanarCosseratShellEnergy<CompositeBasis, 3, adouble, decltype(creator)> localCosseratEnergy(materialParameters, + &creator, + &neumannBoundary, + neumannFunction, + volumeLoad); - localGFEStiffness = std::make_shared<StiffnessType>(&localCosseratEnergy, adolcScalarMode); + localGFEStiffness = std::make_shared<StiffnessType>(&localCosseratEnergy, adolcScalarMode); #endif - MixedGFEAssembler<CompositeBasis, - RealTuple<double,3>, - Rotation<double,3> > mixedAssembler(compositeBasis, localGFEStiffness.get()); + MixedGFEAssembler<CompositeBasis, + RealTuple<double,3>, + Rotation<double,3> > mixedAssembler(compositeBasis, localGFEStiffness.get()); #if MIXED_SPACE - MixedRiemannianTrustRegionSolver<GridType, - CompositeBasis, - DeformationFEBasis, RealTuple<double,3>, - OrientationFEBasis, Rotation<double,3> > solver; - solver.setup(*grid, - &mixedAssembler, - deformationFEBasis, - orientationFEBasis, - x, - deformationDirichletDofs, - orientationDirichletDofs, tolerance, + MixedRiemannianTrustRegionSolver<GridType, + CompositeBasis, + DeformationFEBasis, RealTuple<double,3>, + OrientationFEBasis, Rotation<double,3> > solver; + solver.setup(*grid, + &mixedAssembler, + deformationFEBasis, + orientationFEBasis, + x, + deformationDirichletDofs, + orientationDirichletDofs, tolerance, + maxSolverSteps, + initialTrustRegionRadius, + multigridIterations, + mgTolerance, + mu, nu1, nu2, + baseIterations, + baseTolerance, + instrumented); + + solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); + + solver.setInitialIterate(x); + solver.solve(); + x = solver.getSol(); +#else + //The MixedRiemannianTrustRegionSolver can treat the Displacement and Orientation Space as separate ones + //The RiemannianTrustRegionSolver can only treat the Displacement and Rotation together in a RigidBodyMotion + //Therefore, x and the dirichletDofs are converted to a RigidBodyMotion structure, as well as the Hessian and Gradient that are returned by the assembler + std::vector<TargetSpace> xTargetSpace(compositeBasis.size({0})); + BitSetVector<TargetSpace::TangentVector::dimension> dirichletDofsTargetSpace(compositeBasis.size({0}), false); + for (std::size_t i = 0; i < compositeBasis.size({0}); i++) { + for (int j = 0; j < 3; j ++) { // Displacement part + xTargetSpace[i].r[j] = x[_0][i][j]; + dirichletDofsTargetSpace[i][j] = deformationDirichletDofs[i][j]; + } + xTargetSpace[i].q = x[_1][i]; // Rotation part + for (int j = 3; j < TargetSpace::TangentVector::dimension; j ++) + dirichletDofsTargetSpace[i][j] = orientationDirichletDofs[i][j-3]; + } + + using GFEAssemblerWrapper = Dune::GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, TargetSpace, RealTuple<double, 3>, Rotation<double,3> >; + GFEAssemblerWrapper assembler(&mixedAssembler, deformationFEBasis); + if (parameterSet.get<std::string>("solvertype", "trustRegion") == "trustRegion") { + RiemannianTrustRegionSolver<DeformationFEBasis, TargetSpace, GFEAssemblerWrapper> solver; + solver.setup(*grid, + &assembler, + xTargetSpace, + dirichletDofsTargetSpace, + tolerance, maxSolverSteps, initialTrustRegionRadius, multigridIterations, @@ -612,124 +655,85 @@ int main (int argc, char *argv[]) try baseTolerance, instrumented); - solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); + solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); + solver.setInitialIterate(xTargetSpace); + solver.solve(); + xTargetSpace = solver.getSol(); + } else { //parameterSet.get<std::string>("solvertype") == "proximalNewton" + RiemannianProximalNewtonSolver<DeformationFEBasis, TargetSpace, GFEAssemblerWrapper> solver; + solver.setup(*grid, + &assembler, + xTargetSpace, + dirichletDofsTargetSpace, + tolerance, + maxSolverSteps, + initialRegularization, + instrumented); + solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); + solver.setInitialIterate(xTargetSpace); + solver.solve(); + xTargetSpace = solver.getSol(); + } - solver.setInitialIterate(x); - solver.solve(); - x = solver.getSol(); -#else - //The MixedRiemannianTrustRegionSolver can treat the Displacement and Orientation Space as separate ones - //The RiemannianTrustRegionSolver can only treat the Displacement and Rotation together in a RigidBodyMotion - //Therefore, x and the dirichletDofs are converted to a RigidBodyMotion structure, as well as the Hessian and Gradient that are returned by the assembler - std::vector<TargetSpace> xTargetSpace(compositeBasis.size({0})); - BitSetVector<TargetSpace::TangentVector::dimension> dirichletDofsTargetSpace(compositeBasis.size({0}), false); - for (std::size_t i = 0; i < compositeBasis.size({0}); i++) { - for (int j = 0; j < 3; j ++) { // Displacement part - xTargetSpace[i].r[j] = x[_0][i][j]; - dirichletDofsTargetSpace[i][j] = deformationDirichletDofs[i][j]; - } - xTargetSpace[i].q = x[_1][i]; // Rotation part - for (int j = 3; j < TargetSpace::TangentVector::dimension; j ++) - dirichletDofsTargetSpace[i][j] = orientationDirichletDofs[i][j-3]; - } - - using GFEAssemblerWrapper = Dune::GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, TargetSpace, RealTuple<double, 3>, Rotation<double,3>>; - GFEAssemblerWrapper assembler(&mixedAssembler, deformationFEBasis); - if (parameterSet.get<std::string>("solvertype", "trustRegion") == "trustRegion") { - RiemannianTrustRegionSolver<DeformationFEBasis, TargetSpace, GFEAssemblerWrapper> solver; - solver.setup(*grid, - &assembler, - xTargetSpace, - dirichletDofsTargetSpace, - tolerance, - maxSolverSteps, - initialTrustRegionRadius, - multigridIterations, - mgTolerance, - mu, nu1, nu2, - baseIterations, - baseTolerance, - instrumented); - - solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); - solver.setInitialIterate(xTargetSpace); - solver.solve(); - xTargetSpace = solver.getSol(); - } else { //parameterSet.get<std::string>("solvertype") == "proximalNewton" - RiemannianProximalNewtonSolver<DeformationFEBasis, TargetSpace, GFEAssemblerWrapper> solver; - solver.setup(*grid, - &assembler, - xTargetSpace, - dirichletDofsTargetSpace, - tolerance, - maxSolverSteps, - initialRegularization, - instrumented); - solver.setScaling(parameterSet.get<FieldVector<double,6> >("solverScaling")); - solver.setInitialIterate(xTargetSpace); - solver.solve(); - xTargetSpace = solver.getSol(); - } - - for (std::size_t i = 0; i < xTargetSpace.size(); i++) { - x[_0][i] = xTargetSpace[i].r; - x[_1][i] = xTargetSpace[i].q; - } + for (std::size_t i = 0; i < xTargetSpace.size(); i++) { + x[_0][i] = xTargetSpace[i].r; + x[_1][i] = xTargetSpace[i].q; + } #endif - } - // Output result of each homotopy step - std::stringstream iAsAscii; - iAsAscii << i+1; - - if (dim == 2 && dimworld == 2) { - CosseratVTKWriter<GridType>::writeMixed<DeformationFEBasis,OrientationFEBasis>(deformationFEBasis,x[_0], - orientationFEBasis,x[_1], - resultPath + "cosserat_homotopy_" + iAsAscii.str()); - } else if (dim == 2 && dimworld == 3) { + } + // Output result of each homotopy step + std::stringstream iAsAscii; + iAsAscii << i+1; + + if (dim == 2 && dimworld == 2) { + CosseratVTKWriter<GridType>::writeMixed<DeformationFEBasis,OrientationFEBasis>(deformationFEBasis,x[_0], + orientationFEBasis,x[_1], + resultPath + "cosserat_homotopy_" + iAsAscii.str()); + } else if (dim == 2 && dimworld == 3) { #if MIXED_SPACE - CosseratVTKWriter<GridType>::write<DeformationFEBasis>(deformationFEBasis, x[_0], resultPath + "cosserat_homotopy_" + std::to_string(i+1) + "_l" + std::to_string(numLevels)); -#else - CosseratVTKWriter<GridType>::write<DeformationFEBasis>(deformationFEBasis, x, resultPath + "cosserat_homotopy_" + std::to_string(i+1) + "_l" + std::to_string(numLevels)); + CosseratVTKWriter<GridType>::write<DeformationFEBasis>(deformationFEBasis, x[_0], resultPath + "cosserat_homotopy_" + std::to_string(i+1) + "_l" + std::to_string(numLevels)); +#else + CosseratVTKWriter<GridType>::write<DeformationFEBasis>(deformationFEBasis, x, resultPath + "cosserat_homotopy_" + std::to_string(i+1) + "_l" + std::to_string(numLevels)); #endif - } else if (dim == 3 && dimworld == 3) { - - BlockVector<FieldVector<double,dimworld> > displacement(x[_0].size()); - for (size_t i=0; i<x[_0].size(); i++) { - for (int j = 0; j < 3; j ++) - displacement[i][j] = x[_0][i][j] - identityDeformation[i][j]; - } + } else if (dim == 3 && dimworld == 3) { - auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(deformationPowerBasis, displacement); - SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(displacementOrder-1)); - vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dim)); - vtkWriter.write(resultPath + "cosserat_homotopy_" + std::to_string(i+1) + "_l" + std::to_string(numLevels)); - } + BlockVector<FieldVector<double,dimworld> > displacement(x[_0].size()); + for (size_t i=0; i<x[_0].size(); i++) { + for (int j = 0; j < 3; j ++) + displacement[i][j] = x[_0][i][j] - identityDeformation[i][j]; + } + + auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(deformationPowerBasis, displacement); + SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(displacementOrder-1)); + vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dim)); + vtkWriter.write(resultPath + "cosserat_homotopy_" + std::to_string(i+1) + "_l" + std::to_string(numLevels)); } + } - // ////////////////////////////// - // Output result - // ////////////////////////////// + // ////////////////////////////// + // Output result + // ////////////////////////////// #if !MIXED_SPACE - // Write the corresponding coefficient vector: verbatim in binary, to be completely lossless - // This data may be used by other applications measuring the discretization error - BlockVector<TargetSpace::CoordinateType> xEmbedded(compositeBasis.size({0})); - for (size_t i=0; i<compositeBasis.size({0}); i++) - for (size_t j=0; j<TargetSpace::TangentVector::dimension; j++) - xEmbedded[i][j] = j < 3 ? x[_0][i][j] : x[_1][i].globalCoordinates()[j-3]; - - std::ofstream outFile("cosserat-continuum-result-" + std::to_string(numLevels) + ".data", std::ios_base::binary); - MatrixVector::Generic::writeBinary(outFile, xEmbedded); - outFile.close(); + // Write the corresponding coefficient vector: verbatim in binary, to be completely lossless + // This data may be used by other applications measuring the discretization error + BlockVector<TargetSpace::CoordinateType> xEmbedded(compositeBasis.size({0})); + for (size_t i=0; i<compositeBasis.size({0}); i++) + for (size_t j=0; j<TargetSpace::TangentVector::dimension; j++) + xEmbedded[i][j] = j < 3 ? x[_0][i][j] : x[_1][i].globalCoordinates()[j-3]; + + std::ofstream outFile("cosserat-continuum-result-" + std::to_string(numLevels) + ".data", std::ios_base::binary); + MatrixVector::Generic::writeBinary(outFile, xEmbedded); + outFile.close(); #endif - if (parameterSet.get<bool>("computeAverageNeumannDeflection", false) and parameterSet.hasKey("neumannValues")) { + if (parameterSet.get<bool>("computeAverageNeumannDeflection", false) and parameterSet.hasKey("neumannValues")) { // finally: compute the average deformation of the Neumann boundary // That is what we need for the locking tests FieldVector<double,3> averageDef(0); for (size_t i=0; i<x[_0].size(); i++) - if (neumannNodes[i][0]) - averageDef += x[_0][i].globalCoordinates(); + if (neumannNodes[i][0]) + averageDef += x[_0][i].globalCoordinates(); averageDef /= neumannNodes.count(); if (mpiHelper.rank()==0 and parameterSet.hasKey("neumannValues")) @@ -737,9 +741,10 @@ int main (int argc, char *argv[]) try std::cout << "Neumann values = " << parameterSet.get<FieldVector<double, 3> >("neumannValues") << " " << ", average deflection: " << averageDef << std::endl; } - } + } -} catch (Exception& e) +} +catch (Exception& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << std::endl; } diff --git a/src/film-on-substrate-stress-plot.cc b/src/film-on-substrate-stress-plot.cc index a21523f4..77158d11 100644 --- a/src/film-on-substrate-stress-plot.cc +++ b/src/film-on-substrate-stress-plot.cc @@ -60,10 +60,10 @@ int main (int argc, char *argv[]) try //feenableexcept(FE_INVALID); Python::runStream() - << std::endl << "import sys" - << std::endl << "import os" - << std::endl << "sys.path.append(os.getcwd() + '/../../problems/')" - << std::endl; + << std::endl << "import sys" + << std::endl << "import os" + << std::endl << "sys.path.append(os.getcwd() + '/../../problems/')" + << std::endl; // parse data file ParameterTree parameterSet; @@ -103,12 +103,12 @@ int main (int argc, char *argv[]) try auto pythonSurfaceShellVertices = Python::make_function<bool>(Python::evaluate(lambda)); int numLevels = parameterSet.get<int>("numLevels"); - + while (numLevels > 0) { - for (auto&& e : elements(grid->leafGridView())){ + for (auto&& e : elements(grid->leafGridView())) { bool isSurfaceShell = false; for (int i = 0; i < e.geometry().corners(); i++) { - isSurfaceShell = isSurfaceShell || pythonSurfaceShellVertices(e.geometry().corner(i)); + isSurfaceShell = isSurfaceShell || pythonSurfaceShellVertices(e.geometry().corner(i)); } grid->mark(isSurfaceShell ? 1 : 0,e); } @@ -122,8 +122,8 @@ int main (int argc, char *argv[]) try if (grid->leafGridView().comm().size() > 1) DUNE_THROW(Exception, - std::string("To create a stress plot, please use only one process, now there are ") + std::to_string(grid->leafGridView().comm().size()) + std::string(" procsses.")); - + std::string("To create a stress plot, please use only one process, now there are ") + std::to_string(grid->leafGridView().comm().size()) + std::string(" procsses.")); + typedef GridType::LeafGridView GridView; GridView gridView = grid->leafGridView(); @@ -148,14 +148,14 @@ int main (int argc, char *argv[]) try auto basisOrderD = makeBasis( gridView, power<dim>( - lagrange<displacementOrder>() - )); + lagrange<displacementOrder>() + )); auto basisOrderR = makeBasis( gridView, power<dim>( - lagrange<rotationOrder>() - )); + lagrange<rotationOrder>() + )); ///////////////////////////////////////////////////////////// // INITIAL DATA @@ -169,14 +169,14 @@ int main (int argc, char *argv[]) try std::cout << "... done: The basis has " << basisOrderD.size() << " elements and the defomation file has " << deformationMap.size() << " entries." << std::endl; const auto dimRotation = Rotation<double,dim>::embeddedDim; - std::unordered_map<std::string, FieldVector<double,dimRotation>> rotationMap; + std::unordered_map<std::string, FieldVector<double,dimRotation> > rotationMap; if (parameterSet.hasKey("rotationOutput")) { std::cout << "Reading in rotation file (" << "order is " << rotationOrder << "): " << pathToOutput + parameterSet.get<std::string>("rotationOutput") << std::endl; rotationMap = Dune::GFE::transformFileToMap<dimRotation>(pathToOutput + parameterSet.get<std::string>("rotationOutput")); } const bool startFromFile = parameterSet.get<bool>("startFromFile"); - std::unordered_map<std::string, FieldVector<double,dim>> initialDeformationMap; - + std::unordered_map<std::string, FieldVector<double,dim> > initialDeformationMap; + auto gridDeformationLambda = std::string("lambda x: (") + parameterSet.get<std::string>("gridDeformation") + std::string(")"); auto gridDeformation = Python::make_function<FieldVector<double,dim> >(Python::evaluate(gridDeformationLambda)); @@ -192,9 +192,13 @@ int main (int argc, char *argv[]) try xInitial.resize(basisOrderD.size()); DisplacementVector displacement; displacement.resize(basisOrderD.size()); - - Functions::interpolate(basisOrderD, x, [](FieldVector<double,dim> x){ return x; }); - Functions::interpolate(basisOrderD, xInitial, [](FieldVector<double,dim> x){ return x; }); + + Functions::interpolate(basisOrderD, x, [](FieldVector<double,dim> x){ + return x; + }); + Functions::interpolate(basisOrderD, xInitial, [](FieldVector<double,dim> x){ + return x; + }); for (std::size_t i = 0; i < basisOrderD.size(); i++) { std::stringstream stream; @@ -210,14 +214,16 @@ int main (int argc, char *argv[]) try } } - using RotationVector = std::vector<Rotation<double,dim>>; + using RotationVector = std::vector<Rotation<double,dim> >; RotationVector rot; rot.resize(basisOrderR.size()); DisplacementVector xOrderR; xOrderR.resize(basisOrderR.size()); - Functions::interpolate(basisOrderR, xOrderR, [](FieldVector<double,dim> x){ return x; }); - - using DirectorVector = std::vector<Dune::FieldVector<double,dim>>; + Functions::interpolate(basisOrderR, xOrderR, [](FieldVector<double,dim> x){ + return x; + }); + + using DirectorVector = std::vector<Dune::FieldVector<double,dim> >; std::array<DirectorVector,3> rot_director; rot_director[0].resize(basisOrderR.size()); rot_director[1].resize(basisOrderR.size()); @@ -239,42 +245,42 @@ int main (int argc, char *argv[]) try ///////////////////////////////////////////////////////////// int quadOrder = parameterSet.hasKey("quadOrder") ? parameterSet.get<int>("quadOrder") : 4; - auto stressAssembler = GFE::SurfaceCosseratStressAssembler<decltype(basisOrderD),decltype(basisOrderR), FieldVector<double,dim>, Rotation<double,dim>> - (basisOrderD, basisOrderR); - + auto stressAssembler = GFE::SurfaceCosseratStressAssembler<decltype(basisOrderD),decltype(basisOrderR), FieldVector<double,dim>, Rotation<double,dim> > + (basisOrderD, basisOrderR); + std::cout << "Selected energy is: " << parameterSet.get<std::string>("energy") << std::endl; - std::shared_ptr<Elasticity::LocalDensity<dim,ValueType>> elasticDensity; + std::shared_ptr<Elasticity::LocalDensity<dim,ValueType> > elasticDensity; const ParameterTree& materialParameters = parameterSet.sub("materialParameters"); if (parameterSet.get<std::string>("energy") == "stvenantkirchhoff") - elasticDensity = std::make_shared<Elasticity::StVenantKirchhoffDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::StVenantKirchhoffDensity<dim,ValueType> >(materialParameters); if (parameterSet.get<std::string>("energy") == "neohooke") - elasticDensity = std::make_shared<Elasticity::NeoHookeDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::NeoHookeDensity<dim,ValueType> >(materialParameters); if (parameterSet.get<std::string>("energy") == "hencky") - elasticDensity = std::make_shared<Elasticity::HenckyDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::HenckyDensity<dim,ValueType> >(materialParameters); if (parameterSet.get<std::string>("energy") == "exphencky") - elasticDensity = std::make_shared<Elasticity::ExpHenckyDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::ExpHenckyDensity<dim,ValueType> >(materialParameters); if (parameterSet.get<std::string>("energy") == "mooneyrivlin") - elasticDensity = std::make_shared<Elasticity::MooneyRivlinDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::MooneyRivlinDensity<dim,ValueType> >(materialParameters); if(!elasticDensity) - DUNE_THROW(Exception, "Error: Selected energy not available!"); + DUNE_THROW(Exception, "Error: Selected energy not available!"); Python::Reference surfaceShellClass = Python::import(materialParameters.get<std::string>("surfaceShellParameters")); Python::Callable surfaceShellCallable = surfaceShellClass.get("SurfaceShellParameters"); Python::Reference pythonObject = surfaceShellCallable(); auto fLame = Python::make_function<FieldVector<double, 2> >(pythonObject.get("lame")); - std::vector<FieldMatrix<double,dim,dim>> stressSubstrate1stPiolaKirchhoffTensor; - std::vector<FieldMatrix<double,dim,dim>> stressSubstrateCauchyTensor; + std::vector<FieldMatrix<double,dim,dim> > stressSubstrate1stPiolaKirchhoffTensor; + std::vector<FieldMatrix<double,dim,dim> > stressSubstrateCauchyTensor; std::cout << "Assemble stress for the substrate.." << std::endl; - stressAssembler.assembleSubstrateStress<Elasticity::LocalDensity<dim,ValueType>>(x, elasticDensity.get(), quadOrder, stressSubstrate1stPiolaKirchhoffTensor, stressSubstrateCauchyTensor); + stressAssembler.assembleSubstrateStress<Elasticity::LocalDensity<dim,ValueType> >(x, elasticDensity.get(), quadOrder, stressSubstrate1stPiolaKirchhoffTensor, stressSubstrateCauchyTensor); - std::vector<FieldMatrix<double,dim,dim>> stressShellBiotTensor; + std::vector<FieldMatrix<double,dim,dim> > stressShellBiotTensor; std::cout << "Assemble stress for the shell.." << std::endl; - stressAssembler.assembleShellStress(rot, x, xInitial, fLame,/*mu_c*/0, surfaceShellBoundary, quadOrder, stressShellBiotTensor); + stressAssembler.assembleShellStress(rot, x, xInitial, fLame,/*mu_c*/ 0, surfaceShellBoundary, quadOrder, stressShellBiotTensor); std::vector<double> stressSubstrate1stPiolaKirchhoff(stressSubstrate1stPiolaKirchhoffTensor.size()); std::vector<double> stressSubstrateCauchy(stressSubstrate1stPiolaKirchhoffTensor.size()); @@ -296,12 +302,12 @@ int main (int argc, char *argv[]) try stressShellBiot[i] = stressShellBiotTensor[i].frobenius_norm(); } - - auto displacementFunction = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(basisOrderD, displacement); - auto director0Function = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(basisOrderR, rot_director[0]); - auto director1Function = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(basisOrderR, rot_director[1]); - auto director2Function = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(basisOrderR, rot_director[2]); + auto displacementFunction = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(basisOrderD, displacement); + + auto director0Function = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(basisOrderR, rot_director[0]); + auto director1Function = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(basisOrderR, rot_director[1]); + auto director2Function = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(basisOrderR, rot_director[2]); SubsamplingVTKWriter<GridView> vtkWriter(gridView, refinementLevels(displacementOrder-1)); vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dim)); @@ -330,6 +336,7 @@ int main (int argc, char *argv[]) try vtkWriterElementOnly.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dim)); vtkWriterElementOnly.write("stress_plot_" + parameterSet.get<std::string>("energy") + "_element_only"); -} catch (Exception& e) { - std::cout << e.what() << std::endl; +} +catch (Exception& e) { + std::cout << e.what() << std::endl; } diff --git a/src/film-on-substrate.cc b/src/film-on-substrate.cc index 8c7d2bea..016275d8 100644 --- a/src/film-on-substrate.cc +++ b/src/film-on-substrate.cc @@ -121,9 +121,9 @@ int main (int argc, char *argv[]) try //feenableexcept(FE_INVALID); Python::runStream() - << std::endl << "import sys" - << std::endl << "sys.path.append('" << argv[1] << "')" - << std::endl; + << std::endl << "import sys" + << std::endl << "sys.path.append('" << argv[1] << "')" + << std::endl; // parse data file ParameterTree parameterSet; @@ -194,10 +194,10 @@ int main (int argc, char *argv[]) try auto pythonAdaptiveRefinementVertices = Python::make_function<bool>(Python::evaluate(lambda)); while (numLevels > 0) { - for (auto&& e : elements(grid->leafGridView())){ + for (auto&& e : elements(grid->leafGridView())) { bool refineHere = false; for (int i = 0; i < e.geometry().corners(); i++) { - refineHere = refineHere || pythonSurfaceShellVertices(e.geometry().corner(i)) || pythonAdaptiveRefinementVertices(e.geometry().corner(i)); + refineHere = refineHere || pythonSurfaceShellVertices(e.geometry().corner(i)) || pythonAdaptiveRefinementVertices(e.geometry().corner(i)); } grid->mark(refineHere ? 1 : 0, e); } @@ -216,13 +216,13 @@ int main (int argc, char *argv[]) try GridView gridView = grid->leafGridView(); ///////////////////////////////////////////////////////////// - // DATA TYPES + // DATA TYPES ///////////////////////////////////////////////////////////// using namespace Dune::Indices; typedef std::vector<RealTuple<double,dim> > DisplacementVector; - typedef std::vector<Rotation<double,dim>> RotationVector; + typedef std::vector<Rotation<double,dim> > RotationVector; const int dimRotation = Rotation<double,dim>::embeddedDim; typedef TupleVector<DisplacementVector, RotationVector> SolutionType; @@ -236,25 +236,25 @@ int main (int argc, char *argv[]) try composite( power<dim>( lagrange<displacementOrder>() - ), + ), power<dimRotation>( lagrange<rotationOrder>() - ) - )); + ) + )); auto deformationPowerBasis = makeBasis( gridView, power<dim>( - lagrange<displacementOrder>() - )); + lagrange<displacementOrder>() + )); auto orientationPowerBasis = makeBasis( - gridView, + gridView, + power<dim>( power<dim>( - power<dim>( - lagrange<rotationOrder>() - ) - )); + lagrange<rotationOrder>() + ) + )); typedef Dune::Functions::LagrangeBasis<GridView,displacementOrder> DeformationFEBasis; typedef Dune::Functions::LagrangeBasis<GridView,rotationOrder> OrientationFEBasis; @@ -293,12 +293,12 @@ int main (int argc, char *argv[]) try BoundaryPatch<GridView> dirichletBoundaryX(gridView, dirichletVerticesX); BoundaryPatch<GridView> dirichletBoundaryY(gridView, dirichletVerticesY); BoundaryPatch<GridView> dirichletBoundaryZ(gridView, dirichletVerticesZ); - auto neumannBoundary = std::make_shared<BoundaryPatch<GridView>>(gridView, neumannVertices); + auto neumannBoundary = std::make_shared<BoundaryPatch<GridView> >(gridView, neumannVertices); BoundaryPatch<GridView> surfaceShellBoundary(gridView, surfaceShellVertices); std::cout << "On rank " << mpiHelper.rank() << ": Dirichlet boundary has [" << dirichletBoundaryX.numFaces() << - ", " << dirichletBoundaryY.numFaces() << - ", " << dirichletBoundaryZ.numFaces() <<"] faces\n"; + ", " << dirichletBoundaryY.numFaces() << + ", " << dirichletBoundaryZ.numFaces() <<"] faces\n"; std::cout << "On rank " << mpiHelper.rank() << ": Neumann boundary has " << neumannBoundary->numFaces() << " faces\n"; std::cout << "On rank " << mpiHelper.rank() << ": Shell boundary has " << surfaceShellBoundary.numFaces() << " faces\n"; @@ -313,7 +313,7 @@ int main (int argc, char *argv[]) try //Create BitVector matching the tangential space const int dimRotationTangent = Rotation<double,dim>::TangentVector::dimension; - typedef MultiTypeBlockVector<std::vector<FieldVector<double,dim> >, std::vector<FieldVector<double,dimRotationTangent>> > VectorForBit; + typedef MultiTypeBlockVector<std::vector<FieldVector<double,dim> >, std::vector<FieldVector<double,dimRotationTangent> > > VectorForBit; typedef Solvers::DefaultBitVector_t<VectorForBit> BitVector; BitVector dirichletDofs; @@ -325,7 +325,7 @@ int main (int argc, char *argv[]) try dirichletDofs[_0][i][2] = dirichletNodesZ[i][0]; } for (size_t i = 0; i < compositeBasis.size({1}); i++) { - for (int j = 0; j < dimRotationTangent; j++){ + for (int j = 0; j < dimRotationTangent; j++) { dirichletDofs[_1][i][j] = not surfaceShellNodes[i][0]; } } @@ -333,7 +333,7 @@ int main (int argc, char *argv[]) try ///////////////////////////////////////////////////////////// // INITIAL DATA ///////////////////////////////////////////////////////////// - + SolutionType x; x[_0].resize(compositeBasis.size({0})); x[_1].resize(compositeBasis.size({1})); @@ -345,8 +345,10 @@ int main (int argc, char *argv[]) try Dune::Functions::interpolate(deformationPowerBasis, displacement, pythonInitialDeformation); BlockVector<FieldVector<double,dim> > identity(compositeBasis.size({0})); - Dune::Functions::interpolate(deformationPowerBasis, identity, [](FieldVector<double,dim> x){ return x; }); - + Dune::Functions::interpolate(deformationPowerBasis, identity, [](FieldVector<double,dim> x){ + return x; + }); + double max_x = 0; double initial_max_x = 0; for (int i = 0; i < displacement.size(); i++) { @@ -354,12 +356,12 @@ int main (int argc, char *argv[]) try initial_max_x = std::max(x[_0][i][0], initial_max_x); displacement[i] -= identity[i]; //Subtract identity to get the initial displacement as a function } - auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(deformationPowerBasis, displacement); + auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(deformationPowerBasis, displacement); // We need to subsample, because VTK cannot natively display real second-order functions SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(displacementOrder-1)); vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dim)); vtkWriter.write(resultPath + "finite-strain_homotopy_" + parameterSet.get<std::string>("energy") + "_0"); - + ///////////////////////////////////////////////////////////// // STRESS-FREE SURFACE SHELL DATA ///////////////////////////////////////////////////////////// @@ -368,7 +370,7 @@ int main (int argc, char *argv[]) try power<dim>( lagrange<stressFreeDataOrder>(), blockedInterleaved() - )); + )); auto& idSet = grid->globalIdSet(); GlobalIndexSet<GridView> globalVertexIndexSet(gridView,dim); @@ -377,9 +379,9 @@ int main (int argc, char *argv[]) try if (startFromFile) { const std::string pathToGridDeformationFile = parameterSet.get("pathToGridDeformationFile", ""); - std::unordered_map<std::string, FieldVector<double,3>> deformationMap; + std::unordered_map<std::string, FieldVector<double,3> > deformationMap; std::string line, displacement, entry; - if (mpiHelper.rank() == 0) + if (mpiHelper.rank() == 0) std::cout << "Reading in deformation file (" << "order is " << stressFreeDataOrder << "): " << pathToGridDeformationFile + parameterSet.get<std::string>("gridDeformationFile") << std::endl; // Read grid deformation information from the file specified in the parameter set via gridDeformationFile @@ -404,7 +406,9 @@ int main (int argc, char *argv[]) try } else { DUNE_THROW(Exception, "Error: Could not open the file containing the deformation vector!"); } - Dune::Functions::interpolate(stressFreeFEBasis, stressFreeShellVector, [](FieldVector<double,dim> x){ return x; }); + Dune::Functions::interpolate(stressFreeFEBasis, stressFreeShellVector, [](FieldVector<double,dim> x){ + return x; + }); for (auto& entry : stressFreeShellVector) { std::stringstream stream; @@ -418,16 +422,18 @@ int main (int argc, char *argv[]) try Dune::Functions::interpolate(stressFreeFEBasis, stressFreeShellVector, gridDeformation); } - auto stressFreeShellFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(stressFreeFEBasis, stressFreeShellVector); - + auto stressFreeShellFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(stressFreeFEBasis, stressFreeShellVector); + if (parameterSet.hasKey("writeOutStressFreeData") && parameterSet.get<bool>("writeOutStressFreeData")) { BlockVector<FieldVector<double,dim> > stressFreeDisplacement(stressFreeFEBasis.size()); - Dune::Functions::interpolate(stressFreeFEBasis, stressFreeDisplacement, [](FieldVector<double,dim> x){ return (-1.0)*x; }); + Dune::Functions::interpolate(stressFreeFEBasis, stressFreeDisplacement, [](FieldVector<double,dim> x){ + return (-1.0)*x; + }); for (int i = 0; i < stressFreeFEBasis.size(); i++) { stressFreeDisplacement[i] += stressFreeShellVector[i]; } - auto stressFreeDisplacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(stressFreeFEBasis, stressFreeDisplacement); + auto stressFreeDisplacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(stressFreeFEBasis, stressFreeDisplacement); //Write out the stress-free shell function that was read in SubsamplingVTKWriter<GridView> vtkWriterStressFree(gridView, Dune::refinementLevels(1)); vtkWriterStressFree.addVertexData(stressFreeDisplacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dim)); @@ -459,9 +465,9 @@ int main (int argc, char *argv[]) try // //////////////////////////////////////////////////////////// - // A constant vector-valued function, for simple Neumann boundary values - std::shared_ptr<std::function<Dune::FieldVector<double,dim>(Dune::FieldVector<double,dim>)>> neumannFunctionPtr; - neumannFunctionPtr = std::make_shared<std::function<Dune::FieldVector<double,dim>(Dune::FieldVector<double,dim>)>>([&](FieldVector<double,dim> ) { + // A constant vector-valued function, for simple Neumann boundary values + std::shared_ptr<std::function<Dune::FieldVector<double,dim>(Dune::FieldVector<double,dim>)> > neumannFunctionPtr; + neumannFunctionPtr = std::make_shared<std::function<Dune::FieldVector<double,dim>(Dune::FieldVector<double,dim>)> >([&](FieldVector<double,dim> ) { return neumannValues * (-homotopyParameter); }); @@ -479,43 +485,43 @@ int main (int argc, char *argv[]) try // Create the energy functional // ///////////////////////////////////////////////// - std::shared_ptr<Elasticity::LocalDensity<dim,ValueType>> elasticDensity; + std::shared_ptr<Elasticity::LocalDensity<dim,ValueType> > elasticDensity; if (parameterSet.get<std::string>("energy") == "stvenantkirchhoff") - elasticDensity = std::make_shared<Elasticity::StVenantKirchhoffDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::StVenantKirchhoffDensity<dim,ValueType> >(materialParameters); if (parameterSet.get<std::string>("energy") == "neohooke") - elasticDensity = std::make_shared<Elasticity::NeoHookeDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::NeoHookeDensity<dim,ValueType> >(materialParameters); if (parameterSet.get<std::string>("energy") == "hencky") - elasticDensity = std::make_shared<Elasticity::HenckyDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::HenckyDensity<dim,ValueType> >(materialParameters); if (parameterSet.get<std::string>("energy") == "exphencky") - elasticDensity = std::make_shared<Elasticity::ExpHenckyDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::ExpHenckyDensity<dim,ValueType> >(materialParameters); if (parameterSet.get<std::string>("energy") == "mooneyrivlin") - elasticDensity = std::make_shared<Elasticity::MooneyRivlinDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::MooneyRivlinDensity<dim,ValueType> >(materialParameters); if(!elasticDensity) DUNE_THROW(Exception, "Error: Selected energy not available!"); - auto elasticEnergy = std::make_shared<GFE::LocalIntegralEnergy<CompositeBasis, RealTuple<ValueType,targetDim>, Rotation<ValueType,dim>>>(elasticDensity); - auto neumannEnergy = std::make_shared<GFE::NeumannEnergy<CompositeBasis, RealTuple<ValueType,targetDim>, Rotation<ValueType,dim>>>(neumannBoundary,*neumannFunctionPtr); + auto elasticEnergy = std::make_shared<GFE::LocalIntegralEnergy<CompositeBasis, RealTuple<ValueType,targetDim>, Rotation<ValueType,dim> > >(elasticDensity); + auto neumannEnergy = std::make_shared<GFE::NeumannEnergy<CompositeBasis, RealTuple<ValueType,targetDim>, Rotation<ValueType,dim> > >(neumannBoundary,*neumannFunctionPtr); auto surfaceCosseratEnergy = std::make_shared<GFE::SurfaceCosseratEnergy< - decltype(stressFreeShellFunction), CompositeBasis, RealTuple<ValueType,dim>, Rotation<ValueType,dim> >>( - materialParameters, - &surfaceShellBoundary, - stressFreeShellFunction, - fThickness, - fLame); - - GFE::SumEnergy<CompositeBasis, RealTuple<ValueType,targetDim>, Rotation<ValueType,targetDim>> sumEnergy; + decltype(stressFreeShellFunction), CompositeBasis, RealTuple<ValueType,dim>, Rotation<ValueType,dim> > >( + materialParameters, + &surfaceShellBoundary, + stressFreeShellFunction, + fThickness, + fLame); + + GFE::SumEnergy<CompositeBasis, RealTuple<ValueType,targetDim>, Rotation<ValueType,targetDim> > sumEnergy; sumEnergy.addLocalEnergy(neumannEnergy); sumEnergy.addLocalEnergy(elasticEnergy); sumEnergy.addLocalEnergy(surfaceCosseratEnergy); MixedLocalGFEADOLCStiffness<CompositeBasis, - RealTuple<double,dim>, - Rotation<double,dim> > localGFEADOLCStiffness(&sumEnergy); + RealTuple<double,dim>, + Rotation<double,dim> > localGFEADOLCStiffness(&sumEnergy); MixedGFEAssembler<CompositeBasis, - RealTuple<double,dim>, - Rotation<double,dim> > mixedAssembler(compositeBasis, &localGFEADOLCStiffness); + RealTuple<double,dim>, + Rotation<double,dim> > mixedAssembler(compositeBasis, &localGFEADOLCStiffness); //////////////////////////////////////////////////////// // Set Dirichlet values @@ -573,7 +579,7 @@ int main (int argc, char *argv[]) try for (int j = dim; j < RBM::TangentVector::dimension; j ++) dirichletDofsRBM[i][j] = dirichletDofs[_1][i][j-dim]; } - typedef Dune::GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, RBM, RealTuple<double, dim>, Rotation<double,dim>> GFEAssemblerWrapper; + typedef Dune::GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, RBM, RealTuple<double, dim>, Rotation<double,dim> > GFEAssemblerWrapper; GFEAssemblerWrapper assembler(&mixedAssembler, deformationFEBasis); #endif @@ -583,7 +589,7 @@ int main (int argc, char *argv[]) try if (parameterSet.get<std::string>("solvertype", "trustRegion") == "trustRegion") { #if MIXED_SPACE - MixedRiemannianTrustRegionSolver<GridType, CompositeBasis, DeformationFEBasis, RealTuple<double,dim>, OrientationFEBasis, Rotation<double,dim>> solver; + MixedRiemannianTrustRegionSolver<GridType, CompositeBasis, DeformationFEBasis, RealTuple<double,dim>, OrientationFEBasis, Rotation<double,dim> > solver; solver.setup(*grid, &mixedAssembler, deformationFEBasis, @@ -633,7 +639,7 @@ int main (int argc, char *argv[]) try } else { //parameterSet.get<std::string>("solvertype") == "proximalNewton" #if MIXED_SPACE - DUNE_THROW(Exception, "Error: There is no MixedRiemannianProximalNewtonSolver!"); + DUNE_THROW(Exception, "Error: There is no MixedRiemannianProximalNewtonSolver!"); #else RiemannianProximalNewtonSolver<DeformationFEBasis, RBM, GFEAssemblerWrapper> solver; solver.setup(*grid, @@ -663,12 +669,12 @@ int main (int argc, char *argv[]) try // Compute the displacement for (int i = 0; i < compositeBasis.size({0}); i++) { - for (int j = 0; j < dim; j++) { + for (int j = 0; j < dim; j++) { displacement[i][j] = x[_0][i][j]; } displacement[i] -= identity[i]; } - auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(deformationPowerBasis, displacement); + auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(deformationPowerBasis, displacement); // We need to subsample, because VTK cannot natively display real second-order functions SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(displacementOrder-1)); @@ -688,27 +694,29 @@ int main (int argc, char *argv[]) try std::string pathToOutput = parameterSet.hasKey("pathToOutput") ? parameterSet.get<std::string>("pathToOutput") : "./"; std::string deformationOutput = parameterSet.hasKey("deformationOutput") ? parameterSet.get<std::string>("deformationOutput") : "deformation"; std::string rotationOutput = parameterSet.hasKey("rotationOutput") ? parameterSet.get<std::string>("rotationOutput") : "rotation"; - + deformationOutput = pathToOutput + deformationOutput; rotationOutput = pathToOutput + rotationOutput; file.open(deformationOutput + ending); - for (int i = 0; i < identity.size(); i++){ + for (int i = 0; i < identity.size(); i++) { file << identity[i] << ":" << displacement[i] << "\n"; } file.close(); - + BlockVector<FieldVector<double,dim> > identityRotation(orientationFEBasis.size()); auto identityRotationPowerBasis = makeBasis( gridView, power<dim>( - lagrange<rotationOrder>() - )); - Dune::Functions::interpolate(identityRotationPowerBasis, identityRotation, [](FieldVector<double,dim> x){ return x; }); + lagrange<rotationOrder>() + )); + Dune::Functions::interpolate(identityRotationPowerBasis, identityRotation, [](FieldVector<double,dim> x){ + return x; + }); file.open(rotationOutput + ending); - for (int i = 0; i < identityRotation.size(); i++){ + for (int i = 0; i < identityRotation.size(); i++) { file << identityRotation[i] << ":" << x[_1][i] << "\n"; } @@ -740,6 +748,7 @@ int main (int argc, char *argv[]) try file.close(); } -} catch (Exception& e) { - std::cout << e.what() << std::endl; +} +catch (Exception& e) { + std::cout << e.what() << std::endl; } diff --git a/src/gradient-flow.cc b/src/gradient-flow.cc index 6ffe0055..3979f517 100644 --- a/src/gradient-flow.cc +++ b/src/gradient-flow.cc @@ -77,9 +77,9 @@ int main (int argc, char *argv[]) try //feenableexcept(FE_INVALID); Python::runStream() - << std::endl << "import sys" - << std::endl << "sys.path.append('../../problems/')" - << std::endl; + << std::endl << "import sys" + << std::endl << "sys.path.append('../../problems/')" + << std::endl; typedef std::vector<TargetSpace> SolutionType; @@ -184,7 +184,7 @@ int main (int argc, char *argv[]) try power<TargetSpace::CoordinateType::dimension>( lagrange<order>(), blockedInterleaved() - )); + )); Functions::interpolate(powerBasis, v, pythonInitialIterate); diff --git a/src/harmonicmaps.cc b/src/harmonicmaps.cc index 8a45a3a8..4033c842 100644 --- a/src/harmonicmaps.cc +++ b/src/harmonicmaps.cc @@ -119,234 +119,234 @@ void fillVTKWriter(Writer& vtkWriter, const Basis& feBasis, const SolutionType& int main (int argc, char *argv[]) { - MPIHelper::instance(argc, argv); - - //feenableexcept(FE_INVALID); - // Start Python interpreter - Python::start(); - Python::Reference main = Python::import("__main__"); - Python::run("import math"); - - //feenableexcept(FE_INVALID); - Python::runStream() - << std::endl << "import sys" - << std::endl << "import os" - << std::endl << "sys.path.append(os.getcwd() + '/../../problems/')" - << std::endl; - - - typedef std::vector<TargetSpace> SolutionType; - - // parse data file - ParameterTree parameterSet; - if (argc < 2) - DUNE_THROW(Exception, "Usage: ./harmonicmaps <parameter file>"); - - ParameterTreeParser::readINITree(argv[1], parameterSet); - - ParameterTreeParser::readOptions(argc, argv, parameterSet); - - // read problem settings - const int numLevels = parameterSet.get<int>("numLevels"); - - // read solver settings - const double tolerance = parameterSet.get<double>("tolerance"); - const int maxTrustRegionSteps = parameterSet.get<int>("maxTrustRegionSteps"); - const double initialTrustRegionRadius = parameterSet.get<double>("initialTrustRegionRadius"); - const int multigridIterations = parameterSet.get<int>("numIt"); - const int nu1 = parameterSet.get<int>("nu1"); - const int nu2 = parameterSet.get<int>("nu2"); - const int mu = parameterSet.get<int>("mu"); - const int baseIterations = parameterSet.get<int>("baseIt"); - const double mgTolerance = parameterSet.get<double>("mgTolerance"); - const double baseTolerance = parameterSet.get<double>("baseTolerance"); - std::string resultPath = parameterSet.get("resultPath", ""); - - // /////////////////////////////////////// - // Create the grid - // /////////////////////////////////////// + MPIHelper::instance(argc, argv); + + //feenableexcept(FE_INVALID); + // Start Python interpreter + Python::start(); + Python::Reference main = Python::import("__main__"); + Python::run("import math"); + + //feenableexcept(FE_INVALID); + Python::runStream() + << std::endl << "import sys" + << std::endl << "import os" + << std::endl << "sys.path.append(os.getcwd() + '/../../problems/')" + << std::endl; + + + typedef std::vector<TargetSpace> SolutionType; + + // parse data file + ParameterTree parameterSet; + if (argc < 2) + DUNE_THROW(Exception, "Usage: ./harmonicmaps <parameter file>"); + + ParameterTreeParser::readINITree(argv[1], parameterSet); + + ParameterTreeParser::readOptions(argc, argv, parameterSet); + + // read problem settings + const int numLevels = parameterSet.get<int>("numLevels"); + + // read solver settings + const double tolerance = parameterSet.get<double>("tolerance"); + const int maxTrustRegionSteps = parameterSet.get<int>("maxTrustRegionSteps"); + const double initialTrustRegionRadius = parameterSet.get<double>("initialTrustRegionRadius"); + const int multigridIterations = parameterSet.get<int>("numIt"); + const int nu1 = parameterSet.get<int>("nu1"); + const int nu2 = parameterSet.get<int>("nu2"); + const int mu = parameterSet.get<int>("mu"); + const int baseIterations = parameterSet.get<int>("baseIt"); + const double mgTolerance = parameterSet.get<double>("mgTolerance"); + const double baseTolerance = parameterSet.get<double>("baseTolerance"); + std::string resultPath = parameterSet.get("resultPath", ""); + + // /////////////////////////////////////// + // Create the grid + // /////////////////////////////////////// #if HAVE_DUNE_FOAMGRID - typedef std::conditional<dim==1 or dim!=dimworld,FoamGrid<dim,dimworld>,UGGrid<dim> >::type GridType; + typedef std::conditional<dim==1 or dim!=dimworld,FoamGrid<dim,dimworld>,UGGrid<dim> >::type GridType; #else - static_assert(dim==dimworld, "You need to have dune-foamgrid installed for dim != dimworld!"); - typedef std::conditional<dim==1,OneDGrid,UGGrid<dim> >::type GridType; + static_assert(dim==dimworld, "You need to have dune-foamgrid installed for dim != dimworld!"); + typedef std::conditional<dim==1,OneDGrid,UGGrid<dim> >::type GridType; #endif - std::shared_ptr<GridType> grid; - FieldVector<double,dimworld> lower(0), upper(1); - std::array<unsigned int,dim> elements; + std::shared_ptr<GridType> grid; + FieldVector<double,dimworld> lower(0), upper(1); + std::array<unsigned int,dim> elements; - std::string structuredGridType = parameterSet["structuredGrid"]; - if (structuredGridType != "false" ) { + std::string structuredGridType = parameterSet["structuredGrid"]; + if (structuredGridType != "false" ) { - lower = parameterSet.get<FieldVector<double,dimworld> >("lower"); - upper = parameterSet.get<FieldVector<double,dimworld> >("upper"); + lower = parameterSet.get<FieldVector<double,dimworld> >("lower"); + upper = parameterSet.get<FieldVector<double,dimworld> >("upper"); - elements = parameterSet.get<std::array<unsigned int,dim> >("elements"); - if (structuredGridType == "simplex") - grid = StructuredGridFactory<GridType>::createSimplexGrid(lower, upper, elements); - else if (structuredGridType == "cube") - grid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements); - else - DUNE_THROW(Exception, "Unknown structured grid type '" << structuredGridType << "' found!"); + elements = parameterSet.get<std::array<unsigned int,dim> >("elements"); + if (structuredGridType == "simplex") + grid = StructuredGridFactory<GridType>::createSimplexGrid(lower, upper, elements); + else if (structuredGridType == "cube") + grid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements); + else + DUNE_THROW(Exception, "Unknown structured grid type '" << structuredGridType << "' found!"); - } else { + } else { - std::string path = parameterSet.get<std::string>("path"); - std::string gridFile = parameterSet.get<std::string>("gridFile"); + std::string path = parameterSet.get<std::string>("path"); + std::string gridFile = parameterSet.get<std::string>("gridFile"); - grid = std::shared_ptr<GridType>(GmshReader<GridType>::read(path + "/" + gridFile)); - } + grid = std::shared_ptr<GridType>(GmshReader<GridType>::read(path + "/" + gridFile)); + } - grid->globalRefine(numLevels-1); + grid->globalRefine(numLevels-1); - ////////////////////////////////////////////////////////////////////////////////// - // Construct the scalar function space basis corresponding to the GFE space - ////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + // Construct the scalar function space basis corresponding to the GFE space + ////////////////////////////////////////////////////////////////////////////////// - using GridView = GridType::LeafGridView; - GridView gridView = grid->leafGridView(); + using GridView = GridType::LeafGridView; + GridView gridView = grid->leafGridView(); - typedef Dune::Functions::LagrangeBasis<GridView, order> FEBasis; - FEBasis feBasis(gridView); - SolutionType x(feBasis.size()); + typedef Dune::Functions::LagrangeBasis<GridView, order> FEBasis; + FEBasis feBasis(gridView); + SolutionType x(feBasis.size()); - // ///////////////////////////////////////// - // Read Dirichlet values - // ///////////////////////////////////////// - BitSetVector<1> dirichletVertices(gridView.size(dim), false); - const GridView::IndexSet& indexSet = gridView.indexSet(); + // ///////////////////////////////////////// + // Read Dirichlet values + // ///////////////////////////////////////// + BitSetVector<1> dirichletVertices(gridView.size(dim), false); + const GridView::IndexSet& indexSet = gridView.indexSet(); - // Make Python function that computes which vertices are on the Dirichlet boundary, - // based on the vertex positions. - std::string lambda = std::string("lambda x: (") + parameterSet.get<std::string>("dirichletVerticesPredicate") + std::string(")"); - auto pythonDirichletVertices = Python::make_function<bool>(Python::evaluate(lambda)); + // Make Python function that computes which vertices are on the Dirichlet boundary, + // based on the vertex positions. + std::string lambda = std::string("lambda x: (") + parameterSet.get<std::string>("dirichletVerticesPredicate") + std::string(")"); + auto pythonDirichletVertices = Python::make_function<bool>(Python::evaluate(lambda)); - for (auto&& vertex : vertices(gridView)) - { - bool isDirichlet = pythonDirichletVertices(vertex.geometry().corner(0)); - dirichletVertices[indexSet.index(vertex)] = isDirichlet; - } + for (auto&& vertex : vertices(gridView)) + { + bool isDirichlet = pythonDirichletVertices(vertex.geometry().corner(0)); + dirichletVertices[indexSet.index(vertex)] = isDirichlet; + } - BoundaryPatch<GridView> dirichletBoundary(gridView, dirichletVertices); + BoundaryPatch<GridView> dirichletBoundary(gridView, dirichletVertices); - BitSetVector<blocksize> dirichletNodes(feBasis.size(), false); + BitSetVector<blocksize> dirichletNodes(feBasis.size(), false); - constructBoundaryDofs(dirichletBoundary,feBasis,dirichletNodes); + constructBoundaryDofs(dirichletBoundary,feBasis,dirichletNodes); - // ////////////////////////// - // Initial iterate - // ////////////////////////// + // ////////////////////////// + // Initial iterate + // ////////////////////////// - // Read initial iterate into a Python function - Python::Module module = Python::import(parameterSet.get<std::string>("initialIterate")); - auto pythonInitialIterate = Python::makeFunction<TargetSpace::CoordinateType(const FieldVector<double,dimworld>&)>(module.get("f")); + // Read initial iterate into a Python function + Python::Module module = Python::import(parameterSet.get<std::string>("initialIterate")); + auto pythonInitialIterate = Python::makeFunction<TargetSpace::CoordinateType(const FieldVector<double,dimworld>&)>(module.get("f")); - std::vector<TargetSpace::CoordinateType> v; - using namespace Functions::BasisFactory; + std::vector<TargetSpace::CoordinateType> v; + using namespace Functions::BasisFactory; - auto powerBasis = makeBasis( - gridView, - power<TargetSpace::CoordinateType::dimension>( - lagrange<order>(), - blockedInterleaved() + auto powerBasis = makeBasis( + gridView, + power<TargetSpace::CoordinateType::dimension>( + lagrange<order>(), + blockedInterleaved() )); - Dune::Functions::interpolate(powerBasis, v, pythonInitialIterate); + Dune::Functions::interpolate(powerBasis, v, pythonInitialIterate); - for (size_t i=0; i<x.size(); i++) - x[i] = v[i]; + for (size_t i=0; i<x.size(); i++) + x[i] = v[i]; - // backup for error measurement later - SolutionType initialIterate = x; + // backup for error measurement later + SolutionType initialIterate = x; - // //////////////////////////////////////////////////////////// - // Create an assembler for the Harmonic Energy Functional - // //////////////////////////////////////////////////////////// + // //////////////////////////////////////////////////////////// + // Create an assembler for the Harmonic Energy Functional + // //////////////////////////////////////////////////////////// - typedef TargetSpace::rebind<adouble>::other ATargetSpace; - using GeodesicInterpolationRule = LocalGeodesicFEFunction<dim, double, FEBasis::LocalView::Tree::FiniteElement, ATargetSpace>; - using ProjectedInterpolationRule = GFE::LocalProjectedFEFunction<dim, double, FEBasis::LocalView::Tree::FiniteElement, ATargetSpace>; + typedef TargetSpace::rebind<adouble>::other ATargetSpace; + using GeodesicInterpolationRule = LocalGeodesicFEFunction<dim, double, FEBasis::LocalView::Tree::FiniteElement, ATargetSpace>; + using ProjectedInterpolationRule = GFE::LocalProjectedFEFunction<dim, double, FEBasis::LocalView::Tree::FiniteElement, ATargetSpace>; - // Assembler using ADOL-C - std::shared_ptr<GFE::LocalEnergy<FEBasis,ATargetSpace> > localEnergy; + // Assembler using ADOL-C + std::shared_ptr<GFE::LocalEnergy<FEBasis,ATargetSpace> > localEnergy; - std::string energy = parameterSet.get<std::string>("energy"); - if (energy == "harmonic") - { - if (parameterSet["interpolationMethod"] == "geodesic") - localEnergy.reset(new HarmonicEnergy<FEBasis, GeodesicInterpolationRule, ATargetSpace>); - else if (parameterSet["interpolationMethod"] == "projected") - localEnergy.reset(new HarmonicEnergy<FEBasis, ProjectedInterpolationRule, ATargetSpace>); - else - DUNE_THROW(Exception, "Unknown interpolation method " << parameterSet["interpolationMethod"] << " requested!"); - - } else if (energy == "chiral_skyrmion") - { -// // Doesn't work: we are not inside of a template -// if constexpr (std::is_same<TargetSpace, UnitVector<double,3> >::value) -// { - if (parameterSet["interpolationMethod"] == "geodesic") - localEnergy.reset(new GFE::ChiralSkyrmionEnergy<FEBasis, GeodesicInterpolationRule, adouble>(parameterSet.sub("energyParameters"))); - else if (parameterSet["interpolationMethod"] == "projected") - localEnergy.reset(new GFE::ChiralSkyrmionEnergy<FEBasis, ProjectedInterpolationRule, adouble>(parameterSet.sub("energyParameters"))); - else - DUNE_THROW(Exception, "Unknown interpolation method " << parameterSet["interpolationMethod"] << " requested!"); -// } else -// DUNE_THROW(Exception, "Build program with TargetSpace = UnitVector<3> for the ChiralSkyrmion energy!"); - - } else - DUNE_THROW(Exception, "Unknown energy type '" << energy << "'"); - - LocalGeodesicFEADOLCStiffness<FEBasis,TargetSpace> localGFEADOLCStiffness(localEnergy.get()); - - GeodesicFEAssembler<FEBasis,TargetSpace> assembler(feBasis, localGFEADOLCStiffness); - - // ///////////////////////////////////////////////// - // Create a Riemannian trust-region solver - // ///////////////////////////////////////////////// - - RiemannianTrustRegionSolver<FEBasis,TargetSpace> solver; - solver.setup(*grid, - &assembler, - x, - dirichletNodes, - tolerance, - maxTrustRegionSteps, - initialTrustRegionRadius, - multigridIterations, - mgTolerance, - mu, nu1, nu2, - baseIterations, - baseTolerance, - false); // instrumented - - // ///////////////////////////////////////////////////// - // Solve! - // ///////////////////////////////////////////////////// - - solver.setInitialIterate(x); - solver.solve(); - - x = solver.getSol(); - - // ////////////////////////////// - // Output result - // ////////////////////////////// - - SubsamplingVTKWriter<GridView> vtkWriter(gridView,Dune::refinementLevels(order-1)); - std::string baseName = "harmonicmaps-result-" + std::to_string(order) + "-" + std::to_string(numLevels); - fillVTKWriter(vtkWriter, feBasis, x, resultPath + baseName); - - // Write the corresponding coefficient vector: verbatim in binary, to be completely lossless - typedef BlockVector<TargetSpace::CoordinateType> EmbeddedVectorType; - EmbeddedVectorType xEmbedded(x.size()); - for (size_t i=0; i<x.size(); i++) - xEmbedded[i] = x[i].globalCoordinates(); + std::string energy = parameterSet.get<std::string>("energy"); + if (energy == "harmonic") + { + if (parameterSet["interpolationMethod"] == "geodesic") + localEnergy.reset(new HarmonicEnergy<FEBasis, GeodesicInterpolationRule, ATargetSpace>); + else if (parameterSet["interpolationMethod"] == "projected") + localEnergy.reset(new HarmonicEnergy<FEBasis, ProjectedInterpolationRule, ATargetSpace>); + else + DUNE_THROW(Exception, "Unknown interpolation method " << parameterSet["interpolationMethod"] << " requested!"); + + } else if (energy == "chiral_skyrmion") + { + // // Doesn't work: we are not inside of a template + // if constexpr (std::is_same<TargetSpace, UnitVector<double,3> >::value) + // { + if (parameterSet["interpolationMethod"] == "geodesic") + localEnergy.reset(new GFE::ChiralSkyrmionEnergy<FEBasis, GeodesicInterpolationRule, adouble>(parameterSet.sub("energyParameters"))); + else if (parameterSet["interpolationMethod"] == "projected") + localEnergy.reset(new GFE::ChiralSkyrmionEnergy<FEBasis, ProjectedInterpolationRule, adouble>(parameterSet.sub("energyParameters"))); + else + DUNE_THROW(Exception, "Unknown interpolation method " << parameterSet["interpolationMethod"] << " requested!"); + // } else + // DUNE_THROW(Exception, "Build program with TargetSpace = UnitVector<3> for the ChiralSkyrmion energy!"); + + } else + DUNE_THROW(Exception, "Unknown energy type '" << energy << "'"); + + LocalGeodesicFEADOLCStiffness<FEBasis,TargetSpace> localGFEADOLCStiffness(localEnergy.get()); + + GeodesicFEAssembler<FEBasis,TargetSpace> assembler(feBasis, localGFEADOLCStiffness); + + // ///////////////////////////////////////////////// + // Create a Riemannian trust-region solver + // ///////////////////////////////////////////////// + + RiemannianTrustRegionSolver<FEBasis,TargetSpace> solver; + solver.setup(*grid, + &assembler, + x, + dirichletNodes, + tolerance, + maxTrustRegionSteps, + initialTrustRegionRadius, + multigridIterations, + mgTolerance, + mu, nu1, nu2, + baseIterations, + baseTolerance, + false); // instrumented + + // ///////////////////////////////////////////////////// + // Solve! + // ///////////////////////////////////////////////////// + + solver.setInitialIterate(x); + solver.solve(); + + x = solver.getSol(); + + // ////////////////////////////// + // Output result + // ////////////////////////////// + + SubsamplingVTKWriter<GridView> vtkWriter(gridView,Dune::refinementLevels(order-1)); + std::string baseName = "harmonicmaps-result-" + std::to_string(order) + "-" + std::to_string(numLevels); + fillVTKWriter(vtkWriter, feBasis, x, resultPath + baseName); + + // Write the corresponding coefficient vector: verbatim in binary, to be completely lossless + typedef BlockVector<TargetSpace::CoordinateType> EmbeddedVectorType; + EmbeddedVectorType xEmbedded(x.size()); + for (size_t i=0; i<x.size(); i++) + xEmbedded[i] = x[i].globalCoordinates(); - std::ofstream outFile(baseName + ".data", std::ios_base::binary); - MatrixVector::Generic::writeBinary(outFile, xEmbedded); - outFile.close(); + std::ofstream outFile(baseName + ".data", std::ios_base::binary); + MatrixVector::Generic::writeBinary(outFile, xEmbedded); + outFile.close(); - return 0; - } + return 0; +} diff --git a/src/rod3d.cc b/src/rod3d.cc index f1009140..d934c906 100644 --- a/src/rod3d.cc +++ b/src/rod3d.cc @@ -52,241 +52,246 @@ using namespace Dune; int main (int argc, char *argv[]) try { - MPIHelper::instance(argc, argv); - - // parse data file - ParameterTree parameterSet; - if (argc < 2) - DUNE_THROW(Exception, "Usage: ./rod3d <parameter file>"); - - ParameterTreeParser::readINITree(argv[1], parameterSet); - - ParameterTreeParser::readOptions(argc, argv, parameterSet); - - // read solver settings - const int numLevels = parameterSet.get<int>("numLevels"); - const double tolerance = parameterSet.get<double>("tolerance"); - const int maxTrustRegionSteps = parameterSet.get<int>("maxTrustRegionSteps"); - const double initialTrustRegionRadius = parameterSet.get<double>("initialTrustRegionRadius"); - const int multigridIterations = parameterSet.get<int>("numIt"); - const int nu1 = parameterSet.get<int>("nu1"); - const int nu2 = parameterSet.get<int>("nu2"); - const int mu = parameterSet.get<int>("mu"); - const int baseIterations = parameterSet.get<int>("baseIt"); - const double mgTolerance = parameterSet.get<double>("mgTolerance"); - const double baseTolerance = parameterSet.get<double>("baseTolerance"); - const bool instrumented = parameterSet.get<bool>("instrumented"); - std::string resultPath = parameterSet.get("resultPath", ""); - - // read rod parameter settings - const double A = parameterSet.get<double>("A"); - const double J1 = parameterSet.get<double>("J1"); - const double J2 = parameterSet.get<double>("J2"); - const double E = parameterSet.get<double>("E"); - const double nu = parameterSet.get<double>("nu"); - const int numRodBaseElements = parameterSet.get<int>("numRodBaseElements"); - - // /////////////////////////////////////// - // Create the grid - // /////////////////////////////////////// - typedef OneDGrid GridType; - GridType grid(numRodBaseElements, 0, 1); - - grid.globalRefine(numLevels-1); - - using GridView = GridType::LeafGridView; - GridView gridView = grid.leafGridView(); - - ////////////////////////////////////////////// - // Create the stress-free configuration - ////////////////////////////////////////////// - - using ScalarBasis = Functions::LagrangeBasis<GridView,order>; - ScalarBasis scalarBasis(gridView); - - std::vector<double> referenceConfigurationX(scalarBasis.size()); - - auto identity = [](const FieldVector<double,1>& x) { return x; }; - - Functions::interpolate(scalarBasis, referenceConfigurationX, identity); - - using Configuration = std::vector<RigidBodyMotion<double,3> >; - Configuration referenceConfiguration(scalarBasis.size()); - - for (std::size_t i=0; i<referenceConfiguration.size(); i++) + MPIHelper::instance(argc, argv); + + // parse data file + ParameterTree parameterSet; + if (argc < 2) + DUNE_THROW(Exception, "Usage: ./rod3d <parameter file>"); + + ParameterTreeParser::readINITree(argv[1], parameterSet); + + ParameterTreeParser::readOptions(argc, argv, parameterSet); + + // read solver settings + const int numLevels = parameterSet.get<int>("numLevels"); + const double tolerance = parameterSet.get<double>("tolerance"); + const int maxTrustRegionSteps = parameterSet.get<int>("maxTrustRegionSteps"); + const double initialTrustRegionRadius = parameterSet.get<double>("initialTrustRegionRadius"); + const int multigridIterations = parameterSet.get<int>("numIt"); + const int nu1 = parameterSet.get<int>("nu1"); + const int nu2 = parameterSet.get<int>("nu2"); + const int mu = parameterSet.get<int>("mu"); + const int baseIterations = parameterSet.get<int>("baseIt"); + const double mgTolerance = parameterSet.get<double>("mgTolerance"); + const double baseTolerance = parameterSet.get<double>("baseTolerance"); + const bool instrumented = parameterSet.get<bool>("instrumented"); + std::string resultPath = parameterSet.get("resultPath", ""); + + // read rod parameter settings + const double A = parameterSet.get<double>("A"); + const double J1 = parameterSet.get<double>("J1"); + const double J2 = parameterSet.get<double>("J2"); + const double E = parameterSet.get<double>("E"); + const double nu = parameterSet.get<double>("nu"); + const int numRodBaseElements = parameterSet.get<int>("numRodBaseElements"); + + // /////////////////////////////////////// + // Create the grid + // /////////////////////////////////////// + typedef OneDGrid GridType; + GridType grid(numRodBaseElements, 0, 1); + + grid.globalRefine(numLevels-1); + + using GridView = GridType::LeafGridView; + GridView gridView = grid.leafGridView(); + + ////////////////////////////////////////////// + // Create the stress-free configuration + ////////////////////////////////////////////// + + using ScalarBasis = Functions::LagrangeBasis<GridView,order>; + ScalarBasis scalarBasis(gridView); + + std::vector<double> referenceConfigurationX(scalarBasis.size()); + + auto identity = [](const FieldVector<double,1>& x) { + return x; + }; + + Functions::interpolate(scalarBasis, referenceConfigurationX, identity); + + using Configuration = std::vector<RigidBodyMotion<double,3> >; + Configuration referenceConfiguration(scalarBasis.size()); + + for (std::size_t i=0; i<referenceConfiguration.size(); i++) + { + referenceConfiguration[i].r[0] = 0; + referenceConfiguration[i].r[1] = 0; + referenceConfiguration[i].r[2] = referenceConfigurationX[i]; + referenceConfiguration[i].q = Rotation<double,3>::identity(); + } + + // Select the reference configuration as initial iterate + + Configuration x = referenceConfiguration; + + // ///////////////////////////////////////// + // Read Dirichlet values + // ///////////////////////////////////////// + + // A basis for the tangent space + using namespace Functions::BasisFactory; + + auto tangentBasis = makeBasis( + gridView, + power<TargetSpace::TangentVector::dimension>( + lagrange<order>(), + blockedInterleaved() + )); + + // Find all boundary dofs + BoundaryPatch<GridView> dirichletBoundary(gridView, + true); // true: The entire boundary is Dirichlet boundary + BitSetVector<TargetSpace::TangentVector::dimension> dirichletNodes(tangentBasis.size(), false); + constructBoundaryDofs(dirichletBoundary,tangentBasis,dirichletNodes); + + // Find the dof on the right boundary + std::size_t rightBoundaryDof; + for (std::size_t i=0; i<referenceConfigurationX.size(); i++) + if (std::fabs(referenceConfigurationX[i] - 1.0) < 1e-6) { - referenceConfiguration[i].r[0] = 0; - referenceConfiguration[i].r[1] = 0; - referenceConfiguration[i].r[2] = referenceConfigurationX[i]; - referenceConfiguration[i].q = Rotation<double,3>::identity(); + rightBoundaryDof = i; + break; } - // Select the reference configuration as initial iterate - - Configuration x = referenceConfiguration; - - // ///////////////////////////////////////// - // Read Dirichlet values - // ///////////////////////////////////////// - - // A basis for the tangent space - using namespace Functions::BasisFactory; - - auto tangentBasis = makeBasis( - gridView, - power<TargetSpace::TangentVector::dimension>( - lagrange<order>(), - blockedInterleaved() - )); - - // Find all boundary dofs - BoundaryPatch<GridView> dirichletBoundary(gridView, - true); // true: The entire boundary is Dirichlet boundary - BitSetVector<TargetSpace::TangentVector::dimension> dirichletNodes(tangentBasis.size(), false); - constructBoundaryDofs(dirichletBoundary,tangentBasis,dirichletNodes); - - // Find the dof on the right boundary - std::size_t rightBoundaryDof; - for (std::size_t i=0; i<referenceConfigurationX.size(); i++) - if (std::fabs(referenceConfigurationX[i] - 1.0) < 1e-6) - { - rightBoundaryDof = i; - break; - } - - // Set Dirichlet values - x[rightBoundaryDof].r = parameterSet.get<FieldVector<double,3> >("dirichletValue"); - - auto axis = parameterSet.get<FieldVector<double,3> >("dirichletAxis"); - double angle = parameterSet.get<double>("dirichletAngle"); - - x[rightBoundaryDof].q = Rotation<double,3>(axis, M_PI*angle/180); - - // backup for error measurement later - std::cout << "Right boundary orientation:" << std::endl; - std::cout << "director 0: " << x[rightBoundaryDof].q.director(0) << std::endl; - std::cout << "director 1: " << x[rightBoundaryDof].q.director(1) << std::endl; - std::cout << "director 2: " << x[rightBoundaryDof].q.director(2) << std::endl; - - ////////////////////////////////////////////// - // Create the energy and assembler - ////////////////////////////////////////////// - - using ATargetSpace = TargetSpace::rebind<adouble>::other; - using GeodesicInterpolationRule = LocalGeodesicFEFunction<1, double, ScalarBasis::LocalView::Tree::FiniteElement, ATargetSpace>; - using ProjectedInterpolationRule = GFE::LocalProjectedFEFunction<1, double, ScalarBasis::LocalView::Tree::FiniteElement, ATargetSpace>; - - // Assembler using ADOL-C - std::shared_ptr<GFE::LocalEnergy<ScalarBasis,ATargetSpace> > localRodEnergy; - - if (parameterSet["interpolationMethod"] == "geodesic") - { - auto energy = std::make_shared<GFE::CosseratRodEnergy<ScalarBasis, GeodesicInterpolationRule, adouble> >(gridView, - A, J1, J2, E, nu); - energy->setReferenceConfiguration(referenceConfiguration); - localRodEnergy = energy; - } - else if (parameterSet["interpolationMethod"] == "projected") - { - auto energy = std::make_shared<GFE::CosseratRodEnergy<ScalarBasis, ProjectedInterpolationRule, adouble> >(gridView, - A, J1, J2, E, nu); - energy->setReferenceConfiguration(referenceConfiguration); - localRodEnergy = energy; - } - else - DUNE_THROW(Exception, "Unknown interpolation method " << parameterSet["interpolationMethod"] << " requested!"); - - LocalGeodesicFEADOLCStiffness<ScalarBasis, - TargetSpace> localStiffness(localRodEnergy.get()); - - GeodesicFEAssembler<ScalarBasis,TargetSpace> rodAssembler(gridView, localStiffness); - - ///////////////////////////////////////////// - // Create a solver for the rod problem - ///////////////////////////////////////////// - - RiemannianTrustRegionSolver<ScalarBasis,RigidBodyMotion<double,3> > rodSolver; - - rodSolver.setup(grid, - &rodAssembler, - x, - dirichletNodes, - tolerance, - maxTrustRegionSteps, - initialTrustRegionRadius, - multigridIterations, - mgTolerance, - mu, nu1, nu2, - baseIterations, - baseTolerance, - instrumented); - - // ///////////////////////////////////////////////////// - // Solve! - // ///////////////////////////////////////////////////// - - std::cout << "Energy: " << rodAssembler.computeEnergy(x) << std::endl; - - rodSolver.setInitialIterate(x); - rodSolver.solve(); - - x = rodSolver.getSol(); - - // ////////////////////////////// - // Output result - // ////////////////////////////// + // Set Dirichlet values + x[rightBoundaryDof].r = parameterSet.get<FieldVector<double,3> >("dirichletValue"); + + auto axis = parameterSet.get<FieldVector<double,3> >("dirichletAxis"); + double angle = parameterSet.get<double>("dirichletAngle"); + + x[rightBoundaryDof].q = Rotation<double,3>(axis, M_PI*angle/180); + + // backup for error measurement later + std::cout << "Right boundary orientation:" << std::endl; + std::cout << "director 0: " << x[rightBoundaryDof].q.director(0) << std::endl; + std::cout << "director 1: " << x[rightBoundaryDof].q.director(1) << std::endl; + std::cout << "director 2: " << x[rightBoundaryDof].q.director(2) << std::endl; + + ////////////////////////////////////////////// + // Create the energy and assembler + ////////////////////////////////////////////// + + using ATargetSpace = TargetSpace::rebind<adouble>::other; + using GeodesicInterpolationRule = LocalGeodesicFEFunction<1, double, ScalarBasis::LocalView::Tree::FiniteElement, ATargetSpace>; + using ProjectedInterpolationRule = GFE::LocalProjectedFEFunction<1, double, ScalarBasis::LocalView::Tree::FiniteElement, ATargetSpace>; + + // Assembler using ADOL-C + std::shared_ptr<GFE::LocalEnergy<ScalarBasis,ATargetSpace> > localRodEnergy; + + if (parameterSet["interpolationMethod"] == "geodesic") + { + auto energy = std::make_shared<GFE::CosseratRodEnergy<ScalarBasis, GeodesicInterpolationRule, adouble> >(gridView, + A, J1, J2, E, nu); + energy->setReferenceConfiguration(referenceConfiguration); + localRodEnergy = energy; + } + else if (parameterSet["interpolationMethod"] == "projected") + { + auto energy = std::make_shared<GFE::CosseratRodEnergy<ScalarBasis, ProjectedInterpolationRule, adouble> >(gridView, + A, J1, J2, E, nu); + energy->setReferenceConfiguration(referenceConfiguration); + localRodEnergy = energy; + } + else + DUNE_THROW(Exception, "Unknown interpolation method " << parameterSet["interpolationMethod"] << " requested!"); + + LocalGeodesicFEADOLCStiffness<ScalarBasis, + TargetSpace> localStiffness(localRodEnergy.get()); + + GeodesicFEAssembler<ScalarBasis,TargetSpace> rodAssembler(gridView, localStiffness); + + ///////////////////////////////////////////// + // Create a solver for the rod problem + ///////////////////////////////////////////// + + RiemannianTrustRegionSolver<ScalarBasis,RigidBodyMotion<double,3> > rodSolver; + + rodSolver.setup(grid, + &rodAssembler, + x, + dirichletNodes, + tolerance, + maxTrustRegionSteps, + initialTrustRegionRadius, + multigridIterations, + mgTolerance, + mu, nu1, nu2, + baseIterations, + baseTolerance, + instrumented); + + // ///////////////////////////////////////////////////// + // Solve! + // ///////////////////////////////////////////////////// + + std::cout << "Energy: " << rodAssembler.computeEnergy(x) << std::endl; + + rodSolver.setInitialIterate(x); + rodSolver.solve(); + + x = rodSolver.getSol(); + + // ////////////////////////////// + // Output result + // ////////////////////////////// #if HAVE_DUNE_VTK - using DataCollector = Vtk::LagrangeDataCollector<GridView,order>; - DataCollector dataCollector(gridView); - VtkUnstructuredGridWriter<GridView,DataCollector> vtkWriter(gridView, Vtk::FormatTypes::ASCII); + using DataCollector = Vtk::LagrangeDataCollector<GridView,order>; + DataCollector dataCollector(gridView); + VtkUnstructuredGridWriter<GridView,DataCollector> vtkWriter(gridView, Vtk::FormatTypes::ASCII); - // Make basis for R^3-valued data - using namespace Functions::BasisFactory; + // Make basis for R^3-valued data + using namespace Functions::BasisFactory; - auto worldBasis = makeBasis( - gridView, - power<3>(lagrange<order>()) + auto worldBasis = makeBasis( + gridView, + power<3>(lagrange<order>()) ); - // The rod displacement field - BlockVector<FieldVector<double,3> > displacement(worldBasis.size()); - for (std::size_t i=0; i<x.size(); i++) - displacement[i] = x[i].r; + // The rod displacement field + BlockVector<FieldVector<double,3> > displacement(worldBasis.size()); + for (std::size_t i=0; i<x.size(); i++) + displacement[i] = x[i].r; - std::vector<double> xEmbedding; - Functions::interpolate(scalarBasis, xEmbedding, [](FieldVector<double,1> x){ return x; }); + std::vector<double> xEmbedding; + Functions::interpolate(scalarBasis, xEmbedding, [](FieldVector<double,1> x){ + return x; + }); - BlockVector<FieldVector<double,3> > gridEmbedding(xEmbedding.size()); - for (std::size_t i=0; i<gridEmbedding.size(); i++) - gridEmbedding[i] = {xEmbedding[i], 0, 0}; + BlockVector<FieldVector<double,3> > gridEmbedding(xEmbedding.size()); + for (std::size_t i=0; i<gridEmbedding.size(); i++) + gridEmbedding[i] = {xEmbedding[i], 0, 0}; - displacement -= gridEmbedding; + displacement -= gridEmbedding; - auto displacementFunction = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,3> >(worldBasis, displacement); - vtkWriter.addPointData(displacementFunction, "displacement", 3); + auto displacementFunction = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,3> >(worldBasis, displacement); + vtkWriter.addPointData(displacementFunction, "displacement", 3); - // The three director fields - using FunctionType = decltype(displacementFunction); - std::array<std::optional<FunctionType>, 3> directorFunction; - std::array<BlockVector<FieldVector<double, 3> >, 3> director; - for (int i=0; i<3; i++) - { - director[i].resize(worldBasis.size()); - for (std::size_t j=0; j<x.size(); j++) - director[i][j] = x[j].q.director(i); + // The three director fields + using FunctionType = decltype(displacementFunction); + std::array<std::optional<FunctionType>, 3> directorFunction; + std::array<BlockVector<FieldVector<double, 3> >, 3> director; + for (int i=0; i<3; i++) + { + director[i].resize(worldBasis.size()); + for (std::size_t j=0; j<x.size(); j++) + director[i][j] = x[j].q.director(i); - directorFunction[i] = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,3> >(worldBasis, std::move(director[i])); - vtkWriter.addPointData(*directorFunction[i], "director " + std::to_string(i), 3); - } + directorFunction[i] = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,3> >(worldBasis, std::move(director[i])); + vtkWriter.addPointData(*directorFunction[i], "director " + std::to_string(i), 3); + } - vtkWriter.write(resultPath + "rod3d-result"); + vtkWriter.write(resultPath + "rod3d-result"); #else - std::cout << "Falling back to legacy file writing. Get dune-vtk for better results" << std::endl; - // Fall-back solution for users without dune-vtk - CosseratVTKWriter<GridType>::write<ScalarBasis>(scalarBasis,x, resultPath + "rod3d-result"); + std::cout << "Falling back to legacy file writing. Get dune-vtk for better results" << std::endl; + // Fall-back solution for users without dune-vtk + CosseratVTKWriter<GridType>::write<ScalarBasis>(scalarBasis,x, resultPath + "rod3d-result"); #endif -} catch (Exception& e) +} +catch (Exception& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << std::endl; } diff --git a/src/rodobstacle.cc b/src/rodobstacle.cc index d4b62e05..6267e6d2 100644 --- a/src/rodobstacle.cc +++ b/src/rodobstacle.cc @@ -32,321 +32,322 @@ void setTrustRegionObstacles(double trustRegionRadius, const std::vector<BoxConstraint<double,blocksize> >& trueObstacles, const BitSetVector<blocksize>& dirichletNodes) { - //std::cout << "True obstacles\n" << trueObstacles << std::endl; + //std::cout << "True obstacles\n" << trueObstacles << std::endl; - for (int j=0; j<trustRegionObstacles.size(); j++) { + for (int j=0; j<trustRegionObstacles.size(); j++) { - for (int k=0; k<blocksize; k++) { + for (int k=0; k<blocksize; k++) { - if (dirichletNodes[j][k]) - continue; + if (dirichletNodes[j][k]) + continue; - trustRegionObstacles[j].lower(k) = - (trueObstacles[j].lower(k) < -1e10) + trustRegionObstacles[j].lower(k) = + (trueObstacles[j].lower(k) < -1e10) ? std::min(-trustRegionRadius, trueObstacles[j].upper(k) - trustRegionRadius) : trueObstacles[j].lower(k); - trustRegionObstacles[j].upper(k) = - (trueObstacles[j].upper(k) > 1e10) + trustRegionObstacles[j].upper(k) = + (trueObstacles[j].upper(k) > 1e10) ? std::max(trustRegionRadius,trueObstacles[j].lower(k) + trustRegionRadius) : trueObstacles[j].upper(k); - } - } - //std::cout << "TrustRegion obstacles\n" << trustRegionObstacles << std::endl; + } + + //std::cout << "TrustRegion obstacles\n" << trustRegionObstacles << std::endl; } bool refineCondition(const FieldVector<double,1>& pos) { - return pos[2] > -2 && pos[2] < -0.5; + return pos[2] > -2 && pos[2] < -0.5; } bool refineAll(const FieldVector<double,1>& pos) { - return true; + return true; } int main (int argc, char *argv[]) try { - // Some types that I need - typedef BCRSMatrix<FieldMatrix<double, blocksize, blocksize> > MatrixType; - typedef BlockVector<FieldVector<double, blocksize> > CorrectionType; - typedef std::vector<RigidBodyMotion<double,2> > SolutionType; - - // parse data file - ParameterTree parameterSet; - ParameterTreeParser::readINITree("rodobstacle.parset", parameterSet); - - // read solver settings - const int minLevel = parameterSet.get<int>("minLevel"); - const int maxLevel = parameterSet.get<int>("maxLevel"); - const int maxNewtonSteps = parameterSet.get<int>("maxNewtonSteps"); - const int numIt = parameterSet.get<int>("numIt"); - const int nu1 = parameterSet.get<int>("nu1"); - const int nu2 = parameterSet.get<int>("nu2"); - const int mu = parameterSet.get<int>("mu"); - const int baseIt = parameterSet.get<int>("baseIt"); - const double tolerance = parameterSet.get<double>("tolerance"); - const double baseTolerance = parameterSet.get<double>("baseTolerance"); - - // Problem settings - const int numRodBaseElements = parameterSet.get<int>("numRodBaseElements"); - - // /////////////////////////////////////// - // Create the two grids - // /////////////////////////////////////// - typedef OneDGrid GridType; - GridType grid(numRodBaseElements, 0, numRodBaseElements); - - grid.globalRefine(minLevel); - - std::vector<std::vector<BoxConstraint<double,3> > > trustRegionObstacles(minLevel+1); - std::vector<BitSetVector<3> > hasObstacle(minLevel+1); - BitSetVector<blocksize> dirichletNodes; - - // //////////////////////////////// - // Create a multigrid solver - // //////////////////////////////// - - // First create a gauss-seidel base solver - ProjectedBlockGSStep<MatrixType, CorrectionType> baseSolverStep; - - EnergyNorm<MatrixType, CorrectionType> baseEnergyNorm(baseSolverStep); - - LoopSolver<CorrectionType> baseSolver(&baseSolverStep, - baseIt, - baseTolerance, - &baseEnergyNorm, - Solver::QUIET); - - // Make pre and postsmoothers - ProjectedBlockGSStep<MatrixType, CorrectionType> presmoother; - ProjectedBlockGSStep<MatrixType, CorrectionType> postsmoother; - - MonotoneMGStep<MatrixType, CorrectionType> multigridStep; - - multigridStep.setMGType(mu, nu1, nu2); - multigridStep.ignoreNodes_ = &dirichletNodes; - multigridStep.basesolver_ = &baseSolver; - multigridStep.hasObstacle_ = &hasObstacle; - multigridStep.obstacles_ = &trustRegionObstacles; - multigridStep.verbosity_ = Solver::QUIET; - multigridStep.obstacleRestrictor_ = new MandelObstacleRestrictor<CorrectionType>; - - - EnergyNorm<MatrixType, CorrectionType> energyNorm(multigridStep); - - LoopSolver<CorrectionType> solver(&multigridStep, - numIt, - tolerance, - &energyNorm, - Solver::FULL); - - double trustRegionRadius = 0.1; - - CorrectionType rhs; - SolutionType x(grid.size(1)); - CorrectionType corr; - - // ////////////////////////// - // Initial solution - // ////////////////////////// - - for (int i=0; i<x.size(); i++) { - x[i].r[0] = 0; - x[i].r[1] = i;//double(i)/(x.size()-1); - x[i].q = Rotation<double,2>::identity(); - } + // Some types that I need + typedef BCRSMatrix<FieldMatrix<double, blocksize, blocksize> > MatrixType; + typedef BlockVector<FieldVector<double, blocksize> > CorrectionType; + typedef std::vector<RigidBodyMotion<double,2> > SolutionType; + + // parse data file + ParameterTree parameterSet; + ParameterTreeParser::readINITree("rodobstacle.parset", parameterSet); - x.back().r[1] += 1; + // read solver settings + const int minLevel = parameterSet.get<int>("minLevel"); + const int maxLevel = parameterSet.get<int>("maxLevel"); + const int maxNewtonSteps = parameterSet.get<int>("maxNewtonSteps"); + const int numIt = parameterSet.get<int>("numIt"); + const int nu1 = parameterSet.get<int>("nu1"); + const int nu2 = parameterSet.get<int>("nu2"); + const int mu = parameterSet.get<int>("mu"); + const int baseIt = parameterSet.get<int>("baseIt"); + const double tolerance = parameterSet.get<double>("tolerance"); + const double baseTolerance = parameterSet.get<double>("baseTolerance"); - // ///////////////////////////////////////////////////////////////////// - // Refinement Loop - // ///////////////////////////////////////////////////////////////////// + // Problem settings + const int numRodBaseElements = parameterSet.get<int>("numRodBaseElements"); - for (int toplevel=minLevel; toplevel<=maxLevel; toplevel++) { + // /////////////////////////////////////// + // Create the two grids + // /////////////////////////////////////// + typedef OneDGrid GridType; + GridType grid(numRodBaseElements, 0, numRodBaseElements); - std::cout << "####################################################" << std::endl; - std::cout << " Solving on level: " << toplevel << std::endl; - std::cout << "####################################################" << std::endl; + grid.globalRefine(minLevel); - dirichletNodes.resize( grid.size(1) ); - dirichletNodes.unsetAll(); + std::vector<std::vector<BoxConstraint<double,3> > > trustRegionObstacles(minLevel+1); + std::vector<BitSetVector<3> > hasObstacle(minLevel+1); + BitSetVector<blocksize> dirichletNodes; - dirichletNodes[0] = true; - dirichletNodes.back() = true; + // //////////////////////////////// + // Create a multigrid solver + // //////////////////////////////// - // //////////////////////////////////////////////////////////// - // Create solution and rhs vectors - // //////////////////////////////////////////////////////////// + // First create a gauss-seidel base solver + ProjectedBlockGSStep<MatrixType, CorrectionType> baseSolverStep; + EnergyNorm<MatrixType, CorrectionType> baseEnergyNorm(baseSolverStep); - MatrixType hessianMatrix; - RodAssembler<GridType::LeafGridView,2> rodAssembler(grid.leafGridView()); + LoopSolver<CorrectionType> baseSolver(&baseSolverStep, + baseIt, + baseTolerance, + &baseEnergyNorm, + Solver::QUIET); - rodAssembler.setParameters(1, 350000, 350000); + // Make pre and postsmoothers + ProjectedBlockGSStep<MatrixType, CorrectionType> presmoother; + ProjectedBlockGSStep<MatrixType, CorrectionType> postsmoother; - MatrixIndexSet indices(grid.size(toplevel,1), grid.size(toplevel,1)); - rodAssembler.getNeighborsPerVertex(indices); - indices.exportIdx(hessianMatrix); + MonotoneMGStep<MatrixType, CorrectionType> multigridStep; - rhs.resize(grid.size(toplevel,1)); - corr.resize(grid.size(toplevel,1)); + multigridStep.setMGType(mu, nu1, nu2); + multigridStep.ignoreNodes_ = &dirichletNodes; + multigridStep.basesolver_ = &baseSolver; + multigridStep.hasObstacle_ = &hasObstacle; + multigridStep.obstacles_ = &trustRegionObstacles; + multigridStep.verbosity_ = Solver::QUIET; + multigridStep.obstacleRestrictor_ = new MandelObstacleRestrictor<CorrectionType>; - // ////////////////////////////////////////////////////////// - // Create obstacles - // ////////////////////////////////////////////////////////// + EnergyNorm<MatrixType, CorrectionType> energyNorm(multigridStep); - hasObstacle.resize(toplevel+1); - for (int i=0; i<hasObstacle.size(); i++) { - hasObstacle[i].resize(grid.size(i, 1)); - hasObstacle[i].setAll(); - } + LoopSolver<CorrectionType> solver(&multigridStep, + numIt, + tolerance, + &energyNorm, + Solver::FULL); - std::vector<std::vector<BoxConstraint<double,3> > > trueObstacles(toplevel+1); - trustRegionObstacles.resize(toplevel+1); + double trustRegionRadius = 0.1; - for (int i=0; i<toplevel+1; i++) { - trueObstacles[i].resize(grid.size(i,1)); - trustRegionObstacles[i].resize(grid.size(i,1)); - } + CorrectionType rhs; + SolutionType x(grid.size(1)); + CorrectionType corr; - for (int i=0; i<trueObstacles[toplevel].size(); i++) { - trueObstacles[toplevel][i].clear(); - //trueObstacles[toplevel][i].val[0] = - x[i][0]; - trueObstacles[toplevel][i].upper(0) = 0.1 - x[i].r[0]; - } + // ////////////////////////// + // Initial solution + // ////////////////////////// + for (int i=0; i<x.size(); i++) { + x[i].r[0] = 0; + x[i].r[1] = i; //double(i)/(x.size()-1); + x[i].q = Rotation<double,2>::identity(); + } - trustRegionObstacles.resize(toplevel+1); - for (int i=0; i<=toplevel; i++) - trustRegionObstacles[i].resize(grid.size(i, 1)); + x.back().r[1] += 1; - // //////////////////////////////////////////// - // Adjust the solver to the new hierarchy - // //////////////////////////////////////////// + // ///////////////////////////////////////////////////////////////////// + // Refinement Loop + // ///////////////////////////////////////////////////////////////////// - multigridStep.setNumberOfLevels(toplevel+1); - multigridStep.ignoreNodes_ = &dirichletNodes; - multigridStep.setSmoother(&presmoother, &postsmoother); + for (int toplevel=minLevel; toplevel<=maxLevel; toplevel++) { - for (int k=0; k<multigridStep.mgTransfer_.size(); k++) - delete(multigridStep.mgTransfer_[k]); + std::cout << "####################################################" << std::endl; + std::cout << " Solving on level: " << toplevel << std::endl; + std::cout << "####################################################" << std::endl; - multigridStep.mgTransfer_.resize(toplevel); + dirichletNodes.resize( grid.size(1) ); + dirichletNodes.unsetAll(); - for (int i=0; i<multigridStep.mgTransfer_.size(); i++){ - TruncatedCompressedMGTransfer<CorrectionType>* newTransferOp = new TruncatedCompressedMGTransfer<CorrectionType>; - newTransferOp->setup(grid,i,i+1); - multigridStep.mgTransfer_[i] = newTransferOp; - } + dirichletNodes[0] = true; + dirichletNodes.back() = true; - // ///////////////////////////////////////////////////// - // Trust-Region Solver - // ///////////////////////////////////////////////////// - for (int i=0; i<maxNewtonSteps; i++) { + // //////////////////////////////////////////////////////////// + // Create solution and rhs vectors + // //////////////////////////////////////////////////////////// - std::cout << "-----------------------------------------------------------------------------" << std::endl; - std::cout << " Trust-Region Step Number: " << i - << ", radius: " << trustRegionRadius - << ", energy: " << rodAssembler.computeEnergy(x) << std::endl; - std::cout << "-----------------------------------------------------------------------------" << std::endl; - rhs = 0; - corr = 0; + MatrixType hessianMatrix; + RodAssembler<GridType::LeafGridView,2> rodAssembler(grid.leafGridView()); - rodAssembler.assembleGradient(x, rhs); - rodAssembler.assembleMatrix(x, hessianMatrix); + rodAssembler.setParameters(1, 350000, 350000); - rhs *= -1; + MatrixIndexSet indices(grid.size(toplevel,1), grid.size(toplevel,1)); + rodAssembler.getNeighborsPerVertex(indices); + indices.exportIdx(hessianMatrix); - // Create trust-region obstacle on grid0.maxLevel() - setTrustRegionObstacles(trustRegionRadius, - trustRegionObstacles[toplevel], - trueObstacles[toplevel], - dirichletNodes); + rhs.resize(grid.size(toplevel,1)); + corr.resize(grid.size(toplevel,1)); - dynamic_cast<MultigridStep<MatrixType,CorrectionType>*>(solver.iterationStep_)->setProblem(hessianMatrix, corr, rhs, toplevel+1); - solver.preprocess(); + // ////////////////////////////////////////////////////////// + // Create obstacles + // ////////////////////////////////////////////////////////// - multigridStep.preprocess(); + hasObstacle.resize(toplevel+1); + for (int i=0; i<hasObstacle.size(); i++) { + hasObstacle[i].resize(grid.size(i, 1)); + hasObstacle[i].setAll(); + } + + std::vector<std::vector<BoxConstraint<double,3> > > trueObstacles(toplevel+1); + trustRegionObstacles.resize(toplevel+1); + for (int i=0; i<toplevel+1; i++) { + trueObstacles[i].resize(grid.size(i,1)); + trustRegionObstacles[i].resize(grid.size(i,1)); + } - // ///////////////////////////// - // Solve ! - // ///////////////////////////// - solver.solve(); + for (int i=0; i<trueObstacles[toplevel].size(); i++) { + trueObstacles[toplevel][i].clear(); + //trueObstacles[toplevel][i].val[0] = - x[i][0]; + trueObstacles[toplevel][i].upper(0) = 0.1 - x[i].r[0]; + } - corr = multigridStep.getSol(); - printf("infinity norm of the correction: %g\n", corr.infinity_norm()); - if (corr.infinity_norm() < 1e-5) { - std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; - break; - } + trustRegionObstacles.resize(toplevel+1); + for (int i=0; i<=toplevel; i++) + trustRegionObstacles[i].resize(grid.size(i, 1)); + + // //////////////////////////////////////////// + // Adjust the solver to the new hierarchy + // //////////////////////////////////////////// + + multigridStep.setNumberOfLevels(toplevel+1); + multigridStep.ignoreNodes_ = &dirichletNodes; + multigridStep.setSmoother(&presmoother, &postsmoother); + + for (int k=0; k<multigridStep.mgTransfer_.size(); k++) + delete(multigridStep.mgTransfer_[k]); + + multigridStep.mgTransfer_.resize(toplevel); + + for (int i=0; i<multigridStep.mgTransfer_.size(); i++) { + TruncatedCompressedMGTransfer<CorrectionType>* newTransferOp = new TruncatedCompressedMGTransfer<CorrectionType>; + newTransferOp->setup(grid,i,i+1); + multigridStep.mgTransfer_[i] = newTransferOp; + } + + // ///////////////////////////////////////////////////// + // Trust-Region Solver + // ///////////////////////////////////////////////////// + for (int i=0; i<maxNewtonSteps; i++) { - // //////////////////////////////////////////////////// - // Check whether trust-region step can be accepted - // //////////////////////////////////////////////////// + std::cout << "-----------------------------------------------------------------------------" << std::endl; + std::cout << " Trust-Region Step Number: " << i + << ", radius: " << trustRegionRadius + << ", energy: " << rodAssembler.computeEnergy(x) << std::endl; + std::cout << "-----------------------------------------------------------------------------" << std::endl; - SolutionType newIterate = x; - for (int j=0; j<newIterate.size(); j++) - newIterate[j] = RigidBodyMotion<double,2>::exp(newIterate[j], corr[j]); + rhs = 0; + corr = 0; - /** \todo Don't always recompute oldEnergy */ - double oldEnergy = rodAssembler.computeEnergy(x); - double energy = rodAssembler.computeEnergy(newIterate); + rodAssembler.assembleGradient(x, rhs); + rodAssembler.assembleMatrix(x, hessianMatrix); - if (energy >= oldEnergy) - DUNE_THROW(SolverError, "Direction is not a descent direction!"); + rhs *= -1; - // Add correction to the current solution - for (int j=0; j<x.size(); j++) - x[j] = RigidBodyMotion<double,2>::exp(x[j], corr[j]); + // Create trust-region obstacle on grid0.maxLevel() + setTrustRegionObstacles(trustRegionRadius, + trustRegionObstacles[toplevel], + trueObstacles[toplevel], + dirichletNodes); - // Subtract correction from the current obstacle - for (int k=0; k<corr.size(); k++) - trueObstacles[grid.maxLevel()][k] -= corr[k]; + dynamic_cast<MultigridStep<MatrixType,CorrectionType>*>(solver.iterationStep_)->setProblem(hessianMatrix, corr, rhs, toplevel+1); - } + solver.preprocess(); - // ////////////////////////////// - // Output result - // ////////////////////////////// + multigridStep.preprocess(); - // Write Lagrange multiplyers - std::stringstream levelAsAscii; - levelAsAscii << toplevel; - std::string lagrangeFilename = "pressure/lagrange_" + levelAsAscii.str(); - std::ofstream lagrangeFile(lagrangeFilename.c_str()); - CorrectionType lagrangeMultipliers; - rodAssembler.assembleGradient(x, lagrangeMultipliers); - lagrangeFile << lagrangeMultipliers << std::endl; + // ///////////////////////////// + // Solve ! + // ///////////////////////////// + solver.solve(); - // Write result grid - std::string solutionFilename = "solutions/rod_" + levelAsAscii.str() + ".result"; - writeRod(x, solutionFilename); + corr = multigridStep.getSol(); - // //////////////////////////////////////////////////////////////////////////// - // Refine locally and transfer the current solution to the new leaf level - // //////////////////////////////////////////////////////////////////////////// + printf("infinity norm of the correction: %g\n", corr.infinity_norm()); + if (corr.infinity_norm() < 1e-5) { + std::cout << "CORRECTION IS SMALL ENOUGH" << std::endl; + break; + } - GeometricEstimator<GridType> estimator; + // //////////////////////////////////////////////////// + // Check whether trust-region step can be accepted + // //////////////////////////////////////////////////// - estimator.estimate(grid, (toplevel<=minLevel) ? refineAll : refineCondition); + SolutionType newIterate = x; + for (int j=0; j<newIterate.size(); j++) + newIterate[j] = RigidBodyMotion<double,2>::exp(newIterate[j], corr[j]); - std::cout << " #### WARNING: function not transferred to the next level! #### " << std::endl; - grid.adapt(); - x.resize(grid.size(1)); + /** \todo Don't always recompute oldEnergy */ + double oldEnergy = rodAssembler.computeEnergy(x); + double energy = rodAssembler.computeEnergy(newIterate); + + if (energy >= oldEnergy) + DUNE_THROW(SolverError, "Direction is not a descent direction!"); + + // Add correction to the current solution + for (int j=0; j<x.size(); j++) + x[j] = RigidBodyMotion<double,2>::exp(x[j], corr[j]); + + // Subtract correction from the current obstacle + for (int k=0; k<corr.size(); k++) + trueObstacles[grid.maxLevel()][k] -= corr[k]; - //writeRod(x, "solutions/rod_1.result"); } - } catch (Exception e) { + // ////////////////////////////// + // Output result + // ////////////////////////////// + + // Write Lagrange multiplyers + std::stringstream levelAsAscii; + levelAsAscii << toplevel; + std::string lagrangeFilename = "pressure/lagrange_" + levelAsAscii.str(); + std::ofstream lagrangeFile(lagrangeFilename.c_str()); + + CorrectionType lagrangeMultipliers; + rodAssembler.assembleGradient(x, lagrangeMultipliers); + lagrangeFile << lagrangeMultipliers << std::endl; + + // Write result grid + std::string solutionFilename = "solutions/rod_" + levelAsAscii.str() + ".result"; + writeRod(x, solutionFilename); + + // //////////////////////////////////////////////////////////////////////////// + // Refine locally and transfer the current solution to the new leaf level + // //////////////////////////////////////////////////////////////////////////// - std::cout << e << std::endl; + GeometricEstimator<GridType> estimator; - } + estimator.estimate(grid, (toplevel<=minLevel) ? refineAll : refineCondition); + + std::cout << " #### WARNING: function not transferred to the next level! #### " << std::endl; + grid.adapt(); + x.resize(grid.size(1)); + + //writeRod(x, "solutions/rod_1.result"); + } + +} +catch (Exception e) { + + std::cout << e << std::endl; + +} diff --git a/src/simofoxshell.cc b/src/simofoxshell.cc index 8904851f..f8a77af5 100644 --- a/src/simofoxshell.cc +++ b/src/simofoxshell.cc @@ -285,9 +285,9 @@ int main(int argc, char *argv[]) try }); auto displacementFunctionInitial = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double, 3> >(deformationPowerBasis, - displacementInitial); + displacementInitial); auto directorFunctionInitial = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double, 3> >(directorPowerBasis, - directorInitial); + directorInitial); // We need to subsample, because VTK cannot natively display real second-order functions SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(midsurfaceOrder - 1)); vtkWriter.addVertexData(displacementFunctionInitial, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, 3)); @@ -301,9 +301,9 @@ int main(int argc, char *argv[]) try // Assembler using ADOL-C Dune::GFE::SimoFoxEnergyLocalStiffness<decltype(compositeBasis), LocalFEFunction,adouble> simoFoxEnergyADOLCLocalStiffness(materialParameters, - &neumannBoundary, - neumannFunction, - nullptr, x0); + &neumannBoundary, + neumannFunction, + nullptr, x0); MixedLocalGFEADOLCStiffness<decltype(compositeBasis), RealTuple<double,3>, @@ -328,8 +328,8 @@ int main(int argc, char *argv[]) try BlockVector<FieldVector<double,3> > ddV(deformationPowerBasis.size()); Functions::interpolate(deformationPowerBasis, ddV, deformationDirichletValues, deformationDirichletDofs); - for (size_t j = 0; j < x[_0].size(); j++){ - if (deformationDirichletNodes[j][0]){ + for (size_t j = 0; j < x[_0].size(); j++) { + if (deformationDirichletNodes[j][0]) { x[_0][j] = ddV[j]; } } @@ -339,41 +339,41 @@ int main(int argc, char *argv[]) try // ///////////////////////////////////////////////// if (parameterSet.get<std::string>("solvertype", "trustRegion") == "trustRegion") { - MixedRiemannianTrustRegionSolver<Grid, - decltype(compositeBasis), - MidsurfaceFEBasis, RealTuple<double,3>, - DirectorFEBasis, UnitVector<double,3> > solver; - - solver.setup(*grid, - &assembler, - midsurfaceFEBasis, - directorFEBasis, - x, - deformationDirichletDofs, - orientationDirichletDofs, - tolerance, - maxSolverSteps, - initialTrustRegionRadius, - multigridIterations, - mgTolerance, - mu, nu1, nu2, - baseIterations, - baseTolerance, - instrumented); - - solver.setScaling(parameterSet.get<FieldVector<double, 5> >("trustRegionScaling")); - - // ///////////////////////////////////////////////////// - // Solve! - // ///////////////////////////////////////////////////// - - solver.setInitialIterate(x); - solver.solve(); - - x = solver.getSol(); + MixedRiemannianTrustRegionSolver<Grid, + decltype(compositeBasis), + MidsurfaceFEBasis, RealTuple<double,3>, + DirectorFEBasis, UnitVector<double,3> > solver; + + solver.setup(*grid, + &assembler, + midsurfaceFEBasis, + directorFEBasis, + x, + deformationDirichletDofs, + orientationDirichletDofs, + tolerance, + maxSolverSteps, + initialTrustRegionRadius, + multigridIterations, + mgTolerance, + mu, nu1, nu2, + baseIterations, + baseTolerance, + instrumented); + + solver.setScaling(parameterSet.get<FieldVector<double, 5> >("trustRegionScaling")); + + // ///////////////////////////////////////////////////// + // Solve! + // ///////////////////////////////////////////////////// + + solver.setInitialIterate(x); + solver.solve(); + + x = solver.getSol(); } else { #if !MIXED_SPACE - using TargetSpace = Dune::GFE::ProductManifold<RealTuple<double,3>,UnitVector<double,3>>; + using TargetSpace = Dune::GFE::ProductManifold<RealTuple<double,3>,UnitVector<double,3> >; std::vector<TargetSpace> xTargetSpace(compositeBasis.size({0})); BitSetVector<TargetSpace::TangentVector::dimension> dirichletDofsTargetSpace(compositeBasis.size({0}), false); for (std::size_t i = 0; i < compositeBasis.size({0}); i++) { @@ -384,7 +384,7 @@ int main(int argc, char *argv[]) try for (int j = 3; j < TargetSpace::TangentVector::dimension; j ++) dirichletDofsTargetSpace[i][j] = orientationDirichletDofs[i][j-3]; } - using GFEAssemblerWrapper = Dune::GFE::GeodesicFEAssemblerWrapper<decltype(compositeBasis), MidsurfaceFEBasis, TargetSpace, RealTuple<double, 3>, UnitVector<double,3>>; + using GFEAssemblerWrapper = Dune::GFE::GeodesicFEAssemblerWrapper<decltype(compositeBasis), MidsurfaceFEBasis, TargetSpace, RealTuple<double, 3>, UnitVector<double,3> >; GFEAssemblerWrapper assemblerNotMixed(&assembler, midsurfaceFEBasis); RiemannianProximalNewtonSolver<MidsurfaceFEBasis, TargetSpace, GFEAssemblerWrapper> solver; solver.setup(*grid, diff --git a/test/adolctest-scalar-and-vector-mode.cc b/test/adolctest-scalar-and-vector-mode.cc index a989ac63..61d8f1e5 100644 --- a/test/adolctest-scalar-and-vector-mode.cc +++ b/test/adolctest-scalar-and-vector-mode.cc @@ -44,14 +44,14 @@ using namespace Indices; using ValueType = adouble; //Types for the mixed space -using DisplacementVector = std::vector<RealTuple<double,dim>>; -using RotationVector = std::vector<Rotation<double,dim>>; +using DisplacementVector = std::vector<RealTuple<double,dim> >; +using RotationVector = std::vector<Rotation<double,dim> >; using Vector = TupleVector<DisplacementVector, RotationVector>; const int dimCR = Rotation<double,dim>::TangentVector::dimension; //dimCorrectionRotation = Dimension of the correction for rotations -using CorrectionTypeMixed = MultiTypeBlockVector<BlockVector<FieldVector<double,dim> >, BlockVector<FieldVector<double,dimCR>>>; +using CorrectionTypeMixed = MultiTypeBlockVector<BlockVector<FieldVector<double,dim> >, BlockVector<FieldVector<double,dimCR> > >; -using MatrixRow0 = MultiTypeBlockVector<BCRSMatrix<FieldMatrix<double,dim,dim>>, BCRSMatrix<FieldMatrix<double,dim,dimCR>>>; -using MatrixRow1 = MultiTypeBlockVector<BCRSMatrix<FieldMatrix<double,dimCR,dim>>, BCRSMatrix<FieldMatrix<double,dimCR,dimCR>>>; +using MatrixRow0 = MultiTypeBlockVector<BCRSMatrix<FieldMatrix<double,dim,dim> >, BCRSMatrix<FieldMatrix<double,dim,dimCR> > >; +using MatrixRow1 = MultiTypeBlockVector<BCRSMatrix<FieldMatrix<double,dimCR,dim> >, BCRSMatrix<FieldMatrix<double,dimCR,dimCR> > >; using MatrixTypeMixed = MultiTypeBlockMatrix<MatrixRow0,MatrixRow1>; //Types for the Non-mixed space @@ -87,11 +87,11 @@ int main (int argc, char *argv[]) composite( power<dim>( lagrange<1>() - ), + ), power<dim>( lagrange<1>() - ) - )); + ) + )); using CompositeBasis = decltype(compositeBasis); @@ -114,30 +114,30 @@ int main (int argc, char *argv[]) //Mixed space CosseratEnergyLocalStiffness<decltype(compositeBasis), dim,adouble> cosseratEnergyMixed(parameters, - nullptr, - nullptr, - nullptr); + nullptr, + nullptr, + nullptr); MixedLocalGFEADOLCStiffness<CompositeBasis, - RealTuple<double,dim>, - Rotation<double,dim> > mixedLocalGFEADOLCStiffnessVector(&cosseratEnergyMixed, false); + RealTuple<double,dim>, + Rotation<double,dim> > mixedLocalGFEADOLCStiffnessVector(&cosseratEnergyMixed, false); MixedGFEAssembler<CompositeBasis, - RealTuple<double,dim>, - Rotation<double,dim> > mixedAssemblerVector(compositeBasis, &mixedLocalGFEADOLCStiffnessVector); + RealTuple<double,dim>, + Rotation<double,dim> > mixedAssemblerVector(compositeBasis, &mixedLocalGFEADOLCStiffnessVector); MixedLocalGFEADOLCStiffness<CompositeBasis, - RealTuple<double,dim>, - Rotation<double,dim> > mixedLocalGFEADOLCStiffnessScalar(&cosseratEnergyMixed, true); + RealTuple<double,dim>, + Rotation<double,dim> > mixedLocalGFEADOLCStiffnessScalar(&cosseratEnergyMixed, true); MixedGFEAssembler<CompositeBasis, - RealTuple<double,dim>, - Rotation<double,dim> > mixedAssemblerScalar(compositeBasis, &mixedLocalGFEADOLCStiffnessScalar); - + RealTuple<double,dim>, + Rotation<double,dim> > mixedAssemblerScalar(compositeBasis, &mixedLocalGFEADOLCStiffnessScalar); + //Non-mixed space using DeformationFEBasis = Dune::Functions::LagrangeBasis<GridView,1>; CosseratEnergyLocalStiffness<DeformationFEBasis, dim,adouble> cosseratEnergy(parameters, - nullptr, - nullptr, - nullptr); + nullptr, + nullptr, + nullptr); LocalGeodesicFEADOLCStiffness<DeformationFEBasis,RBM> localGFEADOLCStiffnessVector(&cosseratEnergy, false); GeodesicFEAssembler<DeformationFEBasis,RBM> assemblerVector(gridView, localGFEADOLCStiffnessVector); @@ -151,10 +151,12 @@ int main (int argc, char *argv[]) auto deformationPowerBasis = makeBasis( gridView, power<gridDim>( - lagrange<1>() - )); + lagrange<1>() + )); BlockVector<FieldVector<double,gridDim> > identity(compositeBasis.size({0})); - Functions::interpolate(deformationPowerBasis, identity, [](FieldVector<double,gridDim> x){ return x; }); + Functions::interpolate(deformationPowerBasis, identity, [](FieldVector<double,gridDim> x){ + return x; + }); BlockVector<FieldVector<double,dim> > initialDeformation(compositeBasis.size({0})); initialDeformation = 0; @@ -202,13 +204,13 @@ int main (int argc, char *argv[]) if (differenceMixed.frobenius_norm() > 1e-8) { - std::cerr << "MixedLocalGFEADOLCStiffness: The ADOL-C scalar mode and vector mode produce different Hessians!" << std::endl; - return 1; + std::cerr << "MixedLocalGFEADOLCStiffness: The ADOL-C scalar mode and vector mode produce different Hessians!" << std::endl; + return 1; } if (difference.frobenius_norm() > 1e-8) { - std::cerr << "LocalGFEADOLCStiffness: The ADOL-C scalar mode and vector mode produce different Hessians!" << std::endl; - return 1; + std::cerr << "LocalGFEADOLCStiffness: The ADOL-C scalar mode and vector mode produce different Hessians!" << std::endl; + return 1; } return 0; } diff --git a/test/adolctest.cc b/test/adolctest.cc index 6529617f..b43c9062 100644 --- a/test/adolctest.cc +++ b/test/adolctest.cc @@ -57,41 +57,41 @@ using namespace Dune; template<class Basis> class LocalADOLCStiffness { - // grid types - typedef typename Basis::GridView GridView; - typedef typename GridView::ctype DT; - typedef typename TargetSpace::ctype RT; - typedef typename GridView::template Codim<0>::Entity Entity; + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype DT; + typedef typename TargetSpace::ctype RT; + typedef typename GridView::template Codim<0>::Entity Entity; - typedef typename TargetSpace::template rebind<adouble>::other ATargetSpace; + typedef typename TargetSpace::template rebind<adouble>::other ATargetSpace; - // some other sizes - constexpr static int gridDim = GridView::dimension; + // some other sizes + constexpr static int gridDim = GridView::dimension; public: - //! Dimension of the embedding space - constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; + //! Dimension of the embedding space + constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; - LocalADOLCStiffness(const GFE::LocalEnergy<Basis, ATargetSpace>* energy) + LocalADOLCStiffness(const GFE::LocalEnergy<Basis, ATargetSpace>* energy) : localEnergy_(energy) - {} + {} - /** \brief Compute the energy at the current configuration */ - virtual RT energy (const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution) const; + /** \brief Compute the energy at the current configuration */ + virtual RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution) const; - /** \brief Assemble the local stiffness matrix at the current position + /** \brief Assemble the local stiffness matrix at the current position - This uses the automatic differentiation toolbox ADOL_C. - */ - virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution, - std::vector<Dune::FieldVector<double, embeddedBlocksize> >& localGradient, - Dune::Matrix<Dune::FieldMatrix<RT,embeddedBlocksize,embeddedBlocksize> >& localHessian, - bool vectorMode); + This uses the automatic differentiation toolbox ADOL_C. + */ + virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution, + std::vector<Dune::FieldVector<double, embeddedBlocksize> >& localGradient, + Dune::Matrix<Dune::FieldMatrix<RT,embeddedBlocksize,embeddedBlocksize> >& localHessian, + bool vectorMode); - const GFE::LocalEnergy<Basis, ATargetSpace>* localEnergy_; + const GFE::LocalEnergy<Basis, ATargetSpace>* localEnergy_; }; @@ -102,38 +102,38 @@ LocalADOLCStiffness<Basis>:: energy(const typename Basis::LocalView& localView, 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 - } + 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(localView,localASolution); + energy = localEnergy_->energy(localView,localASolution); - energy >>= pureEnergy; + energy >>= pureEnergy; - trace_off(); - return pureEnergy; + trace_off(); + return pureEnergy; } @@ -146,64 +146,64 @@ energy(const typename Basis::LocalView& localView, template <class Basis> void LocalADOLCStiffness<Basis>:: assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution, - std::vector<Dune::FieldVector<double,embeddedBlocksize> >& localGradient, - Dune::Matrix<Dune::FieldMatrix<RT,embeddedBlocksize,embeddedBlocksize> >& localHessian, - bool vectorMode) + const std::vector<TargetSpace>& localSolution, + std::vector<Dune::FieldVector<double,embeddedBlocksize> >& 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(localView, 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]; + // Tape energy computation. We may not have to do this every time, but it's comparatively cheap. + energy(localView, 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; - } + 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++]; - for(size_t i=0; i<nDoubles; i++) - free(rawHessian[i]); + ///////////////////////////////////////////////////////////////// + // 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]); } @@ -212,32 +212,32 @@ assembleGradientAndHessian(const typename Basis::LocalView& localView, template<class Basis, class field_type=double> class LocalFDStiffness { - // grid types - typedef typename Basis::GridView GridView; - typedef typename GridView::Grid::ctype DT; - typedef typename GridView::template Codim<0>::Entity Entity; + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::Grid::ctype DT; + typedef typename GridView::template Codim<0>::Entity Entity; - typedef typename TargetSpace::template rebind<field_type>::other ATargetSpace; + typedef typename TargetSpace::template rebind<field_type>::other ATargetSpace; public: - //! Dimension of a tangent space - constexpr static int blocksize = TargetSpace::TangentVector::dimension; + //! Dimension of a tangent space + constexpr static int blocksize = TargetSpace::TangentVector::dimension; - //! Dimension of the embedding space - constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; + //! Dimension of the embedding space + constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; - LocalFDStiffness(const GFE::LocalEnergy<Basis, ATargetSpace>* energy) + LocalFDStiffness(const GFE::LocalEnergy<Basis, ATargetSpace>* energy) : localEnergy_(energy) - {} + {} - virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution, - std::vector<Dune::FieldVector<double,embeddedBlocksize> >& localGradient, - Dune::Matrix<Dune::FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> >& localHessian); + virtual void assembleGradientAndHessian(const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution, + std::vector<Dune::FieldVector<double,embeddedBlocksize> >& localGradient, + Dune::Matrix<Dune::FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> >& localHessian); - const GFE::LocalEnergy<Basis, ATargetSpace>* localEnergy_; + const GFE::LocalEnergy<Basis, ATargetSpace>* localEnergy_; }; // /////////////////////////////////////////////////////////// @@ -246,132 +246,132 @@ public: template <class Basis, class field_type> void LocalFDStiffness<Basis, field_type>:: assembleGradientAndHessian(const typename Basis::LocalView& localView, - const std::vector<TargetSpace>& localSolution, - std::vector<Dune::FieldVector<double, embeddedBlocksize> >& localGradient, - Dune::Matrix<Dune::FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> >& localHessian) + const std::vector<TargetSpace>& localSolution, + std::vector<Dune::FieldVector<double, embeddedBlocksize> >& localGradient, + Dune::Matrix<Dune::FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> >& localHessian) { - // Number of degrees of freedom for this element - size_t nDofs = localSolution.size(); + // Number of degrees of freedom for this element + size_t nDofs = localSolution.size(); - // Clear assemble data - localHessian.setSize(nDofs, nDofs); - localHessian = 0; + // Clear assemble data + localHessian.setSize(nDofs, nDofs); + localHessian = 0; #ifdef MULTIPRECISION - const field_type eps = 1e-10; + const field_type eps = 1e-10; #else - const field_type eps = 1e-4; + const field_type eps = 1e-4; #endif - std::vector<ATargetSpace> localASolution(localSolution.size()); - 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 - } - - 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; - } + std::vector<ATargetSpace> localASolution(localSolution.size()); + 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 + } - // 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 = -localEnergy_->energy(localView, localASolution); + 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 energy infinitesimal corrections in the directions of the local basis vectors - std::vector<std::array<field_type,embeddedBlocksize> > forwardEnergy(nDofs); - std::vector<std::array<field_type,embeddedBlocksize> > backwardEnergy(nDofs); + // 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 = -localEnergy_->energy(localView, localASolution); - for (size_t i=0; i<localSolution.size(); i++) { - for (size_t i2=0; i2<embeddedBlocksize; i2++) { - typename ATargetSpace::EmbeddedTangentVector epsXi = B[i][i2]; - epsXi *= eps; - typename ATargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; - minusEpsXi *= -1; + // Precompute energy infinitesimal corrections in the directions of the local basis vectors + std::vector<std::array<field_type,embeddedBlocksize> > forwardEnergy(nDofs); + std::vector<std::array<field_type,embeddedBlocksize> > backwardEnergy(nDofs); - std::vector<ATargetSpace> forwardSolution = localASolution; - std::vector<ATargetSpace> backwardSolution = localASolution; + for (size_t i=0; i<localSolution.size(); i++) { + for (size_t i2=0; i2<embeddedBlocksize; i2++) { + typename ATargetSpace::EmbeddedTangentVector epsXi = B[i][i2]; + epsXi *= eps; + typename ATargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; + minusEpsXi *= -1; - forwardSolution[i] = ATargetSpace(localASolution[i].globalCoordinates() + epsXi); - backwardSolution[i] = ATargetSpace(localASolution[i].globalCoordinates() + minusEpsXi); + std::vector<ATargetSpace> forwardSolution = localASolution; + std::vector<ATargetSpace> backwardSolution = localASolution; - forwardEnergy[i][i2] = localEnergy_->energy(localView, forwardSolution); - backwardEnergy[i][i2] = localEnergy_->energy(localView, backwardSolution); + forwardSolution[i] = ATargetSpace(localASolution[i].globalCoordinates() + epsXi); + backwardSolution[i] = ATargetSpace(localASolution[i].globalCoordinates() + minusEpsXi); - } + forwardEnergy[i][i2] = localEnergy_->energy(localView, forwardSolution); + backwardEnergy[i][i2] = localEnergy_->energy(localView, backwardSolution); } - ////////////////////////////////////////////////////////////// - // Compute gradient by finite-difference approximation - ////////////////////////////////////////////////////////////// + } - localGradient.resize(localSolution.size()); + ////////////////////////////////////////////////////////////// + // Compute gradient by finite-difference approximation + ////////////////////////////////////////////////////////////// - for (size_t i=0; i<localSolution.size(); i++) - for (int j=0; j<embeddedBlocksize; j++) - { - field_type foo = (forwardEnergy[i][j] - backwardEnergy[i][j]) / (2*eps); + localGradient.resize(localSolution.size()); + + for (size_t i=0; i<localSolution.size(); i++) + for (int j=0; j<embeddedBlocksize; j++) + { + field_type foo = (forwardEnergy[i][j] - backwardEnergy[i][j]) / (2*eps); #ifdef MULTIPRECISION - localGradient[i][j] = foo.template convert_to<double>(); + localGradient[i][j] = foo.template convert_to<double>(); #else - localGradient[i][j] = foo; + localGradient[i][j] = foo; #endif - } + } - /////////////////////////////////////////////////////////////////////////// - // 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<ATargetSpace> forwardSolutionXiEta = localASolution; - std::vector<ATargetSpace> backwardSolutionXiEta = localASolution; - - typename ATargetSpace::EmbeddedTangentVector epsXi = B[i][i2]; epsXi *= eps; - typename ATargetSpace::EmbeddedTangentVector epsEta = B[j][j2]; epsEta *= eps; - - typename ATargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; minusEpsXi *= -1; - typename ATargetSpace::EmbeddedTangentVector minusEpsEta = epsEta; minusEpsEta *= -1; - - if (i==j) - forwardSolutionXiEta[i] = ATargetSpace(localASolution[i].globalCoordinates() + epsXi+epsEta); - else { - forwardSolutionXiEta[i] = ATargetSpace(localASolution[i].globalCoordinates() + epsXi); - forwardSolutionXiEta[j] = ATargetSpace(localASolution[j].globalCoordinates() + epsEta); - } - - if (i==j) - backwardSolutionXiEta[i] = ATargetSpace(localASolution[i].globalCoordinates() + minusEpsXi+minusEpsEta); - else { - backwardSolutionXiEta[i] = ATargetSpace(localASolution[i].globalCoordinates() + minusEpsXi); - backwardSolutionXiEta[j] = ATargetSpace(localASolution[j].globalCoordinates() + minusEpsEta); - } - - field_type forwardValue = localEnergy_->energy(localView, forwardSolutionXiEta) - forwardEnergy[i][i2] - forwardEnergy[j][j2]; - field_type backwardValue = localEnergy_->energy(localView, backwardSolutionXiEta) - backwardEnergy[i][i2] - backwardEnergy[j][j2]; - - field_type foo = 0.5 * (forwardValue - 2*centerValue + backwardValue) / (eps*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<ATargetSpace> forwardSolutionXiEta = localASolution; + std::vector<ATargetSpace> backwardSolutionXiEta = localASolution; + + typename ATargetSpace::EmbeddedTangentVector epsXi = B[i][i2]; epsXi *= eps; + typename ATargetSpace::EmbeddedTangentVector epsEta = B[j][j2]; epsEta *= eps; + + typename ATargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; minusEpsXi *= -1; + typename ATargetSpace::EmbeddedTangentVector minusEpsEta = epsEta; minusEpsEta *= -1; + + if (i==j) + forwardSolutionXiEta[i] = ATargetSpace(localASolution[i].globalCoordinates() + epsXi+epsEta); + else { + forwardSolutionXiEta[i] = ATargetSpace(localASolution[i].globalCoordinates() + epsXi); + forwardSolutionXiEta[j] = ATargetSpace(localASolution[j].globalCoordinates() + epsEta); + } + + if (i==j) + backwardSolutionXiEta[i] = ATargetSpace(localASolution[i].globalCoordinates() + minusEpsXi+minusEpsEta); + else { + backwardSolutionXiEta[i] = ATargetSpace(localASolution[i].globalCoordinates() + minusEpsXi); + backwardSolutionXiEta[j] = ATargetSpace(localASolution[j].globalCoordinates() + minusEpsEta); + } + + field_type forwardValue = localEnergy_->energy(localView, forwardSolutionXiEta) - forwardEnergy[i][i2] - forwardEnergy[j][j2]; + field_type backwardValue = localEnergy_->energy(localView, backwardSolutionXiEta) - backwardEnergy[i][i2] - backwardEnergy[j][j2]; + + field_type foo = 0.5 * (forwardValue - 2*centerValue + backwardValue) / (eps*eps); #ifdef MULTIPRECISION - localHessian[i][j][i2][j2] = localHessian[j][i][j2][i2] = foo.template convert_to<double>(); + localHessian[i][j][i2][j2] = localHessian[j][i][j2][i2] = foo.template convert_to<double>(); #else - localHessian[i][j][i2][j2] = localHessian[j][i][j2][i2] = foo; + localHessian[i][j][i2][j2] = localHessian[j][i][j2][i2] = foo; #endif - } - } } + } } + } } @@ -401,7 +401,7 @@ void compareMatrices(const Matrix<FieldMatrix<double,N,N> >& matrixA, std::strin if (relDifference > 1) std::cout << i << ", " << j << " " << ii << ", " << jj - << ", " << nameA << ": " << valueA << ", " << nameB << ": " << valueB << std::endl; + << ", " << nameA << ": " << valueA << ", " << nameB << ": " << valueB << std::endl; } } } @@ -412,184 +412,187 @@ void compareMatrices(const Matrix<FieldMatrix<double,N,N> >& matrixA, std::strin int main (int argc, char *argv[]) try { - MPIHelper::instance(argc, argv); + MPIHelper::instance(argc, argv); - typedef std::vector<TargetSpace> SolutionType; - constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; - constexpr static int blocksize = TargetSpace::TangentVector::dimension; + typedef std::vector<TargetSpace> SolutionType; + constexpr static int embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; + constexpr static int blocksize = TargetSpace::TangentVector::dimension; - // /////////////////////////////////////// - // Create the grid - // /////////////////////////////////////// - typedef YaspGrid<dim> GridType; + // /////////////////////////////////////// + // Create the grid + // /////////////////////////////////////// + typedef YaspGrid<dim> GridType; - FieldVector<double,dim> upper = {{0.38, 0.128}}; + FieldVector<double,dim> upper = {{0.38, 0.128}}; - std::array<int,dim> elements = {{5, 5}}; - GridType grid(upper, elements); + std::array<int,dim> elements = {{5, 5}}; + GridType grid(upper, elements); - typedef GridType::LeafGridView GridView; - GridView gridView = grid.leafGridView(); + typedef GridType::LeafGridView GridView; + GridView gridView = grid.leafGridView(); - typedef Functions::LagrangeBasis<GridView,1> FEBasis; - FEBasis feBasis(gridView); + typedef Functions::LagrangeBasis<GridView,1> FEBasis; + FEBasis feBasis(gridView); - // ///////////////////////////////////////// - // Read Dirichlet values - // ///////////////////////////////////////// + // ///////////////////////////////////////// + // Read Dirichlet values + // ///////////////////////////////////////// - // ////////////////////////// - // Initial iterate - // ////////////////////////// + // ////////////////////////// + // Initial iterate + // ////////////////////////// - SolutionType x(feBasis.size()); + SolutionType x(feBasis.size()); - //////////////////////////////////////////7 - // Read initial iterate from file - //////////////////////////////////////////7 + //////////////////////////////////////////7 + // Read initial iterate from file + //////////////////////////////////////////7 #if 0 - Dune::BlockVector<FieldVector<double,7> > xEmbedded(x.size()); + 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"); + 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); + GenericVector::readBinary(file, xEmbedded); - file.close(); + file.close(); - for (int ii=0; ii<x.size(); ii++) - x[ii] = xEmbedded[ii]; + for (int ii=0; ii<x.size(); ii++) + x[ii] = xEmbedded[ii]; #else - auto identity = [](const FieldVector<double,2>& x) -> FieldVector<double,3> { return {x[0], x[1], 0};}; - - std::vector<FieldVector<double,3> > v; - using namespace Functions::BasisFactory; - - auto powerBasis = makeBasis( - gridView, - power<3>( - lagrange<1>(), - blockedInterleaved() + auto identity = [](const FieldVector<double,2>& x) -> FieldVector<double,3> { + return {x[0], x[1], 0}; + }; + + std::vector<FieldVector<double,3> > v; + using namespace Functions::BasisFactory; + + auto powerBasis = makeBasis( + gridView, + power<3>( + lagrange<1>(), + blockedInterleaved() )); - Functions::interpolate(powerBasis, v, identity); + Functions::interpolate(powerBasis, v, identity); - for (size_t i=0; i<x.size(); i++) - x[i].r = v[i]; + for (size_t i=0; i<x.size(); i++) + x[i].r = v[i]; #endif - // //////////////////////////////////////////////////////////// - // Create an assembler for the energy functional - // //////////////////////////////////////////////////////////// + // //////////////////////////////////////////////////////////// + // Create an assembler for the energy functional + // //////////////////////////////////////////////////////////// - ParameterTree materialParameters; - materialParameters["thickness"] = "1"; - materialParameters["mu"] = "1"; - materialParameters["lambda"] = "1"; - materialParameters["mu_c"] = "1"; - materialParameters["L_c"] = "1"; - materialParameters["q"] = "2"; - materialParameters["kappa"] = "1"; - materialParameters["b1"] = "1"; - materialParameters["b2"] = "1"; - materialParameters["b3"] = "1"; + ParameterTree materialParameters; + materialParameters["thickness"] = "1"; + materialParameters["mu"] = "1"; + materialParameters["lambda"] = "1"; + materialParameters["mu_c"] = "1"; + materialParameters["L_c"] = "1"; + materialParameters["q"] = "2"; + materialParameters["kappa"] = "1"; + materialParameters["b1"] = "1"; + materialParameters["b2"] = "1"; + materialParameters["b3"] = "1"; - /////////////////////////////////////////////////////////////////////// - // Assemblers for the Euclidean derivatives in an embedding space - /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + // Assemblers for the Euclidean derivatives in an embedding space + /////////////////////////////////////////////////////////////////////// - // Assembler using ADOL-C - CosseratEnergyLocalStiffness<FEBasis, - 3,adouble> cosseratEnergyADOLCLocalStiffness(materialParameters, nullptr, nullptr, nullptr); + // Assembler using ADOL-C + CosseratEnergyLocalStiffness<FEBasis, + 3,adouble> cosseratEnergyADOLCLocalStiffness(materialParameters, nullptr, nullptr, nullptr); - LocalADOLCStiffness<FEBasis> localADOLCStiffness(&cosseratEnergyADOLCLocalStiffness); + LocalADOLCStiffness<FEBasis> localADOLCStiffness(&cosseratEnergyADOLCLocalStiffness); - CosseratEnergyLocalStiffness<FEBasis, - 3,FDType> cosseratEnergyFDLocalStiffness(materialParameters, nullptr, nullptr, nullptr); + CosseratEnergyLocalStiffness<FEBasis, + 3,FDType> cosseratEnergyFDLocalStiffness(materialParameters, nullptr, nullptr, nullptr); - LocalFDStiffness<FEBasis,FDType> localFDStiffness(&cosseratEnergyFDLocalStiffness); + LocalFDStiffness<FEBasis,FDType> localFDStiffness(&cosseratEnergyFDLocalStiffness); - /////////////////////////////////////////////////////////////////////// - // Assemblers for the Riemannian derivatives without embedding space - /////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////// + // Assemblers for the Riemannian derivatives without embedding space + /////////////////////////////////////////////////////////////////////// - // Assembler using ADOL-C - LocalGeodesicFEADOLCStiffness<FEBasis, - TargetSpace> localGFEADOLCStiffness(&cosseratEnergyADOLCLocalStiffness); + // Assembler using ADOL-C + LocalGeodesicFEADOLCStiffness<FEBasis, + TargetSpace> localGFEADOLCStiffness(&cosseratEnergyADOLCLocalStiffness); - LocalGeodesicFEFDStiffness<FEBasis, - TargetSpace, - FDType> localGFEFDStiffness(&cosseratEnergyFDLocalStiffness); + LocalGeodesicFEFDStiffness<FEBasis, + TargetSpace, + FDType> localGFEFDStiffness(&cosseratEnergyFDLocalStiffness); - // Compute and compare matrices - for (const auto& element : Dune::elements(gridView)) - { - std::cout << " ++++ element " << gridView.indexSet().index(element) << " ++++" << std::endl; + // Compute and compare matrices + for (const auto& element : Dune::elements(gridView)) + { + std::cout << " ++++ element " << gridView.indexSet().index(element) << " ++++" << std::endl; - auto localView = feBasis.localView(); - localView.bind(element); + auto localView = feBasis.localView(); + localView.bind(element); - const int numOfBaseFct = localView.size(); + const int numOfBaseFct = localView.size(); - // Extract local configuration - std::vector<TargetSpace> localSolution(numOfBaseFct); + // Extract local configuration + std::vector<TargetSpace> localSolution(numOfBaseFct); - for (int i=0; i<numOfBaseFct; i++) - localSolution[i] = x[localView.index(i)]; + for (int i=0; i<numOfBaseFct; i++) + localSolution[i] = x[localView.index(i)]; - std::vector<Dune::FieldVector<double,embeddedBlocksize> > localADGradient(numOfBaseFct); - std::vector<Dune::FieldVector<double,embeddedBlocksize> > localADVMGradient(numOfBaseFct); // VM: vector-mode - std::vector<Dune::FieldVector<double,embeddedBlocksize> > localFDGradient(numOfBaseFct); + std::vector<Dune::FieldVector<double,embeddedBlocksize> > localADGradient(numOfBaseFct); + std::vector<Dune::FieldVector<double,embeddedBlocksize> > localADVMGradient(numOfBaseFct); // VM: vector-mode + std::vector<Dune::FieldVector<double,embeddedBlocksize> > localFDGradient(numOfBaseFct); - Matrix<FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> > localADHessian; - Matrix<FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> > localADVMHessian; // VM: vector-mode - Matrix<FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> > localFDHessian; + Matrix<FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> > localADHessian; + Matrix<FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> > localADVMHessian; // VM: vector-mode + Matrix<FieldMatrix<double,embeddedBlocksize,embeddedBlocksize> > localFDHessian; - // Assemble Euclidean derivatives - localADOLCStiffness.assembleGradientAndHessian(localView, - localSolution, - localADGradient, - localADHessian, - false); // 'true' means 'vector mode' + // Assemble Euclidean derivatives + localADOLCStiffness.assembleGradientAndHessian(localView, + localSolution, + localADGradient, + localADHessian, + false); // 'true' means 'vector mode' - localADOLCStiffness.assembleGradientAndHessian(localView, - localSolution, - localADGradient, - localADVMHessian, - true); // 'true' means 'vector mode' + localADOLCStiffness.assembleGradientAndHessian(localView, + localSolution, + localADGradient, + localADVMHessian, + true); // 'true' means 'vector mode' - localFDStiffness.assembleGradientAndHessian(localView, - localSolution, - localFDGradient, - localFDHessian); + localFDStiffness.assembleGradientAndHessian(localView, + localSolution, + localFDGradient, + localFDHessian); - // compare - compareMatrices(localADHessian, "AD", localFDHessian, "FD"); - compareMatrices(localADHessian, "AD scalar", localADVMHessian, "AD vector"); + // compare + compareMatrices(localADHessian, "AD", localFDHessian, "FD"); + compareMatrices(localADHessian, "AD scalar", localADVMHessian, "AD vector"); - // Assemble Riemannian derivatives - std::vector<Dune::FieldVector<double,blocksize> > localRiemannianADGradient(numOfBaseFct); - std::vector<Dune::FieldVector<double,blocksize> > localRiemannianFDGradient(numOfBaseFct); + // Assemble Riemannian derivatives + std::vector<Dune::FieldVector<double,blocksize> > localRiemannianADGradient(numOfBaseFct); + std::vector<Dune::FieldVector<double,blocksize> > localRiemannianFDGradient(numOfBaseFct); - Matrix<FieldMatrix<double,blocksize,blocksize> > localRiemannianADHessian; - Matrix<FieldMatrix<double,blocksize,blocksize> > localRiemannianFDHessian; + Matrix<FieldMatrix<double,blocksize,blocksize> > localRiemannianADHessian; + Matrix<FieldMatrix<double,blocksize,blocksize> > localRiemannianFDHessian; - localGFEADOLCStiffness.assembleGradientAndHessian(localView, - localSolution, - localRiemannianADGradient); + localGFEADOLCStiffness.assembleGradientAndHessian(localView, + localSolution, + localRiemannianADGradient); - localGFEFDStiffness.assembleGradientAndHessian(localView, - localSolution, - localRiemannianFDGradient); + localGFEFDStiffness.assembleGradientAndHessian(localView, + localSolution, + localRiemannianFDGradient); - // compare - compareMatrices(localGFEADOLCStiffness.A_, "Riemannian AD", localGFEFDStiffness.A_, "Riemannian FD"); + // compare + compareMatrices(localGFEADOLCStiffness.A_, "Riemannian AD", localGFEFDStiffness.A_, "Riemannian FD"); - } + } - // ////////////////////////////// - } catch (Exception& e) { + // ////////////////////////////// +} +catch (Exception& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << std::endl; - } +} diff --git a/test/averagedistanceassemblertest.cc b/test/averagedistanceassemblertest.cc index c106a08a..0dcb7ccf 100644 --- a/test/averagedistanceassemblertest.cc +++ b/test/averagedistanceassemblertest.cc @@ -25,73 +25,73 @@ void assembleGradientAndHessianApproximation(const DistanceAssembler& assembler, typename TargetSpace::TangentVector& gradient, FieldMatrix<double, TargetSpace::TangentVector::dimension, TargetSpace::TangentVector::dimension>& hesseMatrix) { - using field_type = typename TargetSpace::field_type; - constexpr auto blocksize = TargetSpace::TangentVector::dimension; - constexpr auto embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; - - const field_type eps = 1e-4; - - FieldMatrix<double,blocksize,embeddedBlocksize> B = argument.orthonormalFrame(); - - // 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 = -assembler.value(argument); - - // Precompute energy infinitesimal corrections in the directions of the local basis vectors - std::array<field_type,blocksize> forwardEnergy; - std::array<field_type,blocksize> backwardEnergy; - - for (size_t i2=0; i2<blocksize; i2++) + using field_type = typename TargetSpace::field_type; + constexpr auto blocksize = TargetSpace::TangentVector::dimension; + constexpr auto embeddedBlocksize = TargetSpace::EmbeddedTangentVector::dimension; + + const field_type eps = 1e-4; + + FieldMatrix<double,blocksize,embeddedBlocksize> B = argument.orthonormalFrame(); + + // 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 = -assembler.value(argument); + + // Precompute energy infinitesimal corrections in the directions of the local basis vectors + std::array<field_type,blocksize> forwardEnergy; + std::array<field_type,blocksize> backwardEnergy; + + for (size_t i2=0; i2<blocksize; i2++) + { + typename TargetSpace::EmbeddedTangentVector epsXi = B[i2]; + epsXi *= eps; + typename TargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; + minusEpsXi *= -1; + + TargetSpace forwardSolution = argument; + TargetSpace backwardSolution = argument; + + forwardSolution = TargetSpace::exp(argument,epsXi); + backwardSolution = TargetSpace::exp(argument,minusEpsXi); + + forwardEnergy[i2] = assembler.value(forwardSolution); + backwardEnergy[i2] = assembler.value(backwardSolution); + } + + ////////////////////////////////////////////////////////////// + // Compute gradient by finite-difference approximation + ////////////////////////////////////////////////////////////// + + for (int j=0; j<blocksize; j++) + gradient[j] = (forwardEnergy[j] - backwardEnergy[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. + /////////////////////////////////////////////////////////////////////////// + for (size_t i2=0; i2<blocksize; i2++) + { + for (size_t j2=0; j2<i2+1; j2++) { - typename TargetSpace::EmbeddedTangentVector epsXi = B[i2]; - epsXi *= eps; - typename TargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; - minusEpsXi *= -1; + TargetSpace forwardSolutionXiEta = argument; + TargetSpace backwardSolutionXiEta = argument; - TargetSpace forwardSolution = argument; - TargetSpace backwardSolution = argument; + typename TargetSpace::EmbeddedTangentVector epsXi = B[i2]; epsXi *= eps; + typename TargetSpace::EmbeddedTangentVector epsEta = B[j2]; epsEta *= eps; - forwardSolution = TargetSpace::exp(argument,epsXi); - backwardSolution = TargetSpace::exp(argument,minusEpsXi); + typename TargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; minusEpsXi *= -1; + typename TargetSpace::EmbeddedTangentVector minusEpsEta = epsEta; minusEpsEta *= -1; - forwardEnergy[i2] = assembler.value(forwardSolution); - backwardEnergy[i2] = assembler.value(backwardSolution); - } - - ////////////////////////////////////////////////////////////// - // Compute gradient by finite-difference approximation - ////////////////////////////////////////////////////////////// - - for (int j=0; j<blocksize; j++) - gradient[j] = (forwardEnergy[j] - backwardEnergy[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. - /////////////////////////////////////////////////////////////////////////// - for (size_t i2=0; i2<blocksize; i2++) - { - for (size_t j2=0; j2<i2+1; j2++) - { - TargetSpace forwardSolutionXiEta = argument; - TargetSpace backwardSolutionXiEta = argument; + forwardSolutionXiEta = TargetSpace::exp(argument, epsXi+epsEta); + backwardSolutionXiEta = TargetSpace::exp(argument, minusEpsXi+minusEpsEta); - typename TargetSpace::EmbeddedTangentVector epsXi = B[i2]; epsXi *= eps; - typename TargetSpace::EmbeddedTangentVector epsEta = B[j2]; epsEta *= eps; + field_type forwardValue = assembler.value(forwardSolutionXiEta) - forwardEnergy[i2] - forwardEnergy[j2]; + field_type backwardValue = assembler.value(backwardSolutionXiEta) - backwardEnergy[i2] - backwardEnergy[j2]; - typename TargetSpace::EmbeddedTangentVector minusEpsXi = epsXi; minusEpsXi *= -1; - typename TargetSpace::EmbeddedTangentVector minusEpsEta = epsEta; minusEpsEta *= -1; - - forwardSolutionXiEta = TargetSpace::exp(argument, epsXi+epsEta); - backwardSolutionXiEta = TargetSpace::exp(argument, minusEpsXi+minusEpsEta); - - field_type forwardValue = assembler.value(forwardSolutionXiEta) - forwardEnergy[i2] - forwardEnergy[j2]; - field_type backwardValue = assembler.value(backwardSolutionXiEta) - backwardEnergy[i2] - backwardEnergy[j2]; - - hesseMatrix[i2][j2] = hesseMatrix[j2][i2] = 0.5 * (forwardValue - 2*centerValue + backwardValue) / (eps*eps); - } + hesseMatrix[i2][j2] = hesseMatrix[j2][i2] = 0.5 * (forwardValue - 2*centerValue + backwardValue) / (eps*eps); } + } } @@ -99,160 +99,160 @@ void assembleGradientAndHessianApproximation(const DistanceAssembler& assembler, /** \brief Test whether interpolation is invariant under permutation of the simplex vertices */ template <class TargetSpace> -void testPoint(const std::vector<TargetSpace>& corners, +void testPoint(const std::vector<TargetSpace>& corners, const std::vector<double>& weights, const TargetSpace& argument) { - // create the assembler - AverageDistanceAssembler<TargetSpace> assembler(corners, weights); - - // test the functional - double value = assembler.value(argument); - assert(!std::isnan(value)); - assert(value >= 0); - - // test the gradient - typename TargetSpace::TangentVector gradient; - assembler.assembleGradient(argument, gradient); - typename TargetSpace::TangentVector gradientApproximation; - - // test the hessian - FieldMatrix<double, TargetSpace::TangentVector::dimension, TargetSpace::TangentVector::dimension> hessian; - FieldMatrix<double, TargetSpace::TangentVector::dimension, TargetSpace::TangentVector::dimension> hessianApproximation(0); - - assembler.assembleHessian(argument, hessian); - assembleGradientAndHessianApproximation(assembler, argument, - gradientApproximation, hessianApproximation); - - // Check gradient - for (size_t i=0; i<gradient.size(); i++) + // create the assembler + AverageDistanceAssembler<TargetSpace> assembler(corners, weights); + + // test the functional + double value = assembler.value(argument); + assert(!std::isnan(value)); + assert(value >= 0); + + // test the gradient + typename TargetSpace::TangentVector gradient; + assembler.assembleGradient(argument, gradient); + typename TargetSpace::TangentVector gradientApproximation; + + // test the hessian + FieldMatrix<double, TargetSpace::TangentVector::dimension, TargetSpace::TangentVector::dimension> hessian; + FieldMatrix<double, TargetSpace::TangentVector::dimension, TargetSpace::TangentVector::dimension> hessianApproximation(0); + + assembler.assembleHessian(argument, hessian); + assembleGradientAndHessianApproximation(assembler, argument, + gradientApproximation, hessianApproximation); + + // Check gradient + for (size_t i=0; i<gradient.size(); i++) + { + if (std::isnan(gradient[i])) + DUNE_THROW(Dune::Exception, "Gradient contains NaN"); + if (std::isnan(gradientApproximation[i])) + DUNE_THROW(Dune::Exception, "Gradient approximation contains NaN"); + if (std::abs(gradient[i] - gradientApproximation[i]) > 1e-6) + DUNE_THROW(Dune::Exception, "Gradient and its approximation do not match"); + } + + // Check Hesse matrix + for (size_t i=0; i<hessian.N(); i++) + for (size_t j=0; j<hessian.M(); j++) { - if (std::isnan(gradient[i])) - DUNE_THROW(Dune::Exception, "Gradient contains NaN"); - if (std::isnan(gradientApproximation[i])) - DUNE_THROW(Dune::Exception, "Gradient approximation contains NaN"); - if (std::abs(gradient[i] - gradientApproximation[i]) > 1e-6) - DUNE_THROW(Dune::Exception, "Gradient and its approximation do not match"); + if (std::isnan(hessian[i][j])) + DUNE_THROW(Dune::Exception, "Hesse matrix contains NaN"); + if (std::isnan(hessianApproximation[i][j])) + DUNE_THROW(Dune::Exception, "Hesse matrix approximation contains NaN"); + if (std::abs(hessian[i][j] - hessianApproximation[i][j]) > 1e-6) + DUNE_THROW(Dune::Exception, "Hesse matrix and its approximation do not match"); } - // Check Hesse matrix - for (size_t i=0; i<hessian.N(); i++) - for (size_t j=0; j<hessian.M(); j++) - { - if (std::isnan(hessian[i][j])) - DUNE_THROW(Dune::Exception, "Hesse matrix contains NaN"); - if (std::isnan(hessianApproximation[i][j])) - DUNE_THROW(Dune::Exception, "Hesse matrix approximation contains NaN"); - if (std::abs(hessian[i][j] - hessianApproximation[i][j]) > 1e-6) - DUNE_THROW(Dune::Exception, "Hesse matrix and its approximation do not match"); - } - } template <class TargetSpace> -void testWeightSet(const std::vector<TargetSpace>& corners, +void testWeightSet(const std::vector<TargetSpace>& corners, const TargetSpace& argument) { - // A quadrature rule as a set of test points - int quadOrder = 3; - - const auto& quad = QuadratureRules<double, dim>::rule(GeometryTypes::simplex(dim), quadOrder); - - for (size_t pt=0; pt<quad.size(); pt++) { - - const Dune::FieldVector<double,dim>& quadPos = quad[pt].position(); - - // local to barycentric coordinates - std::vector<double> weights(dim+1); - weights[0] = 1; - for (int i=0; i<dim; i++) { - weights[0] -= quadPos[i]; - weights[i+1] = quadPos[i]; - } - - testPoint(corners, weights, argument); + // A quadrature rule as a set of test points + int quadOrder = 3; + + const auto& quad = QuadratureRules<double, dim>::rule(GeometryTypes::simplex(dim), quadOrder); + for (size_t pt=0; pt<quad.size(); pt++) { + + const Dune::FieldVector<double,dim>& quadPos = quad[pt].position(); + + // local to barycentric coordinates + std::vector<double> weights(dim+1); + weights[0] = 1; + for (int i=0; i<dim; i++) { + weights[0] -= quadPos[i]; + weights[i+1] = quadPos[i]; } + testPoint(corners, weights, argument); + + } + } void testRealTuples() { - typedef RealTuple<double,1> TargetSpace; - - std::vector<TargetSpace> corners = {TargetSpace(1), - TargetSpace(2), - TargetSpace(3)}; - - TargetSpace argument = corners[0]; - testWeightSet(corners, argument); - argument = corners[1]; - testWeightSet(corners, argument); - argument = corners[2]; - testWeightSet(corners, argument); + typedef RealTuple<double,1> TargetSpace; + + std::vector<TargetSpace> corners = {TargetSpace(1), + TargetSpace(2), + TargetSpace(3)}; + + TargetSpace argument = corners[0]; + testWeightSet(corners, argument); + argument = corners[1]; + testWeightSet(corners, argument); + argument = corners[2]; + testWeightSet(corners, argument); } void testUnitVectors() { - typedef UnitVector<double,3> TargetSpace; + typedef UnitVector<double,3> TargetSpace; - std::vector<TargetSpace> corners(dim+1); + std::vector<TargetSpace> corners(dim+1); - corners[0] = {1,0,0}; - corners[1] = {0,1,0}; - corners[2] = {0,0,1}; + corners[0] = {1,0,0}; + corners[1] = {0,1,0}; + corners[2] = {0,0,1}; - TargetSpace argument = corners[0]; - testWeightSet(corners, argument); - argument = corners[1]; - testWeightSet(corners, argument); - argument = corners[2]; - testWeightSet(corners, argument); + TargetSpace argument = corners[0]; + testWeightSet(corners, argument); + argument = corners[1]; + testWeightSet(corners, argument); + argument = corners[2]; + testWeightSet(corners, argument); } void testRotations() { - typedef Rotation<double,3> TargetSpace; - - std::vector<TargetSpace> corners(dim+1); - corners[0] = Rotation<double,3>({1,0,0}, 0.1); - corners[1] = Rotation<double,3>({0,1,0}, 0.1); - corners[2] = Rotation<double,3>({0,0,1}, 0.1); - - TargetSpace argument = corners[0]; - testWeightSet(corners, argument); - argument = corners[1]; - testWeightSet(corners, argument); - argument = corners[2]; - testWeightSet(corners, argument); + typedef Rotation<double,3> TargetSpace; + + std::vector<TargetSpace> corners(dim+1); + corners[0] = Rotation<double,3>({1,0,0}, 0.1); + corners[1] = Rotation<double,3>({0,1,0}, 0.1); + corners[2] = Rotation<double,3>({0,0,1}, 0.1); + + TargetSpace argument = corners[0]; + testWeightSet(corners, argument); + argument = corners[1]; + testWeightSet(corners, argument); + argument = corners[2]; + testWeightSet(corners, argument); } void testProductManifold() { - typedef Dune::GFE::ProductManifold<RealTuple<double,5>,UnitVector<double,3>, Rotation<double,3>> TargetSpace; + typedef Dune::GFE::ProductManifold<RealTuple<double,5>,UnitVector<double,3>, Rotation<double,3> > TargetSpace; - std::vector<TargetSpace> corners(dim+1); + std::vector<TargetSpace> corners(dim+1); - std::generate(corners.begin(), corners.end(), []() { - return Dune::GFE::randomFieldVector<typename TargetSpace::field_type,TargetSpace::CoordinateType::dimension>(0.9,1.1); - }); + std::generate(corners.begin(), corners.end(), []() { + return Dune::GFE::randomFieldVector<typename TargetSpace::field_type,TargetSpace::CoordinateType::dimension>(0.9,1.1); + }); - TargetSpace argument = corners[0]; - testWeightSet(corners, argument); - argument = corners[1]; - testWeightSet(corners, argument); - argument = corners[2]; - testWeightSet(corners, argument); + TargetSpace argument = corners[0]; + testWeightSet(corners, argument); + argument = corners[1]; + testWeightSet(corners, argument); + argument = corners[2]; + testWeightSet(corners, argument); } int main() { - testRealTuples(); - testUnitVectors(); - testRotations(); - testProductManifold(); + testRealTuples(); + testUnitVectors(); + testRotations(); + testProductManifold(); } diff --git a/test/cosseratcontinuumtest.cc b/test/cosseratcontinuumtest.cc index 8bcf1a00..54f8dd91 100644 --- a/test/cosseratcontinuumtest.cc +++ b/test/cosseratcontinuumtest.cc @@ -62,14 +62,14 @@ int main (int argc, char *argv[]) // /////////////////////////////////////// using GridType = UGGrid<dim>; - //Create a grid with 2 elements, one element will get refined to create different element types, the other one stays a cube + //Create a grid with 2 elements, one element will get refined to create different element types, the other one stays a cube std::shared_ptr<GridType> grid = StructuredGridFactory<GridType>::createCubeGrid({0,0,0}, {1,1,1}, {2,1,1}); // Refine once - for (auto&& e : elements(grid->leafGridView())){ + for (auto&& e : elements(grid->leafGridView())) { bool refineThisElement = false; for (int i = 0; i < e.geometry().corners(); i++) { - refineThisElement = refineThisElement || (e.geometry().corner(i)[0] > 0.99); + refineThisElement = refineThisElement || (e.geometry().corner(i)[0] > 0.99); } grid->mark(refineThisElement ? 1 : 0, e); } @@ -90,12 +90,12 @@ int main (int argc, char *argv[]) gridView, composite( power<dim>( - lagrange<displacementOrder>() - ), + lagrange<displacementOrder>() + ), power<dimRotation>( - lagrange<rotationOrder>() - ) - )); + lagrange<rotationOrder>() + ) + )); using CompositeBasis = decltype(compositeBasis); using DeformationFEBasis = Functions::LagrangeBasis<GridView,displacementOrder>; @@ -112,18 +112,22 @@ int main (int argc, char *argv[]) gridView, composite( power<dim>( - lagrange<displacementOrder>() - ), + lagrange<displacementOrder>() + ), power<dim>( - lagrange<rotationOrder>() - ) - )); + lagrange<rotationOrder>() + ) + )); auto deformationPowerBasis = Functions::subspaceBasis(identityBasis, _0); auto rotationPowerBasis = Functions::subspaceBasis(identityBasis, _1); - MultiTypeBlockVector<BlockVector<FieldVector<double,dim>>,BlockVector<FieldVector<double,dim>>> identity; - Functions::interpolate(deformationPowerBasis, identity, [&](FieldVector<double,dim> x){ return x;}); - Functions::interpolate(rotationPowerBasis, identity, [&](FieldVector<double,dim> x){ return x;}); + MultiTypeBlockVector<BlockVector<FieldVector<double,dim> >,BlockVector<FieldVector<double,dim> > > identity; + Functions::interpolate(deformationPowerBasis, identity, [&](FieldVector<double,dim> x){ + return x; + }); + Functions::interpolate(rotationPowerBasis, identity, [&](FieldVector<double,dim> x){ + return x; + }); BitSetVector<dim> deformationDirichletDofs(deformationFEBasis.size(), false); BitSetVector<dim> orientationDirichletDofs(orientationFEBasis.size(), false); @@ -132,40 +136,40 @@ int main (int argc, char *argv[]) // Make predicate function that computes which vertices are on the Dirichlet boundary, based on the vertex positions. auto isDirichlet = [](FieldVector<double,dim> coordinate) - { - return coordinate[0] < 0.01; - }; + { + return coordinate[0] < 0.01; + }; for (size_t i=0; i<deformationFEBasis.size(); i++) { - bool isDirichletDeformation = isDirichlet(identity[_0][i]); - for (size_t j=0; j<dim; j++) - deformationDirichletDofs[i][j] = isDirichletDeformation; + bool isDirichletDeformation = isDirichlet(identity[_0][i]); + for (size_t j=0; j<dim; j++) + deformationDirichletDofs[i][j] = isDirichletDeformation; } for (size_t i=0; i<orientationFEBasis.size(); i++) { - bool isDirichletOrientation = isDirichlet(identity[_1][i]); - for (size_t j=0; j<dim; j++) - orientationDirichletDofs[i][j] = isDirichletOrientation; + bool isDirichletOrientation = isDirichlet(identity[_1][i]); + for (size_t j=0; j<dim; j++) + orientationDirichletDofs[i][j] = isDirichletOrientation; } // ///////////////////////////////////////// // Determine Neumann dofs and values - // ///////////////////////////////////////// + // ///////////////////////////////////////// std::function<bool(FieldVector<double,dim>)> isNeumann = [](FieldVector<double,dim> coordinate) { - return coordinate[0] > 0.99; - }; + return coordinate[0] > 0.99; + }; BitSetVector<1> neumannVertices(gridView.size(dim), false); - + for (auto&& vertex : vertices(gridView)) - neumannVertices[indexSet.index(vertex)] = isNeumann(vertex.geometry().corner(0)); + neumannVertices[indexSet.index(vertex)] = isNeumann(vertex.geometry().corner(0)); - auto neumannBoundary = std::make_shared<BoundaryPatch<GridType::LeafGridView>>(gridView, neumannVertices); + auto neumannBoundary = std::make_shared<BoundaryPatch<GridType::LeafGridView> >(gridView, neumannVertices); FieldVector<double,dim> values_ = {0,0,0}; auto neumannFunction = [&](FieldVector<double, dim>){ - return values_; - }; + return values_; + }; // ////////////////////////// // Initial iterate @@ -197,39 +201,39 @@ int main (int argc, char *argv[]) // //////////////////////////// GFE::SumEnergy<CompositeBasis, RealTuple<adouble,dim>,Rotation<adouble,dim> > sumEnergy; - auto neumannEnergy = std::make_shared<GFE::NeumannEnergy<CompositeBasis, RealTuple<adouble,dim>, Rotation<adouble,dim>>>(neumannBoundary,neumannFunction); - auto bulkCosseratDensity = std::make_shared<GFE::BulkCosseratDensity<adouble,double>>(parameters); - auto bulkCosseratEnergy = std::make_shared<GFE::LocalIntegralEnergy<CompositeBasis, RealTuple<adouble,dim>, Rotation<adouble,dim>>>(bulkCosseratDensity); + auto neumannEnergy = std::make_shared<GFE::NeumannEnergy<CompositeBasis, RealTuple<adouble,dim>, Rotation<adouble,dim> > >(neumannBoundary,neumannFunction); + auto bulkCosseratDensity = std::make_shared<GFE::BulkCosseratDensity<adouble,double> >(parameters); + auto bulkCosseratEnergy = std::make_shared<GFE::LocalIntegralEnergy<CompositeBasis, RealTuple<adouble,dim>, Rotation<adouble,dim> > >(bulkCosseratDensity); sumEnergy.addLocalEnergy(bulkCosseratEnergy); sumEnergy.addLocalEnergy(neumannEnergy); MixedLocalGFEADOLCStiffness<CompositeBasis, - RealTuple<double,dim>, - Rotation<double,dim> > localGFEADOLCStiffness(&sumEnergy); + RealTuple<double,dim>, + Rotation<double,dim> > localGFEADOLCStiffness(&sumEnergy); MixedGFEAssembler<CompositeBasis, - RealTuple<double,dim>, - Rotation<double,dim> > mixedAssembler(compositeBasis, &localGFEADOLCStiffness); - + RealTuple<double,dim>, + Rotation<double,dim> > mixedAssembler(compositeBasis, &localGFEADOLCStiffness); + MixedRiemannianTrustRegionSolver<GridType, - CompositeBasis, - DeformationFEBasis, RealTuple<double,dim>, - OrientationFEBasis, Rotation<double,dim> > solver; + CompositeBasis, + DeformationFEBasis, RealTuple<double,dim>, + OrientationFEBasis, Rotation<double,dim> > solver; solver.setup(*grid, - &mixedAssembler, - deformationFEBasis, - orientationFEBasis, - x, - deformationDirichletDofs, - orientationDirichletDofs, - tolerance, - maxSolverSteps, - initialTrustRegionRadius, - multigridIterations, - mgTolerance, - 3, 3, 1, // Multigrid V-cycle - baseIterations, - baseTolerance, - false); + &mixedAssembler, + deformationFEBasis, + orientationFEBasis, + x, + deformationDirichletDofs, + orientationDirichletDofs, + tolerance, + maxSolverSteps, + initialTrustRegionRadius, + multigridIterations, + mgTolerance, + 3, 3, 1, // Multigrid V-cycle + baseIterations, + baseTolerance, + false); solver.setInitialIterate(x); solver.solve(); @@ -243,17 +247,17 @@ int main (int argc, char *argv[]) if (solver.getStatistics().finalIteration != expectedFinalIteration) { - std::cerr << "Trust-region solver did " << solver.getStatistics().finalIteration+1 - << " iterations, instead of the expected '" << expectedFinalIteration+1 << "'!" << std::endl; - return 1; + std::cerr << "Trust-region solver did " << solver.getStatistics().finalIteration+1 + << " iterations, instead of the expected '" << expectedFinalIteration+1 << "'!" << std::endl; + return 1; } if ( std::abs(solver.getStatistics().finalEnergy - expectedEnergy) > 1e-7) { - std::cerr << std::setprecision(9); - std::cerr << "Final energy is " << solver.getStatistics().finalEnergy - << " but '" << expectedEnergy << "' was expected!" << std::endl; - return 1; + std::cerr << std::setprecision(9); + std::cerr << "Final energy is " << solver.getStatistics().finalEnergy + << " but '" << expectedEnergy << "' was expected!" << std::endl; + return 1; } return 0; diff --git a/test/cosseratenergytest.cc b/test/cosseratenergytest.cc index 85fc3b25..f9348a2f 100644 --- a/test/cosseratenergytest.cc +++ b/test/cosseratenergytest.cc @@ -25,24 +25,24 @@ using namespace Dune; template <class GridType> std::unique_ptr<GridType> makeSingleSimplexGrid() { - static const int domainDim = GridType::dimension; - GridFactory<GridType> factory; + static const int domainDim = GridType::dimension; + GridFactory<GridType> factory; - FieldVector<double,domainDim> pos(0); - factory.insertVertex(pos); + FieldVector<double,domainDim> pos(0); + factory.insertVertex(pos); - for (int i=0; i<domainDim; i++) { - pos = 0; - pos[i] = 1; - factory.insertVertex(pos); - } + for (int i=0; i<domainDim; i++) { + pos = 0; + pos[i] = 1; + factory.insertVertex(pos); + } - std::vector<unsigned int> v(domainDim+1); - for (int i=0; i<domainDim+1; i++) - v[i] = i; - factory.insertElement(GeometryTypes::simplex(domainDim), v); + std::vector<unsigned int> v(domainDim+1); + for (int i=0; i<domainDim+1; i++) + v[i] = i; + factory.insertElement(GeometryTypes::simplex(domainDim), v); - return factory.createGrid(); + return factory.createGrid(); } @@ -53,121 +53,121 @@ std::unique_ptr<GridType> makeSingleSimplexGrid() template <class GridType> void testEnergy(const GridType* grid, const std::vector<TargetSpace>& coefficients) { - ParameterTree materialParameters; - materialParameters["thickness"] = "0.1"; - materialParameters["mu"] = "3.8462e+05"; - materialParameters["lambda"] = "2.7149e+05"; - materialParameters["mu_c"] = "3.8462e+05"; - materialParameters["L_c"] = "0.1"; - materialParameters["q"] = "2.5"; - materialParameters["kappa"] = "0.1"; - materialParameters["b1"] = "1"; - materialParameters["b2"] = "1"; - materialParameters["b3"] = "1"; - - typedef Dune::Functions::LagrangeBasis<typename GridType::LeafGridView,1> FEBasis; - FEBasis feBasis(grid->leafGridView()); - - CosseratEnergyLocalStiffness<FEBasis,3> assembler(materialParameters, - nullptr, - nullptr, - nullptr); - - // compute reference energy - auto localView = feBasis.localView(); - localView.bind(*grid->leafGridView().template begin<0>()); - - double referenceEnergy = assembler.energy(localView, - coefficients); - - // rotate the entire configuration - std::vector<TargetSpace> rotatedCoefficients(coefficients.size()); - - std::vector<Rotation<double,3> > testRotations; - ValueFactory<Rotation<double,3> >::get(testRotations); - - for (size_t i=0; i<testRotations.size(); i++) { - - ///////////////////////////////////////////////////////////////////////// - // Multiply the given configuration by the test rotation. - // The energy should remain unchanged. - ///////////////////////////////////////////////////////////////////////// - FieldMatrix<double,3,3> matrix; - testRotations[i].matrix(matrix); - - for (size_t j=0; j<coefficients.size(); j++) { - FieldVector<double,3> tmp; - matrix.mv(coefficients[j].r, tmp); - rotatedCoefficients[j].r = tmp; - - rotatedCoefficients[j].q = testRotations[i].mult(coefficients[j].q); - } - - double energy = assembler.energy(localView, - rotatedCoefficients); - assert(std::fabs(energy-referenceEnergy)/std::fabs(energy) < 1e-4); - + ParameterTree materialParameters; + materialParameters["thickness"] = "0.1"; + materialParameters["mu"] = "3.8462e+05"; + materialParameters["lambda"] = "2.7149e+05"; + materialParameters["mu_c"] = "3.8462e+05"; + materialParameters["L_c"] = "0.1"; + materialParameters["q"] = "2.5"; + materialParameters["kappa"] = "0.1"; + materialParameters["b1"] = "1"; + materialParameters["b2"] = "1"; + materialParameters["b3"] = "1"; + + typedef Dune::Functions::LagrangeBasis<typename GridType::LeafGridView,1> FEBasis; + FEBasis feBasis(grid->leafGridView()); + + CosseratEnergyLocalStiffness<FEBasis,3> assembler(materialParameters, + nullptr, + nullptr, + nullptr); + + // compute reference energy + auto localView = feBasis.localView(); + localView.bind(*grid->leafGridView().template begin<0>()); + + double referenceEnergy = assembler.energy(localView, + coefficients); + + // rotate the entire configuration + std::vector<TargetSpace> rotatedCoefficients(coefficients.size()); + + std::vector<Rotation<double,3> > testRotations; + ValueFactory<Rotation<double,3> >::get(testRotations); + + for (size_t i=0; i<testRotations.size(); i++) { + + ///////////////////////////////////////////////////////////////////////// + // Multiply the given configuration by the test rotation. + // The energy should remain unchanged. + ///////////////////////////////////////////////////////////////////////// + FieldMatrix<double,3,3> matrix; + testRotations[i].matrix(matrix); + + for (size_t j=0; j<coefficients.size(); j++) { + FieldVector<double,3> tmp; + matrix.mv(coefficients[j].r, tmp); + rotatedCoefficients[j].r = tmp; + + rotatedCoefficients[j].q = testRotations[i].mult(coefficients[j].q); } + double energy = assembler.energy(localView, + rotatedCoefficients); + assert(std::fabs(energy-referenceEnergy)/std::fabs(energy) < 1e-4); + + } + } template <int domainDim> void testFrameInvariance() { - std::cout << " --- Testing frame invariance of the Cosserat energy, domain dimension: " << domainDim << " ---" << std::endl; + std::cout << " --- Testing frame invariance of the Cosserat energy, domain dimension: " << domainDim << " ---" << std::endl; - // //////////////////////////////////////////////////////// - // Make a test grid consisting of a single simplex - // //////////////////////////////////////////////////////// + // //////////////////////////////////////////////////////// + // Make a test grid consisting of a single simplex + // //////////////////////////////////////////////////////// - typedef UGGrid<domainDim> GridType; - const std::unique_ptr<GridType> grid = makeSingleSimplexGrid<GridType>(); - - // ////////////////////////////////////////////////////////// - // Test whether the energy is invariant under isometries - // ////////////////////////////////////////////////////////// + typedef UGGrid<domainDim> GridType; + const std::unique_ptr<GridType> grid = makeSingleSimplexGrid<GridType>(); - std::vector<TargetSpace> testPoints; - ValueFactory<TargetSpace>::get(testPoints); + // ////////////////////////////////////////////////////////// + // Test whether the energy is invariant under isometries + // ////////////////////////////////////////////////////////// - // Set up elements of SE(3) - std::vector<TargetSpace> coefficients(domainDim+1); + std::vector<TargetSpace> testPoints; + ValueFactory<TargetSpace>::get(testPoints); - ::MultiIndex index(domainDim+1, testPoints.size()); - int numIndices = index.cycle(); + // Set up elements of SE(3) + std::vector<TargetSpace> coefficients(domainDim+1); - for (int i=0; i<numIndices; i++, ++index) { - - // Discard all configurations that deform the element to zero area - bool identicalPoints = false; - for (int j=0; j<domainDim+1; j++) - for (int k=0; k<domainDim+1; k++) - if (j!=k and (testPoints[index[j]].r - testPoints[index[k]].r).two_norm() < 1e-5) - identicalPoints = true; + ::MultiIndex index(domainDim+1, testPoints.size()); + int numIndices = index.cycle(); - if (identicalPoints) - continue; + for (int i=0; i<numIndices; i++, ++index) { - for (int j=0; j<domainDim+1; j++) - coefficients[j] = testPoints[index[j]]; + // Discard all configurations that deform the element to zero area + bool identicalPoints = false; + for (int j=0; j<domainDim+1; j++) + for (int k=0; k<domainDim+1; k++) + if (j!=k and (testPoints[index[j]].r - testPoints[index[k]].r).two_norm() < 1e-5) + identicalPoints = true; + + if (identicalPoints) + continue; + + for (int j=0; j<domainDim+1; j++) + coefficients[j] = testPoints[index[j]]; + + testEnergy<GridType>(grid.get(), coefficients); + + } - testEnergy<GridType>(grid.get(), coefficients); - - } - } int main(int argc, char** argv) { - MPIHelper::instance(argc, argv); + MPIHelper::instance(argc, argv); + + const int domainDim = 2; - const int domainDim = 2; + ////////////////////////////////////////////////////////////////////////////////////// + // Test invariance of the energy functional under rotations + ////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////// - // Test invariance of the energy functional under rotations - ////////////////////////////////////////////////////////////////////////////////////// - - testFrameInvariance<domainDim>(); + testFrameInvariance<domainDim>(); } diff --git a/test/cosseratrodenergytest.cc b/test/cosseratrodenergytest.cc index 1430f72d..95cbc183 100644 --- a/test/cosseratrodenergytest.cc +++ b/test/cosseratrodenergytest.cc @@ -14,95 +14,96 @@ using namespace Dune; int main (int argc, char *argv[]) try { - // Some types that I need - typedef std::vector<RigidBodyMotion<double,3> > SolutionType; + // Some types that I need + typedef std::vector<RigidBodyMotion<double,3> > SolutionType; - // Problem settings - const int numRodBaseElements = 100; - - // /////////////////////////////////////// - // Create the grid - // /////////////////////////////////////// - typedef OneDGrid GridType; - GridType grid(numRodBaseElements, 0, 1); - using GridView = GridType::LeafGridView; - GridView gridView = grid.leafGridView(); + // Problem settings + const int numRodBaseElements = 100; - using FEBasis = Functions::LagrangeBasis<GridView,1>; - FEBasis feBasis(gridView); + // /////////////////////////////////////// + // Create the grid + // /////////////////////////////////////// + typedef OneDGrid GridType; + GridType grid(numRodBaseElements, 0, 1); + using GridView = GridType::LeafGridView; + GridView gridView = grid.leafGridView(); - SolutionType x(feBasis.size()); + using FEBasis = Functions::LagrangeBasis<GridView,1>; + FEBasis feBasis(gridView); - // ////////////////////////// - // Initial solution - // ////////////////////////// + SolutionType x(feBasis.size()); - for (size_t i=0; i<x.size(); i++) - { - double s = double(i)/(x.size()-1); - x[i].r[0] = 0.1*std::cos(2*M_PI*s); - x[i].r[1] = 0.1*std::sin(2*M_PI*s); - x[i].r[2] = s; - x[i].q = Rotation<double,3>::identity(); - //x[i].q = Quaternion<double>(zAxis, (double(i)*M_PI)/(2*(x.size()-1)) ); - } + // ////////////////////////// + // Initial solution + // ////////////////////////// - FieldVector<double,3> zAxis(0); zAxis[2]=1; - x.back().q = Rotation<double,3>(zAxis, M_PI/4); + for (size_t i=0; i<x.size(); i++) + { + double s = double(i)/(x.size()-1); + x[i].r[0] = 0.1*std::cos(2*M_PI*s); + x[i].r[1] = 0.1*std::sin(2*M_PI*s); + x[i].r[2] = s; + x[i].q = Rotation<double,3>::identity(); + //x[i].q = Quaternion<double>(zAxis, (double(i)*M_PI)/(2*(x.size()-1)) ); + } - // ///////////////////////////////////////////////////////////////////// - // Create a second, rotated copy of the configuration - // ///////////////////////////////////////////////////////////////////// + FieldVector<double,3> zAxis(0); zAxis[2]=1; + x.back().q = Rotation<double,3>(zAxis, M_PI/4); - FieldVector<double,3> displacement {0, 1, 0}; + // ///////////////////////////////////////////////////////////////////// + // Create a second, rotated copy of the configuration + // ///////////////////////////////////////////////////////////////////// - FieldVector<double,3> axis = {1,0,0}; - Rotation<double,3> rotation(axis,M_PI/2); + FieldVector<double,3> displacement {0, 1, 0}; - SolutionType rotatedX = x; + FieldVector<double,3> axis = {1,0,0}; + Rotation<double,3> rotation(axis,M_PI/2); - for (size_t i=0; i<rotatedX.size(); i++) - { - rotatedX[i].r = rotation.rotate(x[i].r); - rotatedX[i].r += displacement; + SolutionType rotatedX = x; - rotatedX[i].q = rotation.mult(x[i].q); - } + for (size_t i=0; i<rotatedX.size(); i++) + { + rotatedX[i].r = rotation.rotate(x[i].r); + rotatedX[i].r += displacement; - using GeodesicInterpolationRule = LocalGeodesicFEFunction<1, double, - FEBasis::LocalView::Tree::FiniteElement, - RigidBodyMotion<double,3> >; + rotatedX[i].q = rotation.mult(x[i].q); + } - GFE::CosseratRodEnergy<FEBasis, - GeodesicInterpolationRule, - double> localRodEnergy(gridView, - 1,1,1,1e6,0.3); + using GeodesicInterpolationRule = LocalGeodesicFEFunction<1, double, + FEBasis::LocalView::Tree::FiniteElement, + RigidBodyMotion<double,3> >; - std::vector<RigidBodyMotion<double,3> > referenceConfiguration(gridView.size(1)); + GFE::CosseratRodEnergy<FEBasis, + GeodesicInterpolationRule, + double> localRodEnergy(gridView, + 1,1,1,1e6,0.3); - for (const auto& vertex : vertices(gridView)) - { - auto idx = gridView.indexSet().index(vertex); + std::vector<RigidBodyMotion<double,3> > referenceConfiguration(gridView.size(1)); - referenceConfiguration[idx].r[0] = 0; - referenceConfiguration[idx].r[1] = 0; - referenceConfiguration[idx].r[2] = vertex.geometry().corner(0)[0]; - referenceConfiguration[idx].q = Rotation<double,3>::identity(); - } + for (const auto& vertex : vertices(gridView)) + { + auto idx = gridView.indexSet().index(vertex); - localRodEnergy.setReferenceConfiguration(referenceConfiguration); + referenceConfiguration[idx].r[0] = 0; + referenceConfiguration[idx].r[1] = 0; + referenceConfiguration[idx].r[2] = vertex.geometry().corner(0)[0]; + referenceConfiguration[idx].q = Rotation<double,3>::identity(); + } - auto localView = feBasis.localView(); - localView.bind(*gridView.begin<0>()); + localRodEnergy.setReferenceConfiguration(referenceConfiguration); - SolutionType localX = {x[0], x[1]}; - SolutionType localRotatedX = {rotatedX[0], rotatedX[1]}; + auto localView = feBasis.localView(); + localView.bind(*gridView.begin<0>()); - if (std::abs(localRodEnergy.energy(localView, localX) - localRodEnergy.energy(localView, localRotatedX)) > 1e-6) - DUNE_THROW(Dune::Exception, "Rod energy not invariant under rigid body motions!"); + SolutionType localX = {x[0], x[1]}; + SolutionType localRotatedX = {rotatedX[0], rotatedX[1]}; - } catch (Exception& e) { + if (std::abs(localRodEnergy.energy(localView, localX) - localRodEnergy.energy(localView, localRotatedX)) > 1e-6) + DUNE_THROW(Dune::Exception, "Rod energy not invariant under rigid body motions!"); - std::cout << e.what() << std::endl; +} +catch (Exception& e) { - } + std::cout << e.what() << std::endl; + +} diff --git a/test/cosseratrodtest.cc b/test/cosseratrodtest.cc index 6347ecca..59fc5a58 100644 --- a/test/cosseratrodtest.cc +++ b/test/cosseratrodtest.cc @@ -64,7 +64,9 @@ int main (int argc, char *argv[]) try std::vector<double> referenceConfigurationX(scalarBasis.size()); - auto identity = [](const FieldVector<double,1>& x) { return x; }; + auto identity = [](const FieldVector<double,1>& x) { + return x; + }; Functions::interpolate(scalarBasis, referenceConfigurationX, identity); @@ -94,7 +96,7 @@ int main (int argc, char *argv[]) try power<TargetSpace::TangentVector::dimension>( lagrange<order>(), blockedInterleaved() - )); + )); // Find all boundary dofs BoundaryPatch<GridView> dirichletBoundary(gridView, @@ -135,14 +137,14 @@ int main (int argc, char *argv[]) try if (interpolationMethod == "geodesic") { auto energy = std::make_shared<GFE::CosseratRodEnergy<ScalarBasis, GeodesicInterpolationRule, adouble> >(gridView, - A, J1, J2, E, nu); + A, J1, J2, E, nu); energy->setReferenceConfiguration(referenceConfiguration); localRodEnergy = energy; } else if (interpolationMethod == "projected") { auto energy = std::make_shared<GFE::CosseratRodEnergy<ScalarBasis, ProjectedInterpolationRule, adouble> >(gridView, - A, J1, J2, E, nu); + A, J1, J2, E, nu); energy->setReferenceConfiguration(referenceConfiguration); localRodEnergy = energy; } @@ -150,7 +152,7 @@ int main (int argc, char *argv[]) try DUNE_THROW(Exception, "Unknown interpolation method " << interpolationMethod << " requested!"); LocalGeodesicFEADOLCStiffness<ScalarBasis, - TargetSpace> localStiffness(localRodEnergy.get()); + TargetSpace> localStiffness(localRodEnergy.get()); GeodesicFEAssembler<ScalarBasis,TargetSpace> rodAssembler(gridView, localStiffness); @@ -182,7 +184,7 @@ int main (int argc, char *argv[]) try solver.solve(); x = solver.getSol(); - + std::size_t expectedFinalIteration = 4; if (solver.getStatistics().finalIteration != expectedFinalIteration) { @@ -200,7 +202,8 @@ int main (int argc, char *argv[]) try return 1; } -} catch (Exception& e) +} +catch (Exception& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << std::endl; } diff --git a/test/geodesicfeassemblerwrappertest.cc b/test/geodesicfeassemblerwrappertest.cc index 737e5d25..ae08c341 100644 --- a/test/geodesicfeassemblerwrappertest.cc +++ b/test/geodesicfeassemblerwrappertest.cc @@ -53,14 +53,14 @@ using namespace Indices; using ValueType = adouble; //Types for the mixed space -using DisplacementVector = std::vector<RealTuple<double,dim>>; -using RotationVector = std::vector<Rotation<double,dim>>; +using DisplacementVector = std::vector<RealTuple<double,dim> >; +using RotationVector = std::vector<Rotation<double,dim> >; using Vector = TupleVector<DisplacementVector, RotationVector>; const int dimCR = Rotation<double,dim>::TangentVector::dimension; //dimCorrectionRotation = Dimension of the correction for rotations -using CorrectionType = MultiTypeBlockVector<BlockVector<FieldVector<double,dim> >, BlockVector<FieldVector<double,dimCR>>>; +using CorrectionType = MultiTypeBlockVector<BlockVector<FieldVector<double,dim> >, BlockVector<FieldVector<double,dimCR> > >; -using MatrixRow0 = MultiTypeBlockVector<BCRSMatrix<FieldMatrix<double,dim,dim>>, BCRSMatrix<FieldMatrix<double,dim,dimCR>>>; -using MatrixRow1 = MultiTypeBlockVector<BCRSMatrix<FieldMatrix<double,dimCR,dim>>, BCRSMatrix<FieldMatrix<double,dimCR,dimCR>>>; +using MatrixRow0 = MultiTypeBlockVector<BCRSMatrix<FieldMatrix<double,dim,dim> >, BCRSMatrix<FieldMatrix<double,dim,dimCR> > >; +using MatrixRow1 = MultiTypeBlockVector<BCRSMatrix<FieldMatrix<double,dimCR,dim> >, BCRSMatrix<FieldMatrix<double,dimCR,dimCR> > >; using MatrixType = MultiTypeBlockMatrix<MatrixRow0,MatrixRow1>; //Types for the Non-mixed space @@ -85,14 +85,14 @@ int main (int argc, char *argv[]) GridView gridView = grid->leafGridView(); std::function<bool(FieldVector<double,gridDim>)> isNeumann = [](FieldVector<double,gridDim> coordinate) { - return coordinate[0] > 0.99; - }; + return coordinate[0] > 0.99; + }; BitSetVector<1> neumannVertices(gridView.size(gridDim), false); const GridView::IndexSet& indexSet = gridView.indexSet(); - + for (auto&& vertex : vertices(gridView)) - neumannVertices[indexSet.index(vertex)] = isNeumann(vertex.geometry().corner(0)); + neumannVertices[indexSet.index(vertex)] = isNeumann(vertex.geometry().corner(0)); BoundaryPatch<GridView> neumannBoundary(gridView, neumannVertices); @@ -108,11 +108,11 @@ int main (int argc, char *argv[]) composite( power<dim>( lagrange<displacementOrder>() - ), + ), power<dim>( lagrange<rotationOrder>() - ) - )); + ) + )); using CompositeBasis = decltype(compositeBasis); @@ -136,36 +136,38 @@ int main (int argc, char *argv[]) FieldVector<double,dim> values_ = {3e4,2e4,1e4}; auto neumannFunction = [&](FieldVector<double, gridDim>){ - return values_; - }; + return values_; + }; CosseratEnergyLocalStiffness<decltype(compositeBasis), dim,adouble> cosseratEnergy(parameters, - &neumannBoundary, - neumannFunction, - nullptr); + &neumannBoundary, + neumannFunction, + nullptr); MixedLocalGFEADOLCStiffness<CompositeBasis, - RealTuple<double,dim>, - Rotation<double,dim> > mixedLocalGFEADOLCStiffness(&cosseratEnergy); + RealTuple<double,dim>, + Rotation<double,dim> > mixedLocalGFEADOLCStiffness(&cosseratEnergy); MixedGFEAssembler<CompositeBasis, - RealTuple<double,dim>, - Rotation<double,dim> > mixedAssembler(compositeBasis, &mixedLocalGFEADOLCStiffness); + RealTuple<double,dim>, + Rotation<double,dim> > mixedAssembler(compositeBasis, &mixedLocalGFEADOLCStiffness); using RBM = RigidBodyMotion<double, dim>; using DeformationFEBasis = Functions::LagrangeBasis<GridView,displacementOrder>; DeformationFEBasis deformationFEBasis(gridView); - using GFEAssemblerWrapper = GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, RBM, RealTuple<double, dim>, Rotation<double,dim>>; + using GFEAssemblerWrapper = GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, RBM, RealTuple<double, dim>, Rotation<double,dim> >; GFEAssemblerWrapper assembler(&mixedAssembler, deformationFEBasis); - + ///////////////////////////////////////////////////////////////////////// // Prepare the iterate x where we want to assemble - identity in 2D with z = 0 - ///////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////// auto deformationPowerBasis = makeBasis( gridView, power<gridDim>( - lagrange<displacementOrder>() - )); + lagrange<displacementOrder>() + )); BlockVector<FieldVector<double,gridDim> > identity(compositeBasis.size({0})); - Functions::interpolate(deformationPowerBasis, identity, [](FieldVector<double,gridDim> x){ return x; }); + Functions::interpolate(deformationPowerBasis, identity, [](FieldVector<double,gridDim> x){ + return x; + }); BlockVector<FieldVector<double,dim> > initialDeformation(compositeBasis.size({0})); initialDeformation = 0; @@ -184,7 +186,7 @@ int main (int argc, char *argv[]) } ////////////////////////////////////////////////////////////////////////////// - // Compute the energy, assemble the Gradient and Hessian using + // Compute the energy, assemble the Gradient and Hessian using // the GeodesicFEAssemblerWrapper and the MixedGFEAssembler and compare! ////////////////////////////////////////////////////////////////////////////// CorrectionTypeWrapped gradient; @@ -207,27 +209,27 @@ int main (int argc, char *argv[]) if (std::abs(energy - energyMixed)/energyMixed > 1e-8) { - std::cerr << std::setprecision(9); - std::cerr << "The energy calculated by the GeodesicFEAssemblerWrapper is " << energy << " but " - << energyMixed << " (calculated by the MixedGFEAssembler) was expected!" << std::endl; - return 1; + std::cerr << std::setprecision(9); + std::cerr << "The energy calculated by the GeodesicFEAssemblerWrapper is " << energy << " but " + << energyMixed << " (calculated by the MixedGFEAssembler) was expected!" << std::endl; + return 1; } if ( std::abs(gradientTwoNorm - gradientMixedTwoNorm)/gradientMixedTwoNorm > 1e-8 || std::abs(gradientInfinityNorm - gradientMixedInfinityNorm)/gradientMixedInfinityNorm > 1e-8) { - std::cerr << std::setprecision(9); - std::cerr << "The gradient infinity norm calculated by the GeodesicFEAssemblerWrapper is " << gradientInfinityNorm << " but " - << gradientMixedInfinityNorm << " (calculated by the MixedGFEAssembler) was expected!" << std::endl; - std::cerr << "The gradient norm calculated by the GeodesicFEAssemblerWrapper is " << gradientTwoNorm << " but " - << gradientMixedTwoNorm << " (calculated by the MixedGFEAssembler) was expected!" << std::endl; - return 1; + std::cerr << std::setprecision(9); + std::cerr << "The gradient infinity norm calculated by the GeodesicFEAssemblerWrapper is " << gradientInfinityNorm << " but " + << gradientMixedInfinityNorm << " (calculated by the MixedGFEAssembler) was expected!" << std::endl; + std::cerr << "The gradient norm calculated by the GeodesicFEAssemblerWrapper is " << gradientTwoNorm << " but " + << gradientMixedTwoNorm << " (calculated by the MixedGFEAssembler) was expected!" << std::endl; + return 1; } if (std::abs(matrixFrobeniusNorm - matrixMixedFrobeniusNorm)/matrixMixedFrobeniusNorm > 1e-8) { - std::cerr << std::setprecision(9); - std::cerr << "The matrix norm calculated by the GeodesicFEAssemblerWrapper is " << matrixFrobeniusNorm << " but " - << matrixMixedFrobeniusNorm << " (calculated by the MixedGFEAssembler) was expected!" << std::endl; - return 1; + std::cerr << std::setprecision(9); + std::cerr << "The matrix norm calculated by the GeodesicFEAssemblerWrapper is " << matrixFrobeniusNorm << " but " + << matrixMixedFrobeniusNorm << " (calculated by the MixedGFEAssembler) was expected!" << std::endl; + return 1; } } diff --git a/test/harmonicenergytest.cc b/test/harmonicenergytest.cc index 6ed8ca93..bdfa68df 100644 --- a/test/harmonicenergytest.cc +++ b/test/harmonicenergytest.cc @@ -19,43 +19,43 @@ using namespace Dune; template <class TargetSpace> double diameter(const std::vector<TargetSpace>& v) { - double d = 0; - for (size_t i=0; i<v.size(); i++) - for (size_t j=0; j<v.size(); j++) - d = std::max(d, TargetSpace::distance(v[i],v[j])); - return d; + double d = 0; + for (size_t i=0; i<v.size(); i++) + for (size_t j=0; j<v.size(); j++) + d = std::max(d, TargetSpace::distance(v[i],v[j])); + return d; } template <class Basis, class TargetSpace> void testEnergy(const Basis& basis, const std::vector<TargetSpace>& coefficients) { - using GridView = typename Basis::GridView; - GridView gridView = basis.gridView(); + using GridView = typename Basis::GridView; + GridView gridView = basis.gridView(); - using GeodesicInterpolationRule = LocalGeodesicFEFunction<Basis::GridView::dimension, double, typename Basis::LocalView::Tree::FiniteElement, TargetSpace>; + using GeodesicInterpolationRule = LocalGeodesicFEFunction<Basis::GridView::dimension, double, typename Basis::LocalView::Tree::FiniteElement, TargetSpace>; - HarmonicEnergy<Basis,GeodesicInterpolationRule,TargetSpace> assembler; - std::vector<TargetSpace> rotatedCoefficients(coefficients.size()); + HarmonicEnergy<Basis,GeodesicInterpolationRule,TargetSpace> assembler; + std::vector<TargetSpace> rotatedCoefficients(coefficients.size()); - auto localView = basis.localView(); - localView.bind(*gridView.template begin<0>()); + auto localView = basis.localView(); + localView.bind(*gridView.template begin<0>()); - for (int i=0; i<10; i++) { + for (int i=0; i<10; i++) { - Rotation<double,3> rotation(FieldVector<double,3>(1), double(i)); + Rotation<double,3> rotation(FieldVector<double,3>(1), double(i)); - FieldMatrix<double,3,3> matrix; - rotation.matrix(matrix); - for (size_t j=0; j<coefficients.size(); j++) { - FieldVector<double,3> tmp; - matrix.mv(coefficients[j].globalCoordinates(), tmp); - rotatedCoefficients[j] = tmp; - } + FieldMatrix<double,3,3> matrix; + rotation.matrix(matrix); + for (size_t j=0; j<coefficients.size(); j++) { + FieldVector<double,3> tmp; + matrix.mv(coefficients[j].globalCoordinates(), tmp); + rotatedCoefficients[j] = tmp; + } - std::cout << "energy: " << assembler.energy(localView, - rotatedCoefficients) << std::endl; + std::cout << "energy: " << assembler.energy(localView, + rotatedCoefficients) << std::endl; - } + } } @@ -63,68 +63,68 @@ void testEnergy(const Basis& basis, const std::vector<TargetSpace>& coefficients template <int domainDim, class TargetSpace> void test() { - // //////////////////////////////////////////////////////// - // Make a test grid consisting of a single simplex - // //////////////////////////////////////////////////////// + // //////////////////////////////////////////////////////// + // Make a test grid consisting of a single simplex + // //////////////////////////////////////////////////////// + + typedef UGGrid<domainDim> GridType; - typedef UGGrid<domainDim> GridType; + GridFactory<GridType> factory; - GridFactory<GridType> factory; + FieldVector<double,domainDim> pos(0); + factory.insertVertex(pos); - FieldVector<double,domainDim> pos(0); + for (int i=0; i<domainDim; i++) { + pos = 0; + pos[i] = 1; factory.insertVertex(pos); + } - for (int i=0; i<domainDim; i++) { - pos = 0; - pos[i] = 1; - factory.insertVertex(pos); - } + std::vector<unsigned int> v(domainDim+1); + for (int i=0; i<domainDim+1; i++) + v[i] = i; + factory.insertElement(GeometryTypes::simplex(domainDim), v); - std::vector<unsigned int> v(domainDim+1); - for (int i=0; i<domainDim+1; i++) - v[i] = i; - factory.insertElement(GeometryTypes::simplex(domainDim), v); + auto grid = std::unique_ptr<const GridType>(factory.createGrid()); + using GridView = typename GridType::LeafGridView; + auto gridView = grid->leafGridView(); - auto grid = std::unique_ptr<const GridType>(factory.createGrid()); - using GridView = typename GridType::LeafGridView; - auto gridView = grid->leafGridView(); + // A function space basis + using Basis = Functions::LagrangeBasis<GridView,1>; + Basis basis(gridView); - // A function space basis - using Basis = Functions::LagrangeBasis<GridView,1>; - Basis basis(gridView); + // ////////////////////////////////////////////////////////// + // Test whether the energy is invariant under isometries + // ////////////////////////////////////////////////////////// - // ////////////////////////////////////////////////////////// - // Test whether the energy is invariant under isometries - // ////////////////////////////////////////////////////////// + std::vector<TargetSpace> testPoints; + ValueFactory<TargetSpace>::get(testPoints); - std::vector<TargetSpace> testPoints; - ValueFactory<TargetSpace>::get(testPoints); + // Set up elements of S^2 + std::vector<TargetSpace> coefficients(domainDim+1); - // Set up elements of S^2 - std::vector<TargetSpace> coefficients(domainDim+1); + ::MultiIndex index(domainDim+1, testPoints.size()); + int numIndices = index.cycle(); - ::MultiIndex index(domainDim+1, testPoints.size()); - int numIndices = index.cycle(); + for (int i=0; i<numIndices; i++, ++index) { - for (int i=0; i<numIndices; i++, ++index) { - - for (int j=0; j<domainDim+1; j++) - coefficients[j] = testPoints[index[j]]; + for (int j=0; j<domainDim+1; j++) + coefficients[j] = testPoints[index[j]]; - // This may be overly restrictive, but see the TODO in targetspacetrsolver.cc - if (diameter(coefficients) > TargetSpace::convexityRadius) - continue; + // This may be overly restrictive, but see the TODO in targetspacetrsolver.cc + if (diameter(coefficients) > TargetSpace::convexityRadius) + continue; - testEnergy<Basis>(basis, coefficients); - - } + testEnergy<Basis>(basis, coefficients); + + } } int main(int argc, char** argv) { - MPIHelper::instance(argc, argv); + MPIHelper::instance(argc, argv); - test<2,UnitVector<double,3> >(); + test<2,UnitVector<double,3> >(); } diff --git a/test/harmonicmaptest.cc b/test/harmonicmaptest.cc index bca635fd..34049922 100644 --- a/test/harmonicmaptest.cc +++ b/test/harmonicmaptest.cc @@ -88,7 +88,7 @@ int main (int argc, char *argv[]) power<TargetSpace::CoordinateType::dimension>( lagrange<order>(), blockedInterleaved() - )); + )); // A basis for the tangent space auto tangentBasis = makeBasis( @@ -96,7 +96,7 @@ int main (int argc, char *argv[]) power<TargetSpace::TangentVector::dimension>( lagrange<order>(), blockedInterleaved() - )); + )); /////////////////////////////////////////// // Determine Dirichlet values @@ -107,9 +107,9 @@ int main (int argc, char *argv[]) // Make predicate function that computes which vertices are on the Dirichlet boundary, // based on the vertex positions. auto dirichletVerticesPredicate = [](FieldVector<double,dim> x) - { - return (x[0] < -4.9999 or x[0] > 4.9999 or x[1] < -4.9999 or x[1] > 4.9999); - }; + { + return (x[0] < -4.9999 or x[0] > 4.9999 or x[1] < -4.9999 or x[1] > 4.9999); + }; for (auto&& vertex : vertices(gridView)) { @@ -127,10 +127,10 @@ int main (int argc, char *argv[]) // The inverse stereographic projection through the north pole auto initialIterateFunction = [](FieldVector<double,dim> x) -> TargetSpace::CoordinateType - { - auto normSquared = x.two_norm2(); - return {2*x[0] / (normSquared+1), 2*x[1] / (normSquared+1), (normSquared-1)/ (normSquared+1)}; - }; + { + auto normSquared = x.two_norm2(); + return {2*x[0] / (normSquared+1), 2*x[1] / (normSquared+1), (normSquared-1)/ (normSquared+1)}; + }; std::vector<TargetSpace::CoordinateType> v; Dune::Functions::interpolate(powerBasis, v, initialIterateFunction); @@ -184,18 +184,18 @@ int main (int argc, char *argv[]) std::size_t expectedFinalIteration = 12; if (solver.getStatistics().finalIteration != expectedFinalIteration) { - std::cerr << "Trust-region solver did " << solver.getStatistics().finalIteration+1 - << " iterations, instead of the expected '" << expectedFinalIteration+1 << "'!" << std::endl; - return 1; + std::cerr << "Trust-region solver did " << solver.getStatistics().finalIteration+1 + << " iterations, instead of the expected '" << expectedFinalIteration+1 << "'!" << std::endl; + return 1; } double expectedEnergy = 12.2927849; if ( std::abs(solver.getStatistics().finalEnergy - expectedEnergy) > 1e-7) { - std::cerr << std::setprecision(9); - std::cerr << "Final energy is " << solver.getStatistics().finalEnergy - << " but '" << expectedEnergy << "' was expected!" << std::endl; - return 1; + std::cerr << std::setprecision(9); + std::cerr << "Final energy is " << solver.getStatistics().finalEnergy + << " but '" << expectedEnergy << "' was expected!" << std::endl; + return 1; } return 0; diff --git a/test/localgeodesicfefunctiontest.cc b/test/localgeodesicfefunctiontest.cc index 30e052f1..1ff9526d 100644 --- a/test/localgeodesicfefunctiontest.cc +++ b/test/localgeodesicfefunctiontest.cc @@ -28,11 +28,11 @@ using namespace Dune; template <class TargetSpace> double diameter(const std::vector<TargetSpace>& v) { - double d = 0; - for (size_t i=0; i<v.size(); i++) - for (size_t j=0; j<v.size(); j++) - d = std::max(d, TargetSpace::distance(v[i],v[j])); - return d; + double d = 0; + for (size_t i=0; i<v.size(); i++) + for (size_t j=0; j<v.size(); j++) + d = std::max(d, TargetSpace::distance(v[i],v[j])); + return d; } template <int dim, class ctype, class LocalFunction> @@ -40,27 +40,27 @@ auto evaluateDerivativeFD(const LocalFunction& f, const Dune::FieldVector<ctype, dim>& local) -> decltype(f.evaluateDerivative(local)) { - double eps = 1e-8; - //static const int embeddedDim = LocalFunction::TargetSpace::embeddedDim; - decltype(f.evaluateDerivative(local)) result; + double eps = 1e-8; + //static const int embeddedDim = LocalFunction::TargetSpace::embeddedDim; + decltype(f.evaluateDerivative(local)) result; - for (int i=0; i<dim; i++) { + for (int i=0; i<dim; i++) { - Dune::FieldVector<ctype, dim> forward = local; - Dune::FieldVector<ctype, dim> backward = local; + Dune::FieldVector<ctype, dim> forward = local; + Dune::FieldVector<ctype, dim> backward = local; - forward[i] += eps; - backward[i] -= eps; + forward[i] += eps; + backward[i] -= eps; - auto fdDer = f.evaluate(forward).globalCoordinates() - f.evaluate(backward).globalCoordinates(); - fdDer /= 2*eps; + auto fdDer = f.evaluate(forward).globalCoordinates() - f.evaluate(backward).globalCoordinates(); + fdDer /= 2*eps; - for (size_t j=0; j<result.N(); j++) - result[j][i] = fdDer[j]; + for (size_t j=0; j<result.N(); j++) + result[j][i] = fdDer[j]; - } + } - return result; + return result; } @@ -70,265 +70,265 @@ evaluateDerivativeFD(const LocalFunction& f, const Dune::FieldVector<ctype, dim> template <int domainDim, class TargetSpace> void testPermutationInvariance(const std::vector<TargetSpace>& corners) { - // works only for 2d domains - if (domainDim!=2) - return; + // works only for 2d domains + if (domainDim!=2) + return; - LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; - typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; - - GeometryType simplex = GeometryTypes::simplex(domainDim); - - // - std::vector<TargetSpace> cornersRotated1(domainDim+1); - std::vector<TargetSpace> cornersRotated2(domainDim+1); - - cornersRotated1[0] = cornersRotated2[2] = corners[1]; - cornersRotated1[1] = cornersRotated2[0] = corners[2]; - cornersRotated1[2] = cornersRotated2[1] = corners[0]; - - LocalGeodesicFEFunction<2,double,LocalFiniteElement,TargetSpace> f0(feCache.get(simplex), corners); - LocalGeodesicFEFunction<2,double,LocalFiniteElement,TargetSpace> f1(feCache.get(simplex), cornersRotated1); - LocalGeodesicFEFunction<2,double,LocalFiniteElement,TargetSpace> f2(feCache.get(simplex), cornersRotated2); - - // A quadrature rule as a set of test points - int quadOrder = 3; - - const Dune::QuadratureRule<double, domainDim>& quad - = Dune::QuadratureRules<double, domainDim>::rule(simplex, quadOrder); - - for (size_t pt=0; pt<quad.size(); pt++) { - - const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); - - Dune::FieldVector<double,domainDim> l0 = quadPos; - Dune::FieldVector<double,domainDim> l1, l2; - - l1[0] = quadPos[1]; - l1[1] = 1-quadPos[0]-quadPos[1]; - - l2[0] = 1-quadPos[0]-quadPos[1]; - l2[1] = quadPos[0]; - - // evaluate the three functions - TargetSpace v0 = f0.evaluate(l0); - TargetSpace v1 = f1.evaluate(l1); - TargetSpace v2 = f2.evaluate(l2); - - // Check that they are all equal - assert(TargetSpace::distance(v0,v1) < eps); - assert(TargetSpace::distance(v0,v2) < eps); + LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; + typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; - } + GeometryType simplex = GeometryTypes::simplex(domainDim); + + // + std::vector<TargetSpace> cornersRotated1(domainDim+1); + std::vector<TargetSpace> cornersRotated2(domainDim+1); + + cornersRotated1[0] = cornersRotated2[2] = corners[1]; + cornersRotated1[1] = cornersRotated2[0] = corners[2]; + cornersRotated1[2] = cornersRotated2[1] = corners[0]; + + LocalGeodesicFEFunction<2,double,LocalFiniteElement,TargetSpace> f0(feCache.get(simplex), corners); + LocalGeodesicFEFunction<2,double,LocalFiniteElement,TargetSpace> f1(feCache.get(simplex), cornersRotated1); + LocalGeodesicFEFunction<2,double,LocalFiniteElement,TargetSpace> f2(feCache.get(simplex), cornersRotated2); + + // A quadrature rule as a set of test points + int quadOrder = 3; + + const Dune::QuadratureRule<double, domainDim>& quad + = Dune::QuadratureRules<double, domainDim>::rule(simplex, quadOrder); + + for (size_t pt=0; pt<quad.size(); pt++) { + + const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); + + Dune::FieldVector<double,domainDim> l0 = quadPos; + Dune::FieldVector<double,domainDim> l1, l2; + + l1[0] = quadPos[1]; + l1[1] = 1-quadPos[0]-quadPos[1]; + + l2[0] = 1-quadPos[0]-quadPos[1]; + l2[1] = quadPos[0]; + + // evaluate the three functions + TargetSpace v0 = f0.evaluate(l0); + TargetSpace v1 = f1.evaluate(l1); + TargetSpace v2 = f2.evaluate(l2); + + // Check that they are all equal + assert(TargetSpace::distance(v0,v1) < eps); + assert(TargetSpace::distance(v0,v2) < eps); + + } } template <int domainDim, class TargetSpace> void testDerivative(const LocalGeodesicFEFunction<domainDim,double,typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType, TargetSpace>& f) { - static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension; - - // A quadrature rule as a set of test points - int quadOrder = 3; - - const Dune::QuadratureRule<double, domainDim>& quad - = Dune::QuadratureRules<double, domainDim>::rule(f.type(), quadOrder); - - for (size_t pt=0; pt<quad.size(); pt++) { - - const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); - - // evaluate actual derivative - Dune::FieldMatrix<double, embeddedDim, domainDim> derivative = f.evaluateDerivative(quadPos); - - // evaluate fd approximation of derivative - Dune::FieldMatrix<double, embeddedDim, domainDim> fdDerivative = evaluateDerivativeFD(f,quadPos); - - Dune::FieldMatrix<double, embeddedDim, domainDim> diff = derivative; - diff -= fdDerivative; - - if ( diff.infinity_norm() > 100*eps ) { - std::cout << className<TargetSpace>() << ": Analytical gradient does not match fd approximation." << std::endl; - std::cout << "Analytical: " << derivative << std::endl; - std::cout << "FD : " << fdDerivative << std::endl; - } + static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension; + + // A quadrature rule as a set of test points + int quadOrder = 3; + + const Dune::QuadratureRule<double, domainDim>& quad + = Dune::QuadratureRules<double, domainDim>::rule(f.type(), quadOrder); + + for (size_t pt=0; pt<quad.size(); pt++) { + + const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); + + // evaluate actual derivative + Dune::FieldMatrix<double, embeddedDim, domainDim> derivative = f.evaluateDerivative(quadPos); + + // evaluate fd approximation of derivative + Dune::FieldMatrix<double, embeddedDim, domainDim> fdDerivative = evaluateDerivativeFD(f,quadPos); + Dune::FieldMatrix<double, embeddedDim, domainDim> diff = derivative; + diff -= fdDerivative; + + if ( diff.infinity_norm() > 100*eps ) { + std::cout << className<TargetSpace>() << ": Analytical gradient does not match fd approximation." << std::endl; + std::cout << "Analytical: " << derivative << std::endl; + std::cout << "FD : " << fdDerivative << std::endl; } + + } } template <int domainDim, class TargetSpace> void testDerivativeOfValueWRTCoefficients(const LocalGeodesicFEFunction<domainDim,double,typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType, TargetSpace>& f) { - static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension; - - // A quadrature rule as a set of test points - int quadOrder = 3; - - const Dune::QuadratureRule<double, domainDim>& quad - = Dune::QuadratureRules<double, domainDim>::rule(f.type(), quadOrder); - - for (size_t pt=0; pt<quad.size(); pt++) { - - Dune::FieldVector<double,domainDim> quadPos = quad[pt].position(); - - // loop over the coefficients - for (size_t i=0; i<f.size(); i++) { - - // evaluate actual derivative - FieldMatrix<double, embeddedDim, embeddedDim> derivative; - f.evaluateDerivativeOfValueWRTCoefficient(quadPos, i, derivative); - - // evaluate fd approximation of derivative - FieldMatrix<double, embeddedDim, embeddedDim> fdDerivative; - f.evaluateFDDerivativeOfValueWRTCoefficient(quadPos, i, fdDerivative); - - if ( (derivative - fdDerivative).infinity_norm() > eps ) { - std::cout << className<TargetSpace>() << ": Analytical derivative of value does not match fd approximation." << std::endl; - std::cout << "coefficient: " << i << std::endl; - std::cout << "quad pos: " << quadPos << std::endl; - std::cout << "gfe: "; - for (size_t j=0; j<f.size(); j++) - std::cout << ", " << f.coefficient(j); - std::cout << std::endl; - std::cout << "Analytical:\n " << derivative << std::endl; - std::cout << "FD :\n " << fdDerivative << std::endl; - assert(false); - } - - } - + static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension; + + // A quadrature rule as a set of test points + int quadOrder = 3; + + const Dune::QuadratureRule<double, domainDim>& quad + = Dune::QuadratureRules<double, domainDim>::rule(f.type(), quadOrder); + + for (size_t pt=0; pt<quad.size(); pt++) { + + Dune::FieldVector<double,domainDim> quadPos = quad[pt].position(); + + // loop over the coefficients + for (size_t i=0; i<f.size(); i++) { + + // evaluate actual derivative + FieldMatrix<double, embeddedDim, embeddedDim> derivative; + f.evaluateDerivativeOfValueWRTCoefficient(quadPos, i, derivative); + + // evaluate fd approximation of derivative + FieldMatrix<double, embeddedDim, embeddedDim> fdDerivative; + f.evaluateFDDerivativeOfValueWRTCoefficient(quadPos, i, fdDerivative); + + if ( (derivative - fdDerivative).infinity_norm() > eps ) { + std::cout << className<TargetSpace>() << ": Analytical derivative of value does not match fd approximation." << std::endl; + std::cout << "coefficient: " << i << std::endl; + std::cout << "quad pos: " << quadPos << std::endl; + std::cout << "gfe: "; + for (size_t j=0; j<f.size(); j++) + std::cout << ", " << f.coefficient(j); + std::cout << std::endl; + std::cout << "Analytical:\n " << derivative << std::endl; + std::cout << "FD :\n " << fdDerivative << std::endl; + assert(false); + } + } + + } } template <int domainDim, class TargetSpace> void testDerivativeOfGradientWRTCoefficients(const LocalGeodesicFEFunction<domainDim,double,typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType, TargetSpace>& f) { - static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension; - - // A quadrature rule as a set of test points - int quadOrder = 3; - - const Dune::QuadratureRule<double, domainDim>& quad - = Dune::QuadratureRules<double, domainDim>::rule(f.type(), quadOrder); - - for (size_t pt=0; pt<quad.size(); pt++) { - - const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); - - // loop over the coefficients - for (size_t i=0; i<f.size(); i++) { - - // evaluate actual derivative - Tensor3<double, embeddedDim, embeddedDim, domainDim> derivative; - f.evaluateDerivativeOfGradientWRTCoefficient(quadPos, i, derivative); - - // evaluate fd approximation of derivative - Tensor3<double, embeddedDim, embeddedDim, domainDim> fdDerivative; - f.evaluateFDDerivativeOfGradientWRTCoefficient(quadPos, i, fdDerivative); - - if ( (derivative - fdDerivative).infinity_norm() > eps ) { - std::cout << className<TargetSpace>() << ": Analytical derivative of gradient does not match fd approximation." << std::endl; - std::cout << "coefficient: " << i << std::endl; - std::cout << "quad pos: " << quadPos << std::endl; - std::cout << "gfe: "; - for (size_t j=0; j<f.size(); j++) - std::cout << ", " << f.coefficient(j); - std::cout << std::endl; - std::cout << "Analytical:\n " << derivative << std::endl; - std::cout << "FD :\n " << fdDerivative << std::endl; - assert(false); - } - - } - + static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension; + + // A quadrature rule as a set of test points + int quadOrder = 3; + + const Dune::QuadratureRule<double, domainDim>& quad + = Dune::QuadratureRules<double, domainDim>::rule(f.type(), quadOrder); + + for (size_t pt=0; pt<quad.size(); pt++) { + + const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); + + // loop over the coefficients + for (size_t i=0; i<f.size(); i++) { + + // evaluate actual derivative + Tensor3<double, embeddedDim, embeddedDim, domainDim> derivative; + f.evaluateDerivativeOfGradientWRTCoefficient(quadPos, i, derivative); + + // evaluate fd approximation of derivative + Tensor3<double, embeddedDim, embeddedDim, domainDim> fdDerivative; + f.evaluateFDDerivativeOfGradientWRTCoefficient(quadPos, i, fdDerivative); + + if ( (derivative - fdDerivative).infinity_norm() > eps ) { + std::cout << className<TargetSpace>() << ": Analytical derivative of gradient does not match fd approximation." << std::endl; + std::cout << "coefficient: " << i << std::endl; + std::cout << "quad pos: " << quadPos << std::endl; + std::cout << "gfe: "; + for (size_t j=0; j<f.size(); j++) + std::cout << ", " << f.coefficient(j); + std::cout << std::endl; + std::cout << "Analytical:\n " << derivative << std::endl; + std::cout << "FD :\n " << fdDerivative << std::endl; + assert(false); + } + } + + } } template <class TargetSpace, int domainDim> void test(const GeometryType& element) { - std::cout << " --- Testing " << className<TargetSpace>() << ", domain dimension: " << element.dim() << " ---" << std::endl; - - std::vector<TargetSpace> testPoints; - ValueFactory<TargetSpace>::get(testPoints); - - int nTestPoints = testPoints.size(); - size_t nVertices = Dune::ReferenceElements<double,domainDim>::general(element).size(domainDim); - - // Set up elements of the target space - std::vector<TargetSpace> corners(nVertices); - - MultiIndex index(nVertices, nTestPoints); - int numIndices = index.cycle(); - - for (int i=0; i<numIndices; i++, ++index) { - - for (size_t j=0; j<nVertices; j++) - corners[j] = testPoints[index[j]]; - - if (diameter(corners) > 0.5*M_PI) - continue; - - // Make local gfe function to be tested - LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; - typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; - - LocalGeodesicFEFunction<domainDim,double,LocalFiniteElement,TargetSpace> f(feCache.get(element),corners); - - //testPermutationInvariance(corners); - testDerivative<domainDim>(f); - testDerivativeOfValueWRTCoefficients<domainDim>(f); - testDerivativeOfGradientWRTCoefficients<domainDim>(f); - - } + std::cout << " --- Testing " << className<TargetSpace>() << ", domain dimension: " << element.dim() << " ---" << std::endl; + + std::vector<TargetSpace> testPoints; + ValueFactory<TargetSpace>::get(testPoints); + + int nTestPoints = testPoints.size(); + size_t nVertices = Dune::ReferenceElements<double,domainDim>::general(element).size(domainDim); + + // Set up elements of the target space + std::vector<TargetSpace> corners(nVertices); + + MultiIndex index(nVertices, nTestPoints); + int numIndices = index.cycle(); + + for (int i=0; i<numIndices; i++, ++index) { + + for (size_t j=0; j<nVertices; j++) + corners[j] = testPoints[index[j]]; + + if (diameter(corners) > 0.5*M_PI) + continue; + + // Make local gfe function to be tested + LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; + typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; + + LocalGeodesicFEFunction<domainDim,double,LocalFiniteElement,TargetSpace> f(feCache.get(element),corners); + + //testPermutationInvariance(corners); + testDerivative<domainDim>(f); + testDerivativeOfValueWRTCoefficients<domainDim>(f); + testDerivativeOfGradientWRTCoefficients<domainDim>(f); + + } } int main() { - // choke on NaN -- don't enable this by default, as there are - // a few harmless NaN in the loopsolver - //feenableexcept(FE_INVALID); - - std::cout << std::setw(15) << std::setprecision(12); - - //////////////////////////////////////////////////////////////// - // Test functions on 1d elements - //////////////////////////////////////////////////////////////// - - test<RealTuple<double,1>,1>(GeometryTypes::simplex(1)); - test<UnitVector<double,2>,1>(GeometryTypes::simplex(1)); - test<UnitVector<double,3>,1>(GeometryTypes::simplex(1)); - test<Rotation<double,3>,1>(GeometryTypes::simplex(1)); - test<RigidBodyMotion<double,3>,1>(GeometryTypes::simplex(1)); - typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold; - test<CrazyManifold,1>(GeometryTypes::simplex(1)); - - //////////////////////////////////////////////////////////////// - // Test functions on 2d simplex elements - //////////////////////////////////////////////////////////////// - - test<RealTuple<double,1>,2>(GeometryTypes::simplex(2)); - test<UnitVector<double,2>,2>(GeometryTypes::simplex(2)); - test<UnitVector<double,3>,2>(GeometryTypes::simplex(2)); - test<Rotation<double,3>,2>(GeometryTypes::simplex(2)); - test<RigidBodyMotion<double,3>,2>(GeometryTypes::simplex(2)); - typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold; - test<CrazyManifold,2>(GeometryTypes::simplex(2)); - - //////////////////////////////////////////////////////////////// - // Test functions on 2d quadrilateral elements - //////////////////////////////////////////////////////////////// - - test<RealTuple<double,1>,2>(GeometryTypes::cube(2)); - test<UnitVector<double,2>,2>(GeometryTypes::cube(2)); - test<UnitVector<double,3>,2>(GeometryTypes::cube(2)); - test<Rotation<double,3>,2>(GeometryTypes::cube(2)); - test<RigidBodyMotion<double,3>,2>(GeometryTypes::cube(2)); - typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold; - test<CrazyManifold,2>(GeometryTypes::cube(2)); + // choke on NaN -- don't enable this by default, as there are + // a few harmless NaN in the loopsolver + //feenableexcept(FE_INVALID); + + std::cout << std::setw(15) << std::setprecision(12); + + //////////////////////////////////////////////////////////////// + // Test functions on 1d elements + //////////////////////////////////////////////////////////////// + + test<RealTuple<double,1>,1>(GeometryTypes::simplex(1)); + test<UnitVector<double,2>,1>(GeometryTypes::simplex(1)); + test<UnitVector<double,3>,1>(GeometryTypes::simplex(1)); + test<Rotation<double,3>,1>(GeometryTypes::simplex(1)); + test<RigidBodyMotion<double,3>,1>(GeometryTypes::simplex(1)); + typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2> > CrazyManifold; + test<CrazyManifold,1>(GeometryTypes::simplex(1)); + + //////////////////////////////////////////////////////////////// + // Test functions on 2d simplex elements + //////////////////////////////////////////////////////////////// + + test<RealTuple<double,1>,2>(GeometryTypes::simplex(2)); + test<UnitVector<double,2>,2>(GeometryTypes::simplex(2)); + test<UnitVector<double,3>,2>(GeometryTypes::simplex(2)); + test<Rotation<double,3>,2>(GeometryTypes::simplex(2)); + test<RigidBodyMotion<double,3>,2>(GeometryTypes::simplex(2)); + typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2> > CrazyManifold; + test<CrazyManifold,2>(GeometryTypes::simplex(2)); + + //////////////////////////////////////////////////////////////// + // Test functions on 2d quadrilateral elements + //////////////////////////////////////////////////////////////// + + test<RealTuple<double,1>,2>(GeometryTypes::cube(2)); + test<UnitVector<double,2>,2>(GeometryTypes::cube(2)); + test<UnitVector<double,3>,2>(GeometryTypes::cube(2)); + test<Rotation<double,3>,2>(GeometryTypes::cube(2)); + test<RigidBodyMotion<double,3>,2>(GeometryTypes::cube(2)); + typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2> > CrazyManifold; + test<CrazyManifold,2>(GeometryTypes::cube(2)); } diff --git a/test/localgfetestfunctiontest.cc b/test/localgfetestfunctiontest.cc index 47ba3738..55a92f96 100644 --- a/test/localgfetestfunctiontest.cc +++ b/test/localgfetestfunctiontest.cc @@ -25,65 +25,65 @@ using namespace Dune; template <class TargetSpace, int domainDim> void test() { - std::cout << " --- Testing " << className<TargetSpace>() << ", domain dimension: " << domainDim << " ---" << std::endl; - - std::vector<TargetSpace> testPoints; - ValueFactory<TargetSpace>::get(testPoints); - - int nTestPoints = testPoints.size(); - - // Set up elements of SO(3) - std::vector<TargetSpace> coefficients(domainDim+1); - - MultiIndex index(domainDim+1, nTestPoints); - int numIndices = index.cycle(); - - LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; - typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; - - for (int i=0; i<numIndices; i++, ++index) { - - for (int j=0; j<domainDim+1; j++) - coefficients[j] = testPoints[index[j]]; - - LocalGfeTestFunctionFiniteElement<LocalFiniteElement,TargetSpace> testFunctionSet(feCache.get(GeometryTypes::simplex(domainDim)),coefficients); - - FieldVector<double,domainDim> stupidTestPoint(0); - - // test whether evaluation of the shape functions works - std::vector<std::array<typename TargetSpace::EmbeddedTangentVector, TargetSpace::TangentVector::dimension> > values; - testFunctionSet.localBasis().evaluateFunction(stupidTestPoint, values); - - // test whether evaluation of the shape function derivatives works - std::vector<std::array<FieldMatrix<double, TargetSpace::EmbeddedTangentVector::dimension, domainDim>, TargetSpace::TangentVector::dimension> > derivatives; - testFunctionSet.localBasis().evaluateJacobian(stupidTestPoint, derivatives); - - } + std::cout << " --- Testing " << className<TargetSpace>() << ", domain dimension: " << domainDim << " ---" << std::endl; + + std::vector<TargetSpace> testPoints; + ValueFactory<TargetSpace>::get(testPoints); + + int nTestPoints = testPoints.size(); + + // Set up elements of SO(3) + std::vector<TargetSpace> coefficients(domainDim+1); + + MultiIndex index(domainDim+1, nTestPoints); + int numIndices = index.cycle(); + + LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; + typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; + + for (int i=0; i<numIndices; i++, ++index) { + + for (int j=0; j<domainDim+1; j++) + coefficients[j] = testPoints[index[j]]; + + LocalGfeTestFunctionFiniteElement<LocalFiniteElement,TargetSpace> testFunctionSet(feCache.get(GeometryTypes::simplex(domainDim)),coefficients); + + FieldVector<double,domainDim> stupidTestPoint(0); + + // test whether evaluation of the shape functions works + std::vector<std::array<typename TargetSpace::EmbeddedTangentVector, TargetSpace::TangentVector::dimension> > values; + testFunctionSet.localBasis().evaluateFunction(stupidTestPoint, values); + + // test whether evaluation of the shape function derivatives works + std::vector<std::array<FieldMatrix<double, TargetSpace::EmbeddedTangentVector::dimension, domainDim>, TargetSpace::TangentVector::dimension> > derivatives; + testFunctionSet.localBasis().evaluateJacobian(stupidTestPoint, derivatives); + + } } int main() try { - // choke on NaN -- don't enable this by default, as there are - // a few harmless NaN in the loopsolver - //feenableexcept(FE_INVALID); - - std::cout << std::setw(15) << std::setprecision(12); - - test<RealTuple<double,1>, 1>(); - - test<UnitVector<double,2>, 1>(); - test<UnitVector<double,3>, 1>(); - test<UnitVector<double,2>, 2>(); - test<UnitVector<double,3>, 2>(); - - test<Rotation<double,3>, 1>(); - test<Rotation<double,3>, 2>(); - typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold; - test<CrazyManifold, 2>(); - -} catch (Exception& e) { - std::cout << e.what() << std::endl; -} + // choke on NaN -- don't enable this by default, as there are + // a few harmless NaN in the loopsolver + //feenableexcept(FE_INVALID); + + std::cout << std::setw(15) << std::setprecision(12); + + test<RealTuple<double,1>, 1>(); + test<UnitVector<double,2>, 1>(); + test<UnitVector<double,3>, 1>(); + test<UnitVector<double,2>, 2>(); + test<UnitVector<double,3>, 2>(); + + test<Rotation<double,3>, 1>(); + test<Rotation<double,3>, 2>(); + typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2> > CrazyManifold; + test<CrazyManifold, 2>(); + +} +catch (Exception& e) { + std::cout << e.what() << std::endl; +} diff --git a/test/localprojectedfefunctiontest.cc b/test/localprojectedfefunctiontest.cc index 142b62ef..58373aba 100644 --- a/test/localprojectedfefunctiontest.cc +++ b/test/localprojectedfefunctiontest.cc @@ -29,11 +29,11 @@ using namespace Dune; template <class TargetSpace> double diameter(const std::vector<TargetSpace>& v) { - double d = 0; - for (size_t i=0; i<v.size(); i++) - for (size_t j=0; j<v.size(); j++) - d = std::max(d, TargetSpace::distance(v[i],v[j])); - return d; + double d = 0; + for (size_t i=0; i<v.size(); i++) + for (size_t j=0; j<v.size(); j++) + d = std::max(d, TargetSpace::distance(v[i],v[j])); + return d; } template <int dim, class ctype, class LocalFunction> @@ -41,27 +41,27 @@ auto evaluateDerivativeFD(const LocalFunction& f, const Dune::FieldVector<ctype, dim>& local) -> decltype(f.evaluateDerivative(local)) { - double eps = 1e-8; - static const int embeddedDim = LocalFunction::TargetSpace::embeddedDim; - Dune::FieldMatrix<ctype, embeddedDim, dim> result; + double eps = 1e-8; + static const int embeddedDim = LocalFunction::TargetSpace::embeddedDim; + Dune::FieldMatrix<ctype, embeddedDim, dim> result; - for (int i=0; i<dim; i++) { + for (int i=0; i<dim; i++) { - Dune::FieldVector<ctype, dim> forward = local; - Dune::FieldVector<ctype, dim> backward = local; + Dune::FieldVector<ctype, dim> forward = local; + Dune::FieldVector<ctype, dim> backward = local; - forward[i] += eps; - backward[i] -= eps; + forward[i] += eps; + backward[i] -= eps; - auto fdDer = f.evaluate(forward).globalCoordinates() - f.evaluate(backward).globalCoordinates(); - fdDer /= 2*eps; + auto fdDer = f.evaluate(forward).globalCoordinates() - f.evaluate(backward).globalCoordinates(); + fdDer /= 2*eps; - for (int j=0; j<embeddedDim; j++) - result[j][i] = fdDer[j]; + for (int j=0; j<embeddedDim; j++) + result[j][i] = fdDer[j]; - } + } - return result; + return result; } @@ -69,7 +69,7 @@ template <int domainDim, int dim> void testDerivativeTangentiality(const RealTuple<double,dim>& x, const FieldMatrix<double,dim,domainDim>& derivative) { - // By construction, derivatives of RealTuples are always tangent + // By construction, derivatives of RealTuples are always tangent } // the columns of the derivative must be tangential to the manifold @@ -77,18 +77,18 @@ template <int domainDim, int vectorDim> void testDerivativeTangentiality(const UnitVector<double,vectorDim>& x, const FieldMatrix<double,vectorDim,domainDim>& derivative) { - for (int i=0; i<domainDim; i++) { + for (int i=0; i<domainDim; i++) { - // The i-th column is a tangent vector if its scalar product with the global coordinates - // of x vanishes. - double sp = 0; - for (int j=0; j<vectorDim; j++) - sp += x.globalCoordinates()[j] * derivative[j][i]; + // The i-th column is a tangent vector if its scalar product with the global coordinates + // of x vanishes. + double sp = 0; + for (int j=0; j<vectorDim; j++) + sp += x.globalCoordinates()[j] * derivative[j][i]; - if (std::fabs(sp) > 1e-8) - DUNE_THROW(Dune::Exception, "Derivative is not tangential: Column: " << i << ", product: " << sp); + if (std::fabs(sp) > 1e-8) + DUNE_THROW(Dune::Exception, "Derivative is not tangential: Column: " << i << ", product: " << sp); - } + } } @@ -96,28 +96,26 @@ void testDerivativeTangentiality(const UnitVector<double,vectorDim>& x, template <int domainDim, int vectorDim> void testDerivativeTangentiality(const Rotation<double,vectorDim-1>& x, const FieldMatrix<double,vectorDim,domainDim>& derivative) -{ -} +{} // the columns of the derivative must be tangential to the manifold template <int domainDim, int vectorDim> void testDerivativeTangentiality(const RigidBodyMotion<double,3>& x, const FieldMatrix<double,vectorDim,domainDim>& derivative) -{ -} +{} // the columns of the derivative must be tangential to the manifold -template <int domainDim, int vectorDim,typename ...TargetSpaces> +template <int domainDim, int vectorDim,typename ... TargetSpaces> void testDerivativeTangentiality(const Dune::GFE::ProductManifold<TargetSpaces...>& x, const FieldMatrix<double,vectorDim,domainDim>& derivative) { - size_t posHelper=0; - using namespace Dune::Hybrid; - forEach(integralRange(Dune::Hybrid::size(x)), [&](auto&& i) { - using Manifold = std::remove_reference_t<decltype(x[i])>; - testDerivativeTangentiality(x[i],Dune::GFE::blockAt<Manifold::embeddedDim,domainDim>( derivative,posHelper,0)); - posHelper +=Manifold::embeddedDim; - }); + size_t posHelper=0; + using namespace Dune::Hybrid; + forEach(integralRange(Dune::Hybrid::size(x)), [&](auto&& i) { + using Manifold = std::remove_reference_t<decltype(x[i])>; + testDerivativeTangentiality(x[i],Dune::GFE::blockAt<Manifold::embeddedDim,domainDim>( derivative,posHelper,0)); + posHelper +=Manifold::embeddedDim; + }); } /** \brief Test whether interpolation is invariant under permutation of the simplex vertices @@ -126,179 +124,179 @@ void testDerivativeTangentiality(const Dune::GFE::ProductManifold<TargetSpaces.. template <int domainDim, class TargetSpace> void testPermutationInvariance(const std::vector<TargetSpace>& corners) { - // works only for 2d domains - if (domainDim!=2) - return; + // works only for 2d domains + if (domainDim!=2) + return; - LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; - typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; + LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; + typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; - GeometryType simplex = GeometryTypes::simplex(domainDim); + GeometryType simplex = GeometryTypes::simplex(domainDim); - // - std::vector<TargetSpace> cornersRotated1(domainDim+1); - std::vector<TargetSpace> cornersRotated2(domainDim+1); + // + std::vector<TargetSpace> cornersRotated1(domainDim+1); + std::vector<TargetSpace> cornersRotated2(domainDim+1); - cornersRotated1[0] = cornersRotated2[2] = corners[1]; - cornersRotated1[1] = cornersRotated2[0] = corners[2]; - cornersRotated1[2] = cornersRotated2[1] = corners[0]; + cornersRotated1[0] = cornersRotated2[2] = corners[1]; + cornersRotated1[1] = cornersRotated2[0] = corners[2]; + cornersRotated1[2] = cornersRotated2[1] = corners[0]; - GFE::LocalProjectedFEFunction<2,double,LocalFiniteElement,TargetSpace> f0(feCache.get(simplex), corners); - GFE::LocalProjectedFEFunction<2,double,LocalFiniteElement,TargetSpace> f1(feCache.get(simplex), cornersRotated1); - GFE::LocalProjectedFEFunction<2,double,LocalFiniteElement,TargetSpace> f2(feCache.get(simplex), cornersRotated2); + GFE::LocalProjectedFEFunction<2,double,LocalFiniteElement,TargetSpace> f0(feCache.get(simplex), corners); + GFE::LocalProjectedFEFunction<2,double,LocalFiniteElement,TargetSpace> f1(feCache.get(simplex), cornersRotated1); + GFE::LocalProjectedFEFunction<2,double,LocalFiniteElement,TargetSpace> f2(feCache.get(simplex), cornersRotated2); - // A quadrature rule as a set of test points - int quadOrder = 3; + // A quadrature rule as a set of test points + int quadOrder = 3; - const Dune::QuadratureRule<double, domainDim>& quad - = Dune::QuadratureRules<double, domainDim>::rule(simplex, quadOrder); + const Dune::QuadratureRule<double, domainDim>& quad + = Dune::QuadratureRules<double, domainDim>::rule(simplex, quadOrder); - for (size_t pt=0; pt<quad.size(); pt++) { + for (size_t pt=0; pt<quad.size(); pt++) { - const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); + const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); - Dune::FieldVector<double,domainDim> l0 = quadPos; - Dune::FieldVector<double,domainDim> l1, l2; + Dune::FieldVector<double,domainDim> l0 = quadPos; + Dune::FieldVector<double,domainDim> l1, l2; - l1[0] = quadPos[1]; - l1[1] = 1-quadPos[0]-quadPos[1]; + l1[0] = quadPos[1]; + l1[1] = 1-quadPos[0]-quadPos[1]; - l2[0] = 1-quadPos[0]-quadPos[1]; - l2[1] = quadPos[0]; + l2[0] = 1-quadPos[0]-quadPos[1]; + l2[1] = quadPos[0]; - // evaluate the three functions - TargetSpace v0 = f0.evaluate(l0); - TargetSpace v1 = f1.evaluate(l1); - TargetSpace v2 = f2.evaluate(l2); + // evaluate the three functions + TargetSpace v0 = f0.evaluate(l0); + TargetSpace v1 = f1.evaluate(l1); + TargetSpace v2 = f2.evaluate(l2); - // Check that they are all equal - assert(TargetSpace::distance(v0,v1) < eps); - assert(TargetSpace::distance(v0,v2) < eps); + // Check that they are all equal + assert(TargetSpace::distance(v0,v1) < eps); + assert(TargetSpace::distance(v0,v2) < eps); - } + } } template <int domainDim, class TargetSpace, bool conforming=true> void testDerivative(const GFE::LocalProjectedFEFunction<domainDim,double,typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType, TargetSpace, conforming>& f) { - static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension; + static const int embeddedDim = TargetSpace::EmbeddedTangentVector::dimension; - // A quadrature rule as a set of test points - int quadOrder = 3; + // A quadrature rule as a set of test points + int quadOrder = 3; - const auto& quad = Dune::QuadratureRules<double, domainDim>::rule(f.type(), quadOrder); + const auto& quad = Dune::QuadratureRules<double, domainDim>::rule(f.type(), quadOrder); - for (size_t pt=0; pt<quad.size(); pt++) { + for (size_t pt=0; pt<quad.size(); pt++) { - const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); + const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); - // evaluate actual derivative - Dune::FieldMatrix<double, embeddedDim, domainDim> derivative = f.evaluateDerivative(quadPos); + // evaluate actual derivative + Dune::FieldMatrix<double, embeddedDim, domainDim> derivative = f.evaluateDerivative(quadPos); - // evaluate fd approximation of derivative - Dune::FieldMatrix<double, embeddedDim, domainDim> fdDerivative = evaluateDerivativeFD(f,quadPos); + // evaluate fd approximation of derivative + Dune::FieldMatrix<double, embeddedDim, domainDim> fdDerivative = evaluateDerivativeFD(f,quadPos); - Dune::FieldMatrix<double, embeddedDim, domainDim> diff = derivative; - diff -= fdDerivative; + Dune::FieldMatrix<double, embeddedDim, domainDim> diff = derivative; + diff -= fdDerivative; - if ( diff.infinity_norm() > 100*eps ) { - std::cout << className<TargetSpace>() << ": Analytical gradient does not match fd approximation." << std::endl; - std::cout << "Analytical: " << derivative << std::endl; - std::cout << "FD : " << fdDerivative << std::endl; - assert(false); - } + if ( diff.infinity_norm() > 100*eps ) { + std::cout << className<TargetSpace>() << ": Analytical gradient does not match fd approximation." << std::endl; + std::cout << "Analytical: " << derivative << std::endl; + std::cout << "FD : " << fdDerivative << std::endl; + assert(false); + } - if(conforming) - testDerivativeTangentiality(f.evaluate(quadPos), derivative); + if(conforming) + testDerivativeTangentiality(f.evaluate(quadPos), derivative); - } + } } template <class TargetSpace, int domainDim> void test(const GeometryType& element) { - std::cout << " --- Testing " << className<TargetSpace>() << ", domain dimension: " << element.dim() << " ---" << std::endl; + std::cout << " --- Testing " << className<TargetSpace>() << ", domain dimension: " << element.dim() << " ---" << std::endl; - std::vector<TargetSpace> testPoints; - ValueFactory<TargetSpace>::get(testPoints); + std::vector<TargetSpace> testPoints; + ValueFactory<TargetSpace>::get(testPoints); - int nTestPoints = testPoints.size(); - size_t nVertices = Dune::ReferenceElements<double,domainDim>::general(element).size(domainDim); + int nTestPoints = testPoints.size(); + size_t nVertices = Dune::ReferenceElements<double,domainDim>::general(element).size(domainDim); - // Set up elements of the target space - std::vector<TargetSpace> corners(nVertices); + // Set up elements of the target space + std::vector<TargetSpace> corners(nVertices); - MultiIndex index(nVertices, nTestPoints); - int numIndices = index.cycle(); + MultiIndex index(nVertices, nTestPoints); + int numIndices = index.cycle(); - for (int i=0; i<numIndices; i++, ++index) { + for (int i=0; i<numIndices; i++, ++index) { - for (size_t j=0; j<nVertices; j++) - corners[j] = testPoints[index[j]]; + for (size_t j=0; j<nVertices; j++) + corners[j] = testPoints[index[j]]; - if (diameter(corners) > 0.5*M_PI) - continue; + if (diameter(corners) > 0.5*M_PI) + continue; - // Make local gfe function to be tested - LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; - typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; + // Make local gfe function to be tested + LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; + typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; - GFE::LocalProjectedFEFunction<domainDim,double,LocalFiniteElement,TargetSpace> f(feCache.get(element),corners); - GFE::LocalProjectedFEFunction<domainDim, double, LocalFiniteElement, TargetSpace,false> f_nonconforming(feCache.get(element), corners); + GFE::LocalProjectedFEFunction<domainDim,double,LocalFiniteElement,TargetSpace> f(feCache.get(element),corners); + GFE::LocalProjectedFEFunction<domainDim, double, LocalFiniteElement, TargetSpace,false> f_nonconforming(feCache.get(element), corners); - //testPermutationInvariance(corners); - testDerivative<domainDim>(f); - testDerivative<domainDim>(f_nonconforming); - } + //testPermutationInvariance(corners); + testDerivative<domainDim>(f); + testDerivative<domainDim>(f_nonconforming); + } } int main() { - // choke on NaN -- don't enable this by default, as there are - // a few harmless NaN in the loopsolver - //feenableexcept(FE_INVALID); - - std::cout << std::setw(15) << std::setprecision(12); - - //////////////////////////////////////////////////////////////// - // Test functions on 1d elements - //////////////////////////////////////////////////////////////// - - test<RealTuple<double,1>,1>(GeometryTypes::line); - test<UnitVector<double,2>,1>(GeometryTypes::line); - test<UnitVector<double,3>,1>(GeometryTypes::line); - test<Rotation<double,3>,1>(GeometryTypes::line); - test<RigidBodyMotion<double,3>,1>(GeometryTypes::line); - typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold; - test<CrazyManifold, 1>(GeometryTypes::line); - - //////////////////////////////////////////////////////////////// - // Test functions on 2d simplex elements - //////////////////////////////////////////////////////////////// - - test<RealTuple<double,1>,2>(GeometryTypes::triangle); - test<UnitVector<double,2>,2>(GeometryTypes::triangle); - test<RealTuple<double,3>,2>(GeometryTypes::triangle); - test<UnitVector<double,3>,2>(GeometryTypes::triangle); - test<Rotation<double,3>,2>(GeometryTypes::triangle); - test<RigidBodyMotion<double,3>,2>(GeometryTypes::triangle); - typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold; - test<CrazyManifold, 2>(GeometryTypes::triangle); - - //////////////////////////////////////////////////////////////// - // Test functions on 2d quadrilateral elements - //////////////////////////////////////////////////////////////// - - test<RealTuple<double,1>,2>(GeometryTypes::quadrilateral); - test<UnitVector<double,2>,2>(GeometryTypes::quadrilateral); - test<UnitVector<double,3>,2>(GeometryTypes::quadrilateral); - test<Rotation<double,3>,2>(GeometryTypes::quadrilateral); - test<RigidBodyMotion<double,3>,2>(GeometryTypes::quadrilateral); - typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>> CrazyManifold; - test<CrazyManifold, 2>(GeometryTypes::quadrilateral); + // choke on NaN -- don't enable this by default, as there are + // a few harmless NaN in the loopsolver + //feenableexcept(FE_INVALID); + + std::cout << std::setw(15) << std::setprecision(12); + + //////////////////////////////////////////////////////////////// + // Test functions on 1d elements + //////////////////////////////////////////////////////////////// + + test<RealTuple<double,1>,1>(GeometryTypes::line); + test<UnitVector<double,2>,1>(GeometryTypes::line); + test<UnitVector<double,3>,1>(GeometryTypes::line); + test<Rotation<double,3>,1>(GeometryTypes::line); + test<RigidBodyMotion<double,3>,1>(GeometryTypes::line); + typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2> > CrazyManifold; + test<CrazyManifold, 1>(GeometryTypes::line); + + //////////////////////////////////////////////////////////////// + // Test functions on 2d simplex elements + //////////////////////////////////////////////////////////////// + + test<RealTuple<double,1>,2>(GeometryTypes::triangle); + test<UnitVector<double,2>,2>(GeometryTypes::triangle); + test<RealTuple<double,3>,2>(GeometryTypes::triangle); + test<UnitVector<double,3>,2>(GeometryTypes::triangle); + test<Rotation<double,3>,2>(GeometryTypes::triangle); + test<RigidBodyMotion<double,3>,2>(GeometryTypes::triangle); + typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2> > CrazyManifold; + test<CrazyManifold, 2>(GeometryTypes::triangle); + + //////////////////////////////////////////////////////////////// + // Test functions on 2d quadrilateral elements + //////////////////////////////////////////////////////////////// + + test<RealTuple<double,1>,2>(GeometryTypes::quadrilateral); + test<UnitVector<double,2>,2>(GeometryTypes::quadrilateral); + test<UnitVector<double,3>,2>(GeometryTypes::quadrilateral); + test<Rotation<double,3>,2>(GeometryTypes::quadrilateral); + test<RigidBodyMotion<double,3>,2>(GeometryTypes::quadrilateral); + typedef Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2> > CrazyManifold; + test<CrazyManifold, 2>(GeometryTypes::quadrilateral); } diff --git a/test/multiindex.hh b/test/multiindex.hh index f9b9fad3..51b80d4e 100644 --- a/test/multiindex.hh +++ b/test/multiindex.hh @@ -4,51 +4,51 @@ #include <vector> /** \brief A multi-index -*/ + */ class MultiIndex - : public std::vector<unsigned int> + : public std::vector<unsigned int> { - // The range of each component - unsigned int limit_; + // The range of each component + unsigned int limit_; public: - /** \brief Constructor with a given range for each digit - * \param n Number of digits - */ - MultiIndex(unsigned int n, unsigned int limit) - : std::vector<unsigned int>(n), - limit_(limit) - { - std::fill(this->begin(), this->end(), 0); - } + /** \brief Constructor with a given range for each digit + * \param n Number of digits + */ + MultiIndex(unsigned int n, unsigned int limit) + : std::vector<unsigned int>(n), + limit_(limit) + { + std::fill(this->begin(), this->end(), 0); + } - /** \brief Increment the MultiIndex */ - MultiIndex& operator++() { + /** \brief Increment the MultiIndex */ + MultiIndex& operator++() { - for (size_t i=0; i<size(); i++) { + for (size_t i=0; i<size(); i++) { - // Augment digit - (*this)[i]++; + // Augment digit + (*this)[i]++; - // If there is no carry-over we can stop here - if ((*this)[i]<limit_) - break; + // If there is no carry-over we can stop here + if ((*this)[i]<limit_) + break; - (*this)[i] = 0; - - } - return *this; - } + (*this)[i] = 0; - /** \brief Compute how many times you can call operator++ before getting to (0,...,0) again */ - size_t cycle() const { - size_t result = 1; - for (size_t i=0; i<size(); i++) - result *= limit_; - return result; } + return *this; + } + + /** \brief Compute how many times you can call operator++ before getting to (0,...,0) again */ + size_t cycle() const { + size_t result = 1; + for (size_t i=0; i<size(); i++) + result *= limit_; + return result; + } }; -#endif \ No newline at end of file +#endif diff --git a/test/nonplanarcosseratenergytest.cc b/test/nonplanarcosseratenergytest.cc index 17c3c3f9..ddb2ee83 100644 --- a/test/nonplanarcosseratenergytest.cc +++ b/test/nonplanarcosseratenergytest.cc @@ -33,20 +33,20 @@ using TargetSpace = RigidBodyMotion<double,dimworld>; template <class GridType> std::unique_ptr<GridType> makeSingleElementGrid() { - constexpr auto triangle = Dune::GeometryTypes::triangle; - GridFactory<GridType> factory; + constexpr auto triangle = Dune::GeometryTypes::triangle; + GridFactory<GridType> factory; - //Create a triangle that is not parallel to the planes formed by the coordinate axes - FieldVector<double,dimworld> vertex0{0,0,0}; - FieldVector<double,dimworld> vertex1{0,1,1}; - FieldVector<double,dimworld> vertex2{1,0,0}; - factory.insertVertex(vertex0); - factory.insertVertex(vertex1); - factory.insertVertex(vertex2); + //Create a triangle that is not parallel to the planes formed by the coordinate axes + FieldVector<double,dimworld> vertex0{0,0,0}; + FieldVector<double,dimworld> vertex1{0,1,1}; + FieldVector<double,dimworld> vertex2{1,0,0}; + factory.insertVertex(vertex0); + factory.insertVertex(vertex1); + factory.insertVertex(vertex2); - factory.insertElement(triangle, {0,1,2}); + factory.insertElement(triangle, {0,1,2}); - return std::unique_ptr<GridType>(factory.createGrid()); + return std::unique_ptr<GridType>(factory.createGrid()); } @@ -56,132 +56,134 @@ std::unique_ptr<GridType> makeSingleElementGrid() template <class F1, class F2> double calculateEnergy(const int numLevels, const F1 referenceConfigurationFunction, const F2 configurationFunction) { - ParameterTree materialParameters; - materialParameters["thickness"] = "0.1"; - materialParameters["mu"] = "3.8462e+05"; - materialParameters["lambda"] = "2.7149e+05"; - materialParameters["mu_c"] = "0"; - materialParameters["L_c"] = "1e-3"; - materialParameters["q"] = "2.5"; - materialParameters["kappa"] = "0.1"; - materialParameters["b1"] = "1"; - materialParameters["b2"] = "1"; - materialParameters["b3"] = "1"; - - const std::unique_ptr<GridType> grid = makeSingleElementGrid<GridType>(); - grid->globalRefine(numLevels-1); - GridType::LeafGridView gridView = grid->leafGridView(); - - using FEBasis = Dune::Functions::LagrangeBasis<typename GridType::LeafGridView,2>; - FEBasis feBasis(gridView); - - using namespace Dune::Functions::BasisFactory; - using namespace Dune::TypeTree::Indices; - - auto deformationPowerBasis = makeBasis( - gridView, - power<dimworld>( - lagrange<2>() - )); - - BlockVector<FieldVector<double,3> > helperVector1(feBasis.size()); - Dune::Functions::interpolate(deformationPowerBasis, helperVector1, referenceConfigurationFunction); - auto stressFreeConfiguration = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dimworld>>(deformationPowerBasis, helperVector1); - - NonplanarCosseratShellEnergy<FEBasis, 3, double, decltype(stressFreeConfiguration)> nonplanarCosseratShellEnergy(materialParameters, - &stressFreeConfiguration, - nullptr, - nullptr, - nullptr); - BlockVector<TargetSpace> sol(feBasis.size()); - TupleVector<std::vector<RealTuple<double,3> >, - std::vector<Rotation<double,3> > > solTuple; - solTuple[_0].resize(feBasis.size()); - solTuple[_1].resize(feBasis.size()); - - BlockVector<FieldVector<double,3> > helperVector2(feBasis.size()); - Dune::Functions::interpolate(deformationPowerBasis, helperVector2, configurationFunction); - for (std::size_t i = 0; i < feBasis.size(); i++) { - for (int j = 0; j < dimworld; j++) - sol[i].r[j] = helperVector2[i][j]; - - FieldVector<double,4> idRotation = {0, 0, 0, 1}; //set rotation = Id everywhere - Rotation<double,dimworld> rotation(idRotation); - FieldMatrix<double,dimworld,dimworld> rotationMatrix(0); - rotation.matrix(rotationMatrix); - sol[i].q.set(rotationMatrix); - solTuple[_0][i] = sol[i].r; - solTuple[_1][i] = sol[i].q; - } - CosseratVTKWriter<GridType>::write<FEBasis>(feBasis, solTuple, "configuration_l" + std::to_string(numLevels)); - - double energy = 0; - // A view on the FE basis on a single element - auto localView = feBasis.localView(); - // Loop over all elements - for (const auto& element : elements(feBasis.gridView(), Dune::Partitions::interior)) { - localView.bind(element); - // Number of degrees of freedom on this element - size_t nDofs = localView.tree().size(); - std::vector<TargetSpace> localSolution(nDofs); - for (size_t i=0; i<nDofs; i++) - localSolution[i] = sol[localView.index(i)[0]]; - energy += nonplanarCosseratShellEnergy.energy(localView, localSolution); - } - return energy; + ParameterTree materialParameters; + materialParameters["thickness"] = "0.1"; + materialParameters["mu"] = "3.8462e+05"; + materialParameters["lambda"] = "2.7149e+05"; + materialParameters["mu_c"] = "0"; + materialParameters["L_c"] = "1e-3"; + materialParameters["q"] = "2.5"; + materialParameters["kappa"] = "0.1"; + materialParameters["b1"] = "1"; + materialParameters["b2"] = "1"; + materialParameters["b3"] = "1"; + + const std::unique_ptr<GridType> grid = makeSingleElementGrid<GridType>(); + grid->globalRefine(numLevels-1); + GridType::LeafGridView gridView = grid->leafGridView(); + + using FEBasis = Dune::Functions::LagrangeBasis<typename GridType::LeafGridView,2>; + FEBasis feBasis(gridView); + + using namespace Dune::Functions::BasisFactory; + using namespace Dune::TypeTree::Indices; + + auto deformationPowerBasis = makeBasis( + gridView, + power<dimworld>( + lagrange<2>() + )); + + BlockVector<FieldVector<double,3> > helperVector1(feBasis.size()); + Dune::Functions::interpolate(deformationPowerBasis, helperVector1, referenceConfigurationFunction); + auto stressFreeConfiguration = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dimworld> >(deformationPowerBasis, helperVector1); + + NonplanarCosseratShellEnergy<FEBasis, 3, double, decltype(stressFreeConfiguration)> nonplanarCosseratShellEnergy(materialParameters, + &stressFreeConfiguration, + nullptr, + nullptr, + nullptr); + BlockVector<TargetSpace> sol(feBasis.size()); + TupleVector<std::vector<RealTuple<double,3> >, + std::vector<Rotation<double,3> > > solTuple; + solTuple[_0].resize(feBasis.size()); + solTuple[_1].resize(feBasis.size()); + + BlockVector<FieldVector<double,3> > helperVector2(feBasis.size()); + Dune::Functions::interpolate(deformationPowerBasis, helperVector2, configurationFunction); + for (std::size_t i = 0; i < feBasis.size(); i++) { + for (int j = 0; j < dimworld; j++) + sol[i].r[j] = helperVector2[i][j]; + + FieldVector<double,4> idRotation = {0, 0, 0, 1}; //set rotation = Id everywhere + Rotation<double,dimworld> rotation(idRotation); + FieldMatrix<double,dimworld,dimworld> rotationMatrix(0); + rotation.matrix(rotationMatrix); + sol[i].q.set(rotationMatrix); + solTuple[_0][i] = sol[i].r; + solTuple[_1][i] = sol[i].q; + } + CosseratVTKWriter<GridType>::write<FEBasis>(feBasis, solTuple, "configuration_l" + std::to_string(numLevels)); + + double energy = 0; + // A view on the FE basis on a single element + auto localView = feBasis.localView(); + // Loop over all elements + for (const auto& element : elements(feBasis.gridView(), Dune::Partitions::interior)) { + localView.bind(element); + // Number of degrees of freedom on this element + size_t nDofs = localView.tree().size(); + std::vector<TargetSpace> localSolution(nDofs); + for (size_t i=0; i<nDofs; i++) + localSolution[i] = sol[localView.index(i)[0]]; + energy += nonplanarCosseratShellEnergy.energy(localView, localSolution); + } + return energy; } int main(int argc, char** argv) { - MPIHelper::instance(argc, argv); - auto configurationId = [](FieldVector<double,dimworld> x){ return x; }; - auto configurationStretchY = [](FieldVector<double,dimworld> x){ - auto out = x; - out[1] *= 2; - return out; - }; - - auto configurationTwist = [](FieldVector<double,dimworld> x){ - auto out = x; - out[1] = x[2]; - out[2] = -x[1]; - return out; - }; - - auto configurationCurved = [](FieldVector<double,dimworld> x){ - auto out = x; - out[1] = x[2]; - out[2] = -x[1]; - return out; - }; - auto configurationSquare = [](FieldVector<double,dimworld> x){ - auto out = x; - out[1] += x[1]*x[1]; - return out; - }; - - auto configurationSin = [](FieldVector<double,dimworld> x){ - auto out = x; - out[2] = sin(x[2]); - return out; - }; - - double energyFine = calculateEnergy(2, configurationId, configurationStretchY); - double energyCoarse = calculateEnergy(1, configurationId, configurationStretchY); - assert(std::abs(energyFine - energyCoarse) < 1e-3); - - double energyForZeroDifference = calculateEnergy(1,configurationId,configurationId); - assert(std::abs(energyForZeroDifference) < 1e-3); - - double energyForZeroDifference2 = calculateEnergy(1,configurationStretchY,configurationStretchY); - assert(std::abs(energyForZeroDifference2) < 1e-3); - - double energyForZeroDifference3 = calculateEnergy(1,configurationTwist,configurationTwist); - assert(std::abs(energyForZeroDifference3) < 1e-3); - - double energyForZeroDifference4 = calculateEnergy(1,configurationSquare,configurationSquare); - assert(std::abs(energyForZeroDifference4) < 1e-3); - - double energyForZeroDifference5 = calculateEnergy(1,configurationSin,configurationSin); - assert(std::abs(energyForZeroDifference5) < 1e-3); + MPIHelper::instance(argc, argv); + auto configurationId = [](FieldVector<double,dimworld> x){ + return x; + }; + auto configurationStretchY = [](FieldVector<double,dimworld> x){ + auto out = x; + out[1] *= 2; + return out; + }; + + auto configurationTwist = [](FieldVector<double,dimworld> x){ + auto out = x; + out[1] = x[2]; + out[2] = -x[1]; + return out; + }; + + auto configurationCurved = [](FieldVector<double,dimworld> x){ + auto out = x; + out[1] = x[2]; + out[2] = -x[1]; + return out; + }; + auto configurationSquare = [](FieldVector<double,dimworld> x){ + auto out = x; + out[1] += x[1]*x[1]; + return out; + }; + + auto configurationSin = [](FieldVector<double,dimworld> x){ + auto out = x; + out[2] = sin(x[2]); + return out; + }; + + double energyFine = calculateEnergy(2, configurationId, configurationStretchY); + double energyCoarse = calculateEnergy(1, configurationId, configurationStretchY); + assert(std::abs(energyFine - energyCoarse) < 1e-3); + + double energyForZeroDifference = calculateEnergy(1,configurationId,configurationId); + assert(std::abs(energyForZeroDifference) < 1e-3); + + double energyForZeroDifference2 = calculateEnergy(1,configurationStretchY,configurationStretchY); + assert(std::abs(energyForZeroDifference2) < 1e-3); + + double energyForZeroDifference3 = calculateEnergy(1,configurationTwist,configurationTwist); + assert(std::abs(energyForZeroDifference3) < 1e-3); + + double energyForZeroDifference4 = calculateEnergy(1,configurationSquare,configurationSquare); + assert(std::abs(energyForZeroDifference4) < 1e-3); + + double energyForZeroDifference5 = calculateEnergy(1,configurationSin,configurationSin); + assert(std::abs(energyForZeroDifference5) < 1e-3); } diff --git a/test/orthogonalmatrixtest.cc b/test/orthogonalmatrixtest.cc index 31f13e67..c1e0ac2c 100644 --- a/test/orthogonalmatrixtest.cc +++ b/test/orthogonalmatrixtest.cc @@ -7,7 +7,7 @@ /** \file \brief Unit tests for the OrthogonalMatrix class -*/ + */ using namespace Dune; @@ -18,88 +18,88 @@ double eps = 1e-6; template <class T, int N> void testOrthogonality(const OrthogonalMatrix<T,N>& p) { - Dune::FieldMatrix<T,N,N> prod(0); - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - for (int k=0; k<N; k++) - prod[i][j] += p.data()[k][i] * p.data()[k][j]; - - for (int i=0; i<N; i++) - for (int j=0; j<N; j++) - if ( std::abs(prod[i][j] - (i==j) ) > eps) - DUNE_THROW(Exception, "Matrix is not orthogonal"); + Dune::FieldMatrix<T,N,N> prod(0); + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + for (int k=0; k<N; k++) + prod[i][j] += p.data()[k][i] * p.data()[k][j]; + + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + if ( std::abs(prod[i][j] - (i==j) ) > eps) + DUNE_THROW(Exception, "Matrix is not orthogonal"); } - + /** \brief Test orthogonal projection onto the tangent space at p */ template <class T, int N> void testProjectionOntoTangentSpace(const OrthogonalMatrix<T,N>& p) { - std::vector<FieldMatrix<T,N,N> > testVectors; - ValueFactory<FieldMatrix<T,N,N> >::get(testVectors); - - // Test each element in the list - for (size_t i=0; i<testVectors.size(); i++) { - - // get projection - FieldMatrix<T,N,N> projected = p.projectOntoTangentSpace(testVectors[i]); - - // Test whether the tangent vector is really tangent. - // The tangent space at q consist of all matrices of the form q * (a skew matrix). - // Hence we left-multiply by q^T and check whether the result is skew-symmetric. - FieldMatrix<T,N,N> skewPart(0); - for (int j=0; j<N; j++) - for (int k=0; k<N; k++) - for (int l=0; l<N; l++) - skewPart[j][k] += p.data()[l][j] * projected[l][k]; - - // is it skew? - for (int j=0; j<N; j++) - for (int k=0; k<=j; k++) - if ( std::abs(skewPart[j][k] + skewPart[k][j]) > eps ) { - - std::cout << "matrix is not skew-symmetric\n" << skewPart << std::endl; - abort(); - - } - } - - + std::vector<FieldMatrix<T,N,N> > testVectors; + ValueFactory<FieldMatrix<T,N,N> >::get(testVectors); + + // Test each element in the list + for (size_t i=0; i<testVectors.size(); i++) { + + // get projection + FieldMatrix<T,N,N> projected = p.projectOntoTangentSpace(testVectors[i]); + + // Test whether the tangent vector is really tangent. + // The tangent space at q consist of all matrices of the form q * (a skew matrix). + // Hence we left-multiply by q^T and check whether the result is skew-symmetric. + FieldMatrix<T,N,N> skewPart(0); + for (int j=0; j<N; j++) + for (int k=0; k<N; k++) + for (int l=0; l<N; l++) + skewPart[j][k] += p.data()[l][j] * projected[l][k]; + + // is it skew? + for (int j=0; j<N; j++) + for (int k=0; k<=j; k++) + if ( std::abs(skewPart[j][k] + skewPart[k][j]) > eps ) { + + std::cout << "matrix is not skew-symmetric\n" << skewPart << std::endl; + abort(); + + } + } + + } template <class T, int N> void test() { - std::cout << "Testing class " << className<OrthogonalMatrix<T,N> >() << std::endl; - - // Get set of orthogonal test matrices - std::vector<OrthogonalMatrix<T,N> > testPoints; - ValueFactory<OrthogonalMatrix<T,N> >::get(testPoints); - - int nTestPoints = testPoints.size(); - - // Test each element in the list - for (int i=0; i<nTestPoints; i++) { - - // Test whether the matrix really is orthogonal - testOrthogonality(testPoints[i]); - - testProjectionOntoTangentSpace(testPoints[i]); - - } - + std::cout << "Testing class " << className<OrthogonalMatrix<T,N> >() << std::endl; + + // Get set of orthogonal test matrices + std::vector<OrthogonalMatrix<T,N> > testPoints; + ValueFactory<OrthogonalMatrix<T,N> >::get(testPoints); + + int nTestPoints = testPoints.size(); + + // Test each element in the list + for (int i=0; i<nTestPoints; i++) { + + // Test whether the matrix really is orthogonal + testOrthogonality(testPoints[i]); + + testProjectionOntoTangentSpace(testPoints[i]); + + } + } int main() try { - test<double,1>(); - test<double,2>(); - test<double,3>(); - -} catch (Exception& e) { - - std::cout << e.what() << std::endl; + test<double,1>(); + test<double,2>(); + test<double,3>(); } +catch (Exception& e) { + std::cout << e.what() << std::endl; + +} diff --git a/test/polardecompositiontest.cc b/test/polardecompositiontest.cc index b4c87f6a..7ab8a775 100644 --- a/test/polardecompositiontest.cc +++ b/test/polardecompositiontest.cc @@ -7,7 +7,7 @@ #include <chrono> #include <fstream> -#include <random> +#include <random> using namespace Dune; @@ -15,228 +15,228 @@ using field_type = double; class PolarDecompositionTest : public Dune::GFE::HighamNoferiniPolarDecomposition { - public: - using HighamNoferiniPolarDecomposition::HighamNoferiniPolarDecomposition; - using HighamNoferiniPolarDecomposition::bilinearMatrix; - using HighamNoferiniPolarDecomposition::determinantLaplace; - using HighamNoferiniPolarDecomposition::dominantEVNewton; - using HighamNoferiniPolarDecomposition::characteristicCoefficients; - using HighamNoferiniPolarDecomposition::obtainQuaternion; +public: + using HighamNoferiniPolarDecomposition::HighamNoferiniPolarDecomposition; + using HighamNoferiniPolarDecomposition::bilinearMatrix; + using HighamNoferiniPolarDecomposition::determinantLaplace; + using HighamNoferiniPolarDecomposition::dominantEVNewton; + using HighamNoferiniPolarDecomposition::characteristicCoefficients; + using HighamNoferiniPolarDecomposition::obtainQuaternion; }; /** \brief Check if the matrix is not orthogonal */ static bool isNotOrthogonal(const FieldMatrix<field_type,3,3>& matrix) { - bool notOrthogonal = std::abs(1-matrix.determinant()) > 1e-12; - notOrthogonal = notOrthogonal or (( matrix[0]*matrix[1] ) > 1e-12); - notOrthogonal = notOrthogonal or (( matrix[0]*matrix[2] ) > 1e-12); - notOrthogonal = notOrthogonal or (( matrix[1]*matrix[2] ) > 1e-12); - return notOrthogonal; + bool notOrthogonal = std::abs(1-matrix.determinant()) > 1e-12; + notOrthogonal = notOrthogonal or (( matrix[0]*matrix[1] ) > 1e-12); + notOrthogonal = notOrthogonal or (( matrix[0]*matrix[2] ) > 1e-12); + notOrthogonal = notOrthogonal or (( matrix[1]*matrix[2] ) > 1e-12); + return notOrthogonal; } /** \brief Return the time needed for 1000 polar decompositions of the given matrix using the iterative algorithm and the algorithm by Higham. */ static FieldVector<field_type,2> measureTimeForPolarDecomposition(const FieldMatrix<field_type, 3, 3>& matrix, double tol = 10e-3) { - FieldMatrix<field_type, 3, 3> Q1; - FieldMatrix<field_type, 3, 3> Q2; - FieldVector<field_type,2> actualtime; - std::chrono::steady_clock::time_point begindecomold = std::chrono::steady_clock::now(); - for (int i = 0; i < 1000; ++i) { - Q1 = Dune::GFE::PolarDecomposition()(matrix, tol); - } - std::chrono::steady_clock::time_point enddecomold = std::chrono::steady_clock::now(); - actualtime[0] = (enddecomold - begindecomold).count(); - - std::chrono::steady_clock::time_point begindecom = std::chrono::steady_clock::now(); - for (int i = 0; i < 1000; ++i) { - Q2 = Dune::GFE::HighamNoferiniPolarDecomposition()(matrix, tol, tol); - } - std::chrono::steady_clock::time_point enddecom = std::chrono::steady_clock::now(); - actualtime[1] = (enddecom - begindecom).count(); - return actualtime; + FieldMatrix<field_type, 3, 3> Q1; + FieldMatrix<field_type, 3, 3> Q2; + FieldVector<field_type,2> actualtime; + std::chrono::steady_clock::time_point begindecomold = std::chrono::steady_clock::now(); + for (int i = 0; i < 1000; ++i) { + Q1 = Dune::GFE::PolarDecomposition()(matrix, tol); + } + std::chrono::steady_clock::time_point enddecomold = std::chrono::steady_clock::now(); + actualtime[0] = (enddecomold - begindecomold).count(); + + std::chrono::steady_clock::time_point begindecom = std::chrono::steady_clock::now(); + for (int i = 0; i < 1000; ++i) { + Q2 = Dune::GFE::HighamNoferiniPolarDecomposition()(matrix, tol, tol); + } + std::chrono::steady_clock::time_point enddecom = std::chrono::steady_clock::now(); + actualtime[1] = (enddecom - begindecom).count(); + return actualtime; } /** \brief Returns a 3x3-Matrix with random entries between 0 and upper Bound*/ static FieldMatrix<field_type, 3, 3> randomMatrix3d(double upperBound = 1.0) { - const int dim = 3; - FieldMatrix<field_type, dim, dim> matrix; - std::random_device rd; // Will be used to obtain a seed for the random number engine - std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() - std::uniform_real_distribution<> dis(0.0, upperBound); // equally distributed between 0 and upper bound - for (int i = 0; i < dim; ++i) - for (int j = 0; j < dim; ++j) - matrix[i][j] = dis(gen); - - return matrix; + const int dim = 3; + FieldMatrix<field_type, dim, dim> matrix; + std::random_device rd; // Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() + std::uniform_real_distribution<> dis(0.0, upperBound); // equally distributed between 0 and upper bound + for (int i = 0; i < dim; ++i) + for (int j = 0; j < dim; ++j) + matrix[i][j] = dis(gen); + + return matrix; } /** \brief Returns an "almost orthogonal" 3x3-Matrix where a random perturbation between 0 and maxPerturbation is added to each entry.*/ static FieldMatrix<field_type, 3, 3> randomMatrixAlmostOrthogonal(double maxPerturbation = 0.1) { - const int dim = 3; - FieldVector<field_type,4> f; - std::random_device rd; // Will be used to obtain a seed for the random number engine - std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() - std::uniform_real_distribution<> dis(0.0, 1.0); // equally distributed between 0 and upper bound - for (int i = 0; i < 4; ++i) - f[i] = dis(gen); - Rotation<field_type,3> q(f); - q.normalize(); - - assert(std::abs(1-q.two_norm()) < 1e-12); - - // Turn it into a matrix - FieldMatrix<double,3,3> matrix; - q.matrix(matrix); - - if (isNotOrthogonal(matrix)) - DUNE_THROW(Exception, "Expected the matrix " << matrix << " to be orthogonal, but it is not!"); - - //Now perturb this a little bit - for (int i = 0; i < dim; ++i) - for (int j = 0; j < dim; ++j) - matrix[i][j] += dis(gen)*maxPerturbation; - return matrix; + const int dim = 3; + FieldVector<field_type,4> f; + std::random_device rd; // Will be used to obtain a seed for the random number engine + std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd() + std::uniform_real_distribution<> dis(0.0, 1.0); // equally distributed between 0 and upper bound + for (int i = 0; i < 4; ++i) + f[i] = dis(gen); + Rotation<field_type,3> q(f); + q.normalize(); + + assert(std::abs(1-q.two_norm()) < 1e-12); + + // Turn it into a matrix + FieldMatrix<double,3,3> matrix; + q.matrix(matrix); + + if (isNotOrthogonal(matrix)) + DUNE_THROW(Exception, "Expected the matrix " << matrix << " to be orthogonal, but it is not!"); + + //Now perturb this a little bit + for (int i = 0; i < dim; ++i) + for (int j = 0; j < dim; ++j) + matrix[i][j] += dis(gen)*maxPerturbation; + return matrix; } static double timeTest(double perturbationFromSO3 = 1.0) { - // Define matrices we want to use for testing - int numberOfTests = 100; - std::vector<double> timeOld(numberOfTests); - std::vector<double> timeNew(numberOfTests); - double totalTimeOld = 0; - double totalTimeNew = 0; - - for (int j = 0; j < numberOfTests; ++j) { // testing loop - FieldMatrix<field_type,3,3> N; - // Only measure the time if the decomposition is unique and if both algorithms will return an orthogonal matrix! - // Attention: For matrices that are quite far away from an orthogonal matrix, Dune::GFE::PolarDecomposition() might return a matrix with determinant = -1 ! - double normOfDifference = 10; - FieldMatrix<field_type,3,3> Q1; - FieldMatrix<field_type,3,3> Q2; - while(isNotOrthogonal(Q1) or isNotOrthogonal(Q2) or normOfDifference > 0.001) { - N = randomMatrixAlmostOrthogonal(perturbationFromSO3); - Q1 = PolarDecompositionTest()(N); - Q2 = Dune::GFE::PolarDecomposition()(N); - normOfDifference = (Q1-Q2).frobenius_norm(); - } - auto actualtime = measureTimeForPolarDecomposition(N); - timeOld[j] = actualtime[0]; - timeNew[j] = actualtime[1]; + // Define matrices we want to use for testing + int numberOfTests = 100; + std::vector<double> timeOld(numberOfTests); + std::vector<double> timeNew(numberOfTests); + double totalTimeOld = 0; + double totalTimeNew = 0; + + for (int j = 0; j < numberOfTests; ++j) { // testing loop + FieldMatrix<field_type,3,3> N; + // Only measure the time if the decomposition is unique and if both algorithms will return an orthogonal matrix! + // Attention: For matrices that are quite far away from an orthogonal matrix, Dune::GFE::PolarDecomposition() might return a matrix with determinant = -1 ! + double normOfDifference = 10; + FieldMatrix<field_type,3,3> Q1; + FieldMatrix<field_type,3,3> Q2; + while(isNotOrthogonal(Q1) or isNotOrthogonal(Q2) or normOfDifference > 0.001) { + N = randomMatrixAlmostOrthogonal(perturbationFromSO3); + Q1 = PolarDecompositionTest()(N); + Q2 = Dune::GFE::PolarDecomposition()(N); + normOfDifference = (Q1-Q2).frobenius_norm(); } - - std::sort(timeOld.begin(), timeOld.end()); - std::sort(timeNew.begin(), timeNew.end()); - - for (int j = 0; j < numberOfTests; ++j) { - totalTimeOld += timeOld[j]; - totalTimeNew += timeNew[j]; - } - std::cout << "Perturbation from an orthogonal matrix: " << perturbationFromSO3 << std::endl; - std::cout << "Average (old): " << totalTimeOld/numberOfTests << "[ns]" << std::endl; - std::cout << "Average (new): " << totalTimeNew/numberOfTests << "[ns]" << std::endl; - std::cout << "Relative: " << totalTimeOld/totalTimeNew << std::endl << std::endl; - - return totalTimeOld/totalTimeNew; + auto actualtime = measureTimeForPolarDecomposition(N); + timeOld[j] = actualtime[0]; + timeNew[j] = actualtime[1]; + } + + std::sort(timeOld.begin(), timeOld.end()); + std::sort(timeNew.begin(), timeNew.end()); + + for (int j = 0; j < numberOfTests; ++j) { + totalTimeOld += timeOld[j]; + totalTimeNew += timeNew[j]; + } + std::cout << "Perturbation from an orthogonal matrix: " << perturbationFromSO3 << std::endl; + std::cout << "Average (old): " << totalTimeOld/numberOfTests << "[ns]" << std::endl; + std::cout << "Average (new): " << totalTimeNew/numberOfTests << "[ns]" << std::endl; + std::cout << "Relative: " << totalTimeOld/totalTimeNew << std::endl << std::endl; + + return totalTimeOld/totalTimeNew; } static bool testBilinearMatrix() { - FieldMatrix<field_type,3,3> M = { { 20, 0, -3 }, - { 3, 2.0, 310 }, - { 5, 0.22, 0 } }; - M /= M.frobenius_norm(); - FieldMatrix<field_type,4,4> B = { { 0.0096632, 0.997822, 0.0257685, 0.00644213 }, - { 0.997822, -0.00322107, 0.0708635, 0.00644213 }, - { 0.0257685, 0.0708635, 0.00322107, 0.999239 }, - { 0.00644213, 0.00644213, 0.999239, -0.0096632 } }; - auto Btest = PolarDecompositionTest::bilinearMatrix(M); - Btest -= B; - return Btest.frobenius_norm() < 0.001; + FieldMatrix<field_type,3,3> M = { { 20, 0, -3 }, + { 3, 2.0, 310 }, + { 5, 0.22, 0 } }; + M /= M.frobenius_norm(); + FieldMatrix<field_type,4,4> B = { { 0.0096632, 0.997822, 0.0257685, 0.00644213 }, + { 0.997822, -0.00322107, 0.0708635, 0.00644213 }, + { 0.0257685, 0.0708635, 0.00322107, 0.999239 }, + { 0.00644213, 0.00644213, 0.999239, -0.0096632 } }; + auto Btest = PolarDecompositionTest::bilinearMatrix(M); + Btest -= B; + return Btest.frobenius_norm() < 0.001; } static bool test4dDeterminant() { - FieldMatrix<field_type,4,4> B = { { 0.0096632, 0.997822, 0.0257685, 0.00644213 }, - { 0.997822, -0.00322107, 0.0708635, 0.00644213 }, - { 0.0257685, 0.0708635, 0.00322107, 0.999239 }, - { 0.00644213, 0.00644213, 0.999239, -0.0096632 } }; - double detBtest = PolarDecompositionTest::determinantLaplace(B); - detBtest -= B.determinant(); - return std::abs(detBtest) < 0.001; + FieldMatrix<field_type,4,4> B = { { 0.0096632, 0.997822, 0.0257685, 0.00644213 }, + { 0.997822, -0.00322107, 0.0708635, 0.00644213 }, + { 0.0257685, 0.0708635, 0.00322107, 0.999239 }, + { 0.00644213, 0.00644213, 0.999239, -0.0096632 } }; + double detBtest = PolarDecompositionTest::determinantLaplace(B); + detBtest -= B.determinant(); + return std::abs(detBtest) < 0.001; } static bool testdominantEVNewton() { - field_type detB = 0.992928; - FieldVector<field_type,3> minpol = {0, -1.99999, -0.00496083}; - double correctDominantEV = 1.05397; + field_type detB = 0.992928; + FieldVector<field_type,3> minpol = {0, -1.99999, -0.00496083}; + double correctDominantEV = 1.05397; - auto dominantEVNewton = PolarDecompositionTest::dominantEVNewton(minpol, detB); - return std::abs(correctDominantEV - dominantEVNewton) < 0.001; + auto dominantEVNewton = PolarDecompositionTest::dominantEVNewton(minpol, detB); + return std::abs(correctDominantEV - dominantEVNewton) < 0.001; } static bool testCharacteristicPolynomial4D() { - FieldMatrix<field_type,4,4> B = { { 0.0096632, 0.997822, 0.0257685, 0.00644213 }, - { 0.997822, -0.00322107, 0.0708635, 0.00644213 }, - { 0.0257685, 0.0708635, 0.00322107, 0.999239 }, - { 0.00644213, 0.00644213, 0.999239, -0.0096632 } }; - FieldVector<field_type,3> minpol = {0, -1.99999, -0.00496083}; - - auto coefficients = PolarDecompositionTest::characteristicCoefficients(B); - coefficients -= minpol; - return coefficients.two_norm() < 0.001; + FieldMatrix<field_type,4,4> B = { { 0.0096632, 0.997822, 0.0257685, 0.00644213 }, + { 0.997822, -0.00322107, 0.0708635, 0.00644213 }, + { 0.0257685, 0.0708635, 0.00322107, 0.999239 }, + { 0.00644213, 0.00644213, 0.999239, -0.0096632 } }; + FieldVector<field_type,3> minpol = {0, -1.99999, -0.00496083}; + + auto coefficients = PolarDecompositionTest::characteristicCoefficients(B); + coefficients -= minpol; + return coefficients.two_norm() < 0.001; } static bool testQuaternionFunction() { - FieldMatrix<field_type,4,4> BS = { { 1.04431, -0.997822, -0.0257685, -0.00644213 }, - { -0.997822, 1.05719, -0.0708635, -0.00644213 }, - { -0.0257685, -0.0708635, 1.05075, -0.999239 }, - { -0.00644213, -0.00644213, -0.999239, 1.06363 } }; - - FieldVector<field_type, 4 > v = { 0.508566, 0.516353, 0.499062, 0.475055 }; - auto vtest = PolarDecompositionTest::obtainQuaternion(BS); - vtest -= v; - return vtest.two_norm() < 0.001; + FieldMatrix<field_type,4,4> BS = { { 1.04431, -0.997822, -0.0257685, -0.00644213 }, + { -0.997822, 1.05719, -0.0708635, -0.00644213 }, + { -0.0257685, -0.0708635, 1.05075, -0.999239 }, + { -0.00644213, -0.00644213, -0.999239, 1.06363 } }; + + FieldVector<field_type, 4 > v = { 0.508566, 0.516353, 0.499062, 0.475055 }; + auto vtest = PolarDecompositionTest::obtainQuaternion(BS); + vtest -= v; + return vtest.two_norm() < 0.001; } int main (int argc, char *argv[]) try { - //test the correctness of the algorithm - auto testMatrix = randomMatrix3d(); - auto Q = PolarDecompositionTest()(testMatrix,1e-12,1e-12); - if (isNotOrthogonal(Q)) { - std::cerr << "The polar decomposition did not return an orthogonal matrix when decomposing: " << std::endl << testMatrix << std::endl; - return 1; - } + //test the correctness of the algorithm + auto testMatrix = randomMatrix3d(); + auto Q = PolarDecompositionTest()(testMatrix,1e-12,1e-12); + if (isNotOrthogonal(Q)) { + std::cerr << "The polar decomposition did not return an orthogonal matrix when decomposing: " << std::endl << testMatrix << std::endl; + return 1; + } + + if (testBilinearMatrix()) { + std::cerr << "The test calculation of the bilinearMatrix is wrong!" << std::endl; + return 1; + } + if (!test4dDeterminant()) { + std::cerr << "The test calculation of the 4d determinant is wrong!" << std::endl; + return 1; + } + if (!testdominantEVNewton()) { + std::cerr << "The test calculation of the dominant eigenvalue is wrong!" << std::endl; + return 1; + } + if (!testCharacteristicPolynomial4D()) { + std::cerr << "The test calculation of the characteristic polynomial is wrong!" << std::endl; + return 1; + } + if (!testQuaternionFunction()) { + std::cerr << "The test calculation of the quaternion function is wrong!" << std::endl; + return 1; + } + + timeTest(.1); + timeTest(2); + timeTest(30); + + return 0; - if (testBilinearMatrix()) { - std::cerr << "The test calculation of the bilinearMatrix is wrong!" << std::endl; - return 1; - } - if (!test4dDeterminant()) { - std::cerr << "The test calculation of the 4d determinant is wrong!" << std::endl; - return 1; - } - if (!testdominantEVNewton()) { - std::cerr << "The test calculation of the dominant eigenvalue is wrong!" << std::endl; - return 1; - } - if (!testCharacteristicPolynomial4D()) { - std::cerr << "The test calculation of the characteristic polynomial is wrong!" << std::endl; - return 1; - } - if (!testQuaternionFunction()) { - std::cerr << "The test calculation of the quaternion function is wrong!" << std::endl; - return 1; - } - - timeTest(.1); - timeTest(2); - timeTest(30); - - return 0; - -} catch (Exception& e) { - std::cout << e.what() << std::endl; } - +catch (Exception& e) { + std::cout << e.what() << std::endl; +} diff --git a/test/rigidbodymotiontest.cc b/test/rigidbodymotiontest.cc index d82cb23f..af824ad2 100644 --- a/test/rigidbodymotiontest.cc +++ b/test/rigidbodymotiontest.cc @@ -22,78 +22,78 @@ using namespace Dune; template <int domainDim,class LocalFiniteElement> Tensor3<double,3,3,domainDim> evaluateDerivativeFD(const LocalGeodesicFEFunction<domainDim,double,LocalFiniteElement,TargetSpace>& f, - const Dune::FieldVector<double,domainDim>& local) + const Dune::FieldVector<double,domainDim>& local) { const double stepSize = std::sqrt(std::numeric_limits<double>::epsilon()); - Tensor3<double,3,3,domainDim> result(0); - - for (int i=0; i<domainDim; i++) { - - Dune::FieldVector<double, domainDim> forward = local; - Dune::FieldVector<double, domainDim> backward = local; - - forward[i] += stepSize; - backward[i] -= stepSize; - - TargetSpace forwardValue = f.evaluate(forward); - TargetSpace backwardValue = f.evaluate(backward); - - FieldMatrix<double,3,3> forwardMatrix, backwardMatrix; - forwardValue.q.matrix(forwardMatrix); - backwardValue.q.matrix(backwardMatrix); - - FieldMatrix<double,3,3> fdDer = (forwardMatrix - backwardMatrix) / (2*stepSize); - - for (int j=0; j<3; j++) - for (int k=0; k<3; k++) - result[j][k][i] = fdDer[j][k]; - - } + Tensor3<double,3,3,domainDim> result(0); + + for (int i=0; i<domainDim; i++) { + + Dune::FieldVector<double, domainDim> forward = local; + Dune::FieldVector<double, domainDim> backward = local; + + forward[i] += stepSize; + backward[i] -= stepSize; + + TargetSpace forwardValue = f.evaluate(forward); + TargetSpace backwardValue = f.evaluate(backward); + + FieldMatrix<double,3,3> forwardMatrix, backwardMatrix; + forwardValue.q.matrix(forwardMatrix); + backwardValue.q.matrix(backwardMatrix); - return result; + FieldMatrix<double,3,3> fdDer = (forwardMatrix - backwardMatrix) / (2*stepSize); + + for (int j=0; j<3; j++) + for (int k=0; k<3; k++) + result[j][k][i] = fdDer[j][k]; + + } + + return result; } template <int domainDim> void testDerivativeOfRotationMatrix(const std::vector<TargetSpace>& corners) { - // Make local fe function to be tested - LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; - typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; - - LocalGeodesicFEFunction<domainDim,double,LocalFiniteElement, TargetSpace> f(feCache.get(GeometryTypes::simplex(domainDim)), corners); - - // A quadrature rule as a set of test points - int quadOrder = 3; - - const auto& quad = Dune::QuadratureRules<double, domainDim>::rule(GeometryTypes::simplex(domainDim), quadOrder); - - for (size_t pt=0; pt<quad.size(); pt++) { - - const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); - - // evaluate actual derivative - Dune::FieldMatrix<double, TargetSpace::EmbeddedTangentVector::dimension, domainDim> derivative = f.evaluateDerivative(quadPos); - - Tensor3<double,3,3,domainDim> DR = f.evaluate(quadPos).quaternionTangentToMatrixTangent(derivative); - - // evaluate fd approximation of derivative - Tensor3<double,3,3,domainDim> DR_fd = evaluateDerivativeFD(f,quadPos); - - double maxDiff = 0; - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) - for (int k=0; k<domainDim; k++) - maxDiff = std::max(maxDiff, std::abs(DR[i][j][k] - DR_fd[i][j][k])); - - if ( maxDiff > 100*eps ) { - std::cout << className(corners[0]) << ": Analytical gradient does not match fd approximation." << std::endl; - std::cout << "Analytical:\n " << DR << std::endl; - std::cout << "FD :\n " << DR_fd << std::endl; - assert(false); - } + // Make local fe function to be tested + LagrangeLocalFiniteElementCache<double,double,domainDim,1> feCache; + typedef typename LagrangeLocalFiniteElementCache<double,double,domainDim,1>::FiniteElementType LocalFiniteElement; + + LocalGeodesicFEFunction<domainDim,double,LocalFiniteElement, TargetSpace> f(feCache.get(GeometryTypes::simplex(domainDim)), corners); + // A quadrature rule as a set of test points + int quadOrder = 3; + + const auto& quad = Dune::QuadratureRules<double, domainDim>::rule(GeometryTypes::simplex(domainDim), quadOrder); + + for (size_t pt=0; pt<quad.size(); pt++) { + + const Dune::FieldVector<double,domainDim>& quadPos = quad[pt].position(); + + // evaluate actual derivative + Dune::FieldMatrix<double, TargetSpace::EmbeddedTangentVector::dimension, domainDim> derivative = f.evaluateDerivative(quadPos); + + Tensor3<double,3,3,domainDim> DR = f.evaluate(quadPos).quaternionTangentToMatrixTangent(derivative); + + // evaluate fd approximation of derivative + Tensor3<double,3,3,domainDim> DR_fd = evaluateDerivativeFD(f,quadPos); + + double maxDiff = 0; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + for (int k=0; k<domainDim; k++) + maxDiff = std::max(maxDiff, std::abs(DR[i][j][k] - DR_fd[i][j][k])); + + if ( maxDiff > 100*eps ) { + std::cout << className(corners[0]) << ": Analytical gradient does not match fd approximation." << std::endl; + std::cout << "Analytical:\n " << DR << std::endl; + std::cout << "FD :\n " << DR_fd << std::endl; + assert(false); } + + } } int main(int argc, char** argv) @@ -121,9 +121,9 @@ int main(int argc, char** argv) for (int i=0; i<numIndices; i++, ++index) { - for (int j=0; j<domainDim+1; j++) - corners[j].q = testPoints[index[j]]; + for (int j=0; j<domainDim+1; j++) + corners[j].q = testPoints[index[j]]; - testDerivativeOfRotationMatrix<domainDim>(corners); + testDerivativeOfRotationMatrix<domainDim>(corners); } } diff --git a/test/rotationtest.cc b/test/rotationtest.cc index aa67c616..f95ac20a 100644 --- a/test/rotationtest.cc +++ b/test/rotationtest.cc @@ -12,264 +12,265 @@ using namespace Dune; void testDDExp() { - std::array<FieldVector<double,3>, 125> v; - int ct = 0; - double eps = 1e-4; - - for (int i=-2; i<3; i++) - for (int j=-2; j<3; j++) - for (int k=-2; k<3; k++) { - v[ct][0] = i; - v[ct][1] = j; - v[ct][2] = k; - ct++; - } - - for (size_t i=0; i<v.size(); i++) { - - // Compute FD approximation of second derivative of exp - std::array<Dune::FieldMatrix<double,3,3>, 4> fdDDExp; + std::array<FieldVector<double,3>, 125> v; + int ct = 0; + double eps = 1e-4; + + for (int i=-2; i<3; i++) + for (int j=-2; j<3; j++) + for (int k=-2; k<3; k++) { + v[ct][0] = i; + v[ct][1] = j; + v[ct][2] = k; + ct++; + } - for (int j=0; j<3; j++) { + for (size_t i=0; i<v.size(); i++) { - for (int k=0; k<3; k++) { + // Compute FD approximation of second derivative of exp + std::array<Dune::FieldMatrix<double,3,3>, 4> fdDDExp; - if (j==k) { + for (int j=0; j<3; j++) { - SkewMatrix<double,3> forward(v[i]); - forward.axial()[j] += eps; - Rotation<double,3> forwardQ = Rotation<double,3>::exp(forward); + for (int k=0; k<3; k++) { - SkewMatrix<double,3> center(v[i]); - Rotation<double,3> centerQ = Rotation<double,3>::exp(center); + if (j==k) { - SkewMatrix<double,3> backward(v[i]); - backward.axial()[j] -= eps; - Rotation<double,3> backwardQ = Rotation<double,3>::exp(backward); + SkewMatrix<double,3> forward(v[i]); + forward.axial()[j] += eps; + Rotation<double,3> forwardQ = Rotation<double,3>::exp(forward); - for (int l=0; l<4; l++) - fdDDExp[l][j][j] = (forwardQ[l] - 2*centerQ[l] + backwardQ[l]) / (eps*eps); + SkewMatrix<double,3> center(v[i]); + Rotation<double,3> centerQ = Rotation<double,3>::exp(center); + SkewMatrix<double,3> backward(v[i]); + backward.axial()[j] -= eps; + Rotation<double,3> backwardQ = Rotation<double,3>::exp(backward); - } else { + for (int l=0; l<4; l++) + fdDDExp[l][j][j] = (forwardQ[l] - 2*centerQ[l] + backwardQ[l]) / (eps*eps); - SkewMatrix<double,3> ffV(v[i]); ffV.axial()[j] += eps; ffV.axial()[k] += eps; - SkewMatrix<double,3> fbV(v[i]); fbV.axial()[j] += eps; fbV.axial()[k] -= eps; - SkewMatrix<double,3> bfV(v[i]); bfV.axial()[j] -= eps; bfV.axial()[k] += eps; - SkewMatrix<double,3> bbV(v[i]); bbV.axial()[j] -= eps; bbV.axial()[k] -= eps; - Rotation<double,3> forwardForwardQ = Rotation<double,3>::exp(ffV); - Rotation<double,3> forwardBackwardQ = Rotation<double,3>::exp(fbV); - Rotation<double,3> backwardForwardQ = Rotation<double,3>::exp(bfV); - Rotation<double,3> backwardBackwardQ = Rotation<double,3>::exp(bbV); + } else { - for (int l=0; l<4; l++) - fdDDExp[l][j][k] = (forwardForwardQ[l] + backwardBackwardQ[l] - - forwardBackwardQ[l] - backwardForwardQ[l]) / (4*eps*eps); + SkewMatrix<double,3> ffV(v[i]); ffV.axial()[j] += eps; ffV.axial()[k] += eps; + SkewMatrix<double,3> fbV(v[i]); fbV.axial()[j] += eps; fbV.axial()[k] -= eps; + SkewMatrix<double,3> bfV(v[i]); bfV.axial()[j] -= eps; bfV.axial()[k] += eps; + SkewMatrix<double,3> bbV(v[i]); bbV.axial()[j] -= eps; bbV.axial()[k] -= eps; - } + Rotation<double,3> forwardForwardQ = Rotation<double,3>::exp(ffV); + Rotation<double,3> forwardBackwardQ = Rotation<double,3>::exp(fbV); + Rotation<double,3> backwardForwardQ = Rotation<double,3>::exp(bfV); + Rotation<double,3> backwardBackwardQ = Rotation<double,3>::exp(bbV); - } + for (int l=0; l<4; l++) + fdDDExp[l][j][k] = (forwardForwardQ[l] + backwardBackwardQ[l] + - forwardBackwardQ[l] - backwardForwardQ[l]) / (4*eps*eps); } - // Compute analytical second derivative of exp - std::array<Dune::FieldMatrix<double,3,3>, 4> ddExp; - Rotation<double,3>::DDexp(v[i], ddExp); - - for (int m=0; m<4; m++) - for (int j=0; j<3; j++) - for (int k=0; k<3; k++) - if ( std::abs(fdDDExp[m][j][k] - ddExp[m][j][k]) > eps) { - std::cout << "Error at v = " << v[i] - << "[" << m << ", " << j << ", " << k << "] " - << " fd: " << fdDDExp[m][j][k] - << " analytical: " << ddExp[m][j][k] << std::endl; - } + } + } + + // Compute analytical second derivative of exp + std::array<Dune::FieldMatrix<double,3,3>, 4> ddExp; + Rotation<double,3>::DDexp(v[i], ddExp); + + for (int m=0; m<4; m++) + for (int j=0; j<3; j++) + for (int k=0; k<3; k++) + if ( std::abs(fdDDExp[m][j][k] - ddExp[m][j][k]) > eps) { + std::cout << "Error at v = " << v[i] + << "[" << m << ", " << j << ", " << k << "] " + << " fd: " << fdDDExp[m][j][k] + << " analytical: " << ddExp[m][j][k] << std::endl; + } + } } void testRotation(Rotation<double,3> q) { - // Make sure it really is a unit quaternion - q.normalize(); - - assert(std::abs(1-q.two_norm()) < 1e-12); + // Make sure it really is a unit quaternion + q.normalize(); - // Turn it into a matrix - FieldMatrix<double,3,3> matrix; - q.matrix(matrix); + assert(std::abs(1-q.two_norm()) < 1e-12); - // make sure it is an orthogonal matrix - if (std::abs(1-matrix.determinant()) > 1e-12 ) - DUNE_THROW(Exception, "Expected determinant 1, but the computed value is " << matrix.determinant()); + // Turn it into a matrix + FieldMatrix<double,3,3> matrix; + q.matrix(matrix); - assert( std::abs( matrix[0]*matrix[1] ) < 1e-12 ); - assert( std::abs( matrix[0]*matrix[2] ) < 1e-12 ); - assert( std::abs( matrix[1]*matrix[2] ) < 1e-12 ); + // make sure it is an orthogonal matrix + if (std::abs(1-matrix.determinant()) > 1e-12 ) + DUNE_THROW(Exception, "Expected determinant 1, but the computed value is " << matrix.determinant()); - // Turn the matrix back into a quaternion, and check whether it is the same one - // Since the quaternions form a double covering of SO(3), we may either get q back - // or -q. We have to check both. - Rotation<double,3> newQ; - newQ.set(matrix); + assert( std::abs( matrix[0]*matrix[1] ) < 1e-12 ); + assert( std::abs( matrix[0]*matrix[2] ) < 1e-12 ); + assert( std::abs( matrix[1]*matrix[2] ) < 1e-12 ); - Rotation<double,3> diff = newQ; - diff -= q; + // Turn the matrix back into a quaternion, and check whether it is the same one + // Since the quaternions form a double covering of SO(3), we may either get q back + // or -q. We have to check both. + Rotation<double,3> newQ; + newQ.set(matrix); - Rotation<double,3> sum = newQ; - sum += q; + Rotation<double,3> diff = newQ; + diff -= q; - if (diff.infinity_norm() > 1e-12 && sum.infinity_norm() > 1e-12) - DUNE_THROW(Exception, "Backtransformation failed for " << q << ". "); + Rotation<double,3> sum = newQ; + sum += q; - // ////////////////////////////////////////////////////// - // Check the director vectors - // ////////////////////////////////////////////////////// + if (diff.infinity_norm() > 1e-12 && sum.infinity_norm() > 1e-12) + DUNE_THROW(Exception, "Backtransformation failed for " << q << ". "); - for (int i=0; i<3; i++) - for (int j=0; j<3; j++) - assert( std::abs(matrix[i][j] - q.director(j)[i]) < 1e-12 ); + // ////////////////////////////////////////////////////// + // Check the director vectors + // ////////////////////////////////////////////////////// - // ////////////////////////////////////////////////////// - // Check multiplication with another unit quaternion - // ////////////////////////////////////////////////////// + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + assert( std::abs(matrix[i][j] - q.director(j)[i]) < 1e-12 ); - for (int i=-2; i<2; i++) - for (int j=-2; j<2; j++) - for (int k=-2; k<2; k++) - for (int l=-2; l<2; l++) - if (i!=0 || j!=0 || k!=0 || l!=0) { + // ////////////////////////////////////////////////////// + // Check multiplication with another unit quaternion + // ////////////////////////////////////////////////////// - Rotation<double,3> q2(Quaternion<double>(i,j,k,l)); - q2.normalize(); + for (int i=-2; i<2; i++) + for (int j=-2; j<2; j++) + for (int k=-2; k<2; k++) + for (int l=-2; l<2; l++) + if (i!=0 || j!=0 || k!=0 || l!=0) { - // set up corresponding rotation matrix - FieldMatrix<double,3,3> q2Matrix; - q2.matrix(q2Matrix); + Rotation<double,3> q2(Quaternion<double>(i,j,k,l)); + q2.normalize(); - // q2 = q2 * q Quaternion multiplication - q2 = q2.mult(q); + // set up corresponding rotation matrix + FieldMatrix<double,3,3> q2Matrix; + q2.matrix(q2Matrix); - // q2 = q2 * q Matrix multiplication - q2Matrix.rightmultiply(matrix); + // q2 = q2 * q Quaternion multiplication + q2 = q2.mult(q); - FieldMatrix<double,3,3> productMatrix; - q2.matrix(productMatrix); + // q2 = q2 * q Matrix multiplication + q2Matrix.rightmultiply(matrix); - // Make sure we got identical results - productMatrix -= q2Matrix; - assert(productMatrix.infinity_norm() < 1e-10); + FieldMatrix<double,3,3> productMatrix; + q2.matrix(productMatrix); - } + // Make sure we got identical results + productMatrix -= q2Matrix; + assert(productMatrix.infinity_norm() < 1e-10); - // //////////////////////////////////////////////////////////////// - // Check the operators 'B' that create an orthonormal basis of H - // //////////////////////////////////////////////////////////////// + } - Quaternion<double> Bq[4]; - Bq[0] = q; - Bq[1] = q.B(0); - Bq[2] = q.B(1); - Bq[3] = q.B(2); + // //////////////////////////////////////////////////////////////// + // Check the operators 'B' that create an orthonormal basis of H + // //////////////////////////////////////////////////////////////// - for (int i=0; i<4; i++) { + Quaternion<double> Bq[4]; + Bq[0] = q; + Bq[1] = q.B(0); + Bq[2] = q.B(1); + Bq[3] = q.B(2); - for (int j=0; j<4; j++) { + for (int i=0; i<4; i++) { - double prod = Bq[i]*Bq[j]; - assert( std::abs( prod - (i==j) ) < 1e-6 ); + for (int j=0; j<4; j++) { - } + double prod = Bq[i]*Bq[j]; + assert( std::abs( prod - (i==j) ) < 1e-6 ); } - ////////////////////////////////////////////////////////////////////// - // Check whether the derivativeOfMatrixToQuaternion methods works - ////////////////////////////////////////////////////////////////////// + } - Tensor3<double,4,3,3> derivative = Rotation<double,3>::derivativeOfMatrixToQuaternion(matrix); + ////////////////////////////////////////////////////////////////////// + // Check whether the derivativeOfMatrixToQuaternion methods works + ////////////////////////////////////////////////////////////////////// - const double eps = 1e-8; - Tensor3<double,4,3,3> derivativeFD; + Tensor3<double,4,3,3> derivative = Rotation<double,3>::derivativeOfMatrixToQuaternion(matrix); - for (size_t i=0; i<3; i++) - { - for (size_t j=0; j<3; j++) - { - auto forwardMatrix = matrix; - forwardMatrix[i][j] += eps; - auto backwardMatrix = matrix; - backwardMatrix[i][j] -= eps; + const double eps = 1e-8; + Tensor3<double,4,3,3> derivativeFD; - Rotation<double,3> forwardRotation, backwardRotation; - forwardRotation.set(forwardMatrix); - backwardRotation.set(backwardMatrix); + for (size_t i=0; i<3; i++) + { + for (size_t j=0; j<3; j++) + { + auto forwardMatrix = matrix; + forwardMatrix[i][j] += eps; + auto backwardMatrix = matrix; + backwardMatrix[i][j] -= eps; - for (size_t k=0; k<4; k++) - derivativeFD[k][i][j] = (forwardRotation.globalCoordinates()[k] - backwardRotation.globalCoordinates()[k]) / (2*eps); + Rotation<double,3> forwardRotation, backwardRotation; + forwardRotation.set(forwardMatrix); + backwardRotation.set(backwardMatrix); - } - } + for (size_t k=0; k<4; k++) + derivativeFD[k][i][j] = (forwardRotation.globalCoordinates()[k] - backwardRotation.globalCoordinates()[k]) / (2*eps); - if ((derivative - derivativeFD).infinity_norm() > 1e-6) - { - std::cout << "At matrix:\n" << matrix << std::endl; - - std::cout << "Derivative of matrix to quaternion map does not match its FD approximation" << std::endl; - std::cout << "Analytical derivative:" << std::endl; - std::cout << derivative << std::endl; - std::cout << "Finite difference approximation" << std::endl; - std::cout << derivativeFD << std::endl; - abort(); } + } + + if ((derivative - derivativeFD).infinity_norm() > 1e-6) + { + std::cout << "At matrix:\n" << matrix << std::endl; + + std::cout << "Derivative of matrix to quaternion map does not match its FD approximation" << std::endl; + std::cout << "Analytical derivative:" << std::endl; + std::cout << derivative << std::endl; + std::cout << "Finite difference approximation" << std::endl; + std::cout << derivativeFD << std::endl; + abort(); + } } //! test interpolation between two rotations bool testInterpolation(const Rotation<double, 3>& a, const Rotation<double, 3>& b) { - // Compute difference on T_a SO(3) - Rotation<double, 3> newB = Rotation<double, 3>::interpolate(a, b, 1.0); + // Compute difference on T_a SO(3) + Rotation<double, 3> newB = Rotation<double, 3>::interpolate(a, b, 1.0); - // Compare matrix representation - FieldMatrix<double, 3, 3> matB; - b.matrix(matB); + // Compare matrix representation + FieldMatrix<double, 3, 3> matB; + b.matrix(matB); - FieldMatrix<double, 3, 3> matNewB; - newB.matrix(matNewB); + FieldMatrix<double, 3, 3> matNewB; + newB.matrix(matNewB); - matNewB -= matB; - if (matNewB.infinity_norm() > 1e-14) - std::cout << " Interpolation failed with difference " << matNewB.infinity_norm() << std::endl; + matNewB -= matB; + if (matNewB.infinity_norm() > 1e-14) + std::cout << " Interpolation failed with difference " << matNewB.infinity_norm() << std::endl; - return (matNewB.infinity_norm() < 1e-14); + return (matNewB.infinity_norm() < 1e-14); } int main (int argc, char *argv[]) try { - std::vector<Rotation<double,3> > testPoints; - ValueFactory<Rotation<double,3> >::get(testPoints); + std::vector<Rotation<double,3> > testPoints; + ValueFactory<Rotation<double,3> >::get(testPoints); - int nTestPoints = testPoints.size(); + int nTestPoints = testPoints.size(); - // Test each element in the list - for (int i=0; i<nTestPoints; i++) - testRotation(testPoints[i]); + // Test each element in the list + for (int i=0; i<nTestPoints; i++) + testRotation(testPoints[i]); - bool passed(true); - // Test interpolating between pairs of rotations - for (int i=0; i<nTestPoints-1; i++) - passed = passed and testInterpolation(testPoints[i], testPoints[i+1]); + bool passed(true); + // Test interpolating between pairs of rotations + for (int i=0; i<nTestPoints-1; i++) + passed = passed and testInterpolation(testPoints[i], testPoints[i+1]); - // ////////////////////////////////////////////// - // Test second derivative of exp - // ////////////////////////////////////////////// - testDDExp(); + // ////////////////////////////////////////////// + // Test second derivative of exp + // ////////////////////////////////////////////// + testDDExp(); - return not passed; + return not passed; - } catch (Exception& e) { +} +catch (Exception& e) { - std::cout << e.what() << std::endl; + std::cout << e.what() << std::endl; - } +} diff --git a/test/surfacecosseratstressassemblertest.cc b/test/surfacecosseratstressassemblertest.cc index e3752141..b50f2fb9 100644 --- a/test/surfacecosseratstressassemblertest.cc +++ b/test/surfacecosseratstressassemblertest.cc @@ -46,14 +46,14 @@ using ValueType = adouble; // Surface Shell Boundary std::function<bool(FieldVector<double,dim>)> isSurfaceShellBoundary = [](FieldVector<double,dim> coordinate) { - return coordinate[2] > 199.99 and coordinate[0] > 49.99 and coordinate[0] < 150.01; -}; + return coordinate[2] > 199.99 and coordinate[0] > 49.99 and coordinate[0] < 150.01; + }; static bool sameEntries(FieldVector<double,3> a,FieldVector<double,3> b) { b[1] *= -1; b -= a; //If a.two_norm > 0, so if there is a displacement at this points: Scale the difference with the length of the vectors - return a.two_norm() > 0 ? b.two_norm()/a.two_norm() < 0.05 : b.two_norm() < 0.05; + return a.two_norm() > 0 ? b.two_norm()/a.two_norm() < 0.05 : b.two_norm() < 0.05; } static bool sameEntries(FieldVector<double,4> a,FieldVector<double,4> b) { @@ -66,7 +66,7 @@ static bool sameEntries(FieldVector<double,4> a,FieldVector<double,4> b) { } template <int d> -static bool symmetryTest(std::unordered_map<std::string, FieldVector<double,d>> map, double axis) { +static bool symmetryTest(std::unordered_map<std::string, FieldVector<double,d> > map, double axis) { bool isSame = true; std::string entry; for (auto it = map.begin(); it != map.end(); it++) { @@ -103,12 +103,12 @@ int main (int argc, char *argv[]) grid->setRefinementType(GridType::RefinementType::COPY); int numLevels = 4; - + while (numLevels > 0) { - for (auto&& e : elements(grid->leafGridView())){ + for (auto&& e : elements(grid->leafGridView())) { bool isSurfaceShell = false; for (int i = 0; i < e.geometry().corners(); i++) { - isSurfaceShell = isSurfaceShell || isSurfaceShellBoundary(e.geometry().corner(i)); + isSurfaceShell = isSurfaceShell || isSurfaceShellBoundary(e.geometry().corner(i)); } grid->mark(isSurfaceShell ? 1 : 0,e); } @@ -134,9 +134,9 @@ int main (int argc, char *argv[]) BoundaryPatch<GridView> surfaceShellBoundary(gridView, surfaceShellVertices); std::function<Dune::FieldVector<double,2>(Dune::FieldVector<double,dim>)> fLame = [](Dune::FieldVector<double,dim> x){ - Dune::FieldVector<double,2> lameConstants {1.24E+07,2.52E+07}; //stiffness = 33 - return lameConstants; - }; + Dune::FieldVector<double,2> lameConstants {1.24E+07,2.52E+07}; //stiffness = 33 + return lameConstants; + }; ///////////////////////////////////////////////////////////// // FUNCTION SPACE @@ -146,14 +146,14 @@ int main (int argc, char *argv[]) auto basisOrderD = makeBasis( gridView, power<dim>( - lagrange<displacementOrder>() - )); + lagrange<displacementOrder>() + )); auto basisOrderR = makeBasis( gridView, power<dim>( - lagrange<rotationOrder>() - )); + lagrange<rotationOrder>() + )); ///////////////////////////////////////////////////////////// // READ IN TEST DATA @@ -182,9 +182,13 @@ int main (int argc, char *argv[]) xInitial.resize(basisOrderD.size()); DisplacementVector displacement; displacement.resize(basisOrderD.size()); - - Functions::interpolate(basisOrderD, x, [](FieldVector<double,dim> x){ return x; }); - Functions::interpolate(basisOrderD, xInitial, [](FieldVector<double,dim> x){ return x; }); + + Functions::interpolate(basisOrderD, x, [](FieldVector<double,dim> x){ + return x; + }); + Functions::interpolate(basisOrderD, xInitial, [](FieldVector<double,dim> x){ + return x; + }); for (std::size_t i = 0; i < basisOrderD.size(); i++) { std::stringstream stream; @@ -195,12 +199,14 @@ int main (int argc, char *argv[]) xInitial[i] += initialDeformationMap.at(stream.str()); } - using RotationVector = std::vector<Rotation<double,dim>>; + using RotationVector = std::vector<Rotation<double,dim> >; RotationVector rot; rot.resize(basisOrderR.size()); DisplacementVector xOrderR; xOrderR.resize(basisOrderR.size()); - Functions::interpolate(basisOrderR, xOrderR, [](FieldVector<double,dim> x){ return x; }); + Functions::interpolate(basisOrderR, xOrderR, [](FieldVector<double,dim> x){ + return x; + }); for (std::size_t i = 0; i < basisOrderR.size(); i++) { std::stringstream stream; stream << xOrderR[i]; @@ -227,20 +233,20 @@ int main (int argc, char *argv[]) ///////////////////////////////////////////////////////////// auto quadOrder = 4; - auto stressAssembler = GFE::SurfaceCosseratStressAssembler<decltype(basisOrderD),decltype(basisOrderR), FieldVector<double,dim>, Rotation<double,dim>> - (basisOrderD, basisOrderR); + auto stressAssembler = GFE::SurfaceCosseratStressAssembler<decltype(basisOrderD),decltype(basisOrderR), FieldVector<double,dim>, Rotation<double,dim> > + (basisOrderD, basisOrderR); - std::shared_ptr<Elasticity::LocalDensity<dim,ValueType>> elasticDensity; + std::shared_ptr<Elasticity::LocalDensity<dim,ValueType> > elasticDensity; ParameterTree materialParameters; materialParameters["mu"] = "2.7191e+4"; materialParameters["lambda"] = "4.4364e+4"; - elasticDensity = std::make_shared<Elasticity::StVenantKirchhoffDensity<dim,ValueType>>(materialParameters); + elasticDensity = std::make_shared<Elasticity::StVenantKirchhoffDensity<dim,ValueType> >(materialParameters); - std::vector<FieldMatrix<double,dim,dim>> stressSubstrate1stPiolaKirchhoffTensor; - std::vector<FieldMatrix<double,dim,dim>> stressSubstrateCauchyTensor; - stressAssembler.assembleSubstrateStress<Elasticity::LocalDensity<dim,ValueType>>(x, elasticDensity.get(), quadOrder, stressSubstrate1stPiolaKirchhoffTensor, stressSubstrateCauchyTensor); - std::vector<FieldMatrix<double,dim,dim>> stressShellBiotTensor; - stressAssembler.assembleShellStress(rot, x, xInitial, fLame,/*mu_c*/0, surfaceShellBoundary, quadOrder, stressShellBiotTensor); + std::vector<FieldMatrix<double,dim,dim> > stressSubstrate1stPiolaKirchhoffTensor; + std::vector<FieldMatrix<double,dim,dim> > stressSubstrateCauchyTensor; + stressAssembler.assembleSubstrateStress<Elasticity::LocalDensity<dim,ValueType> >(x, elasticDensity.get(), quadOrder, stressSubstrate1stPiolaKirchhoffTensor, stressSubstrateCauchyTensor); + std::vector<FieldMatrix<double,dim,dim> > stressShellBiotTensor; + stressAssembler.assembleShellStress(rot, x, xInitial, fLame,/*mu_c*/ 0, surfaceShellBoundary, quadOrder, stressShellBiotTensor); //Now modify ONE value in the rotation function int i = 39787; @@ -249,13 +255,13 @@ int main (int argc, char *argv[]) rot[i].matrix(rotationMatrix); Dune::MatrixVector::transpose(transposed, rotationMatrix); rot[i].set(transposed); - std::vector<FieldMatrix<double,dim,dim>> stressShellBiotTensorNotSymmetric; - stressAssembler.assembleShellStress(rot, x, xInitial, fLame,/*mu_c*/0, surfaceShellBoundary, quadOrder, stressShellBiotTensorNotSymmetric); + std::vector<FieldMatrix<double,dim,dim> > stressShellBiotTensorNotSymmetric; + stressAssembler.assembleShellStress(rot, x, xInitial, fLame,/*mu_c*/ 0, surfaceShellBoundary, quadOrder, stressShellBiotTensorNotSymmetric); // ... and ONE in the displacement function x[i] *= 2; - std::vector<FieldMatrix<double,dim,dim>> stressSubstrate1stPiolaKirchhoffTensorNotSymmetric; - std::vector<FieldMatrix<double,dim,dim>> stressSubstrateCauchyTensorNotSymmetric; - stressAssembler.assembleSubstrateStress<Elasticity::LocalDensity<dim,ValueType>>(x, elasticDensity.get(), quadOrder, stressSubstrate1stPiolaKirchhoffTensorNotSymmetric, stressSubstrateCauchyTensorNotSymmetric); + std::vector<FieldMatrix<double,dim,dim> > stressSubstrate1stPiolaKirchhoffTensorNotSymmetric; + std::vector<FieldMatrix<double,dim,dim> > stressSubstrateCauchyTensorNotSymmetric; + stressAssembler.assembleSubstrateStress<Elasticity::LocalDensity<dim,ValueType> >(x, elasticDensity.get(), quadOrder, stressSubstrate1stPiolaKirchhoffTensorNotSymmetric, stressSubstrateCauchyTensorNotSymmetric); // ... and make sure that the stress is not symmetric anymore! bool substrateModifiedIsNotSymmetric = false; bool shellModifiedIsNotSymmetric = false; @@ -283,7 +289,7 @@ int main (int argc, char *argv[]) auto shell = stressShellBiotTensor[thisIndex]; auto mirroredShell = stressShellBiotTensor[mirroredIndex]; - if ((shell.frobenius_norm()-mirroredShell.frobenius_norm())/mirroredShell.frobenius_norm() > 0.05){ + if ((shell.frobenius_norm()-mirroredShell.frobenius_norm())/mirroredShell.frobenius_norm() > 0.05) { std::cerr << "The elements with center " << element.geometry().center() << " and " << elementCenter << " have different Biot stress values (assembled using order 3): " << std::endl << shell << " and " << mirroredShell << ", but should have the same!" << std::endl; diff --git a/test/svdtest.cc b/test/svdtest.cc index 7f53915c..e795674a 100644 --- a/test/svdtest.cc +++ b/test/svdtest.cc @@ -8,50 +8,50 @@ using namespace Dune; int main() { - const int N=3; - const int M=3; - - FieldMatrix<double,N,M> testMatrix; - - int nTestValues = 5; - double maxDiff = 0; - - MultiIndex index(N*M, nTestValues); - int numIndices = index.cycle(); - - for (int i=0; i<numIndices; i++, ++index) { - - for (int j=0; j<N; j++) - for (int k=0; k<M; k++) { - testMatrix[j][k] = std::exp(double(2*index[j*M+k]-double(nTestValues))) - std::exp(double(nTestValues)); - //std::cout << std::exp(2*index[j*M+k]-double(nTestValues)) << std::endl; - } - //std::cout << "testMatrix:\n" << testMatrix << std::endl; - //exit(0); - FieldVector<double,N> w; - FieldMatrix<double,N,N> v; - - FieldMatrix<double,N,M> testMatrixBackup = testMatrix; - svdcmp(testMatrix,w,v); - - // Multiply the three matrices to see whether we get the original one back - FieldMatrix<double,N,M> product(0); - - for (int j=0; j<N; j++) - for (int k=0; k<M; k++) - for (int l=0; l<N; l++) - product[j][k] += testMatrix[j][l] * w[l] * v[k][l]; - - product -= testMatrixBackup; - //std::cout << product << std::endl; - if (maxDiff < product.infinity_norm()) { - maxDiff = product.infinity_norm(); - std::cout << "max diff: " << maxDiff << std::endl; - std::cout << "testMatrix:\n" << testMatrixBackup << std::endl; - std::cout << "difference:\n" << product << std::endl; - exit(0); - } - + const int N=3; + const int M=3; + + FieldMatrix<double,N,M> testMatrix; + + int nTestValues = 5; + double maxDiff = 0; + + MultiIndex index(N*M, nTestValues); + int numIndices = index.cycle(); + + for (int i=0; i<numIndices; i++, ++index) { + + for (int j=0; j<N; j++) + for (int k=0; k<M; k++) { + testMatrix[j][k] = std::exp(double(2*index[j*M+k]-double(nTestValues))) - std::exp(double(nTestValues)); + //std::cout << std::exp(2*index[j*M+k]-double(nTestValues)) << std::endl; + } + //std::cout << "testMatrix:\n" << testMatrix << std::endl; + //exit(0); + FieldVector<double,N> w; + FieldMatrix<double,N,N> v; + + FieldMatrix<double,N,M> testMatrixBackup = testMatrix; + svdcmp(testMatrix,w,v); + + // Multiply the three matrices to see whether we get the original one back + FieldMatrix<double,N,M> product(0); + + for (int j=0; j<N; j++) + for (int k=0; k<M; k++) + for (int l=0; l<N; l++) + product[j][k] += testMatrix[j][l] * w[l] * v[k][l]; + + product -= testMatrixBackup; + //std::cout << product << std::endl; + if (maxDiff < product.infinity_norm()) { + maxDiff = product.infinity_norm(); + std::cout << "max diff: " << maxDiff << std::endl; + std::cout << "testMatrix:\n" << testMatrixBackup << std::endl; + std::cout << "difference:\n" << product << std::endl; + exit(0); } - -} \ No newline at end of file + + } + +} diff --git a/test/targetspacetest.cc b/test/targetspacetest.cc index 81819cc3..5c2d855c 100644 --- a/test/targetspacetest.cc +++ b/test/targetspacetest.cc @@ -13,17 +13,17 @@ using namespace Dune; /** \file \brief Unit tests for classes that implement value manifolds for geodesic FE functions -*/ + */ /** \brief Computes the diameter of a set */ template <class TargetSpace> double diameter(const std::vector<TargetSpace>& v) { - double d = 0; - for (size_t i=0; i<v.size(); i++) - for (size_t j=0; j<v.size(); j++) - d = std::max(d, TargetSpace::distance(v[i],v[j])); - return d; + double d = 0; + for (size_t i=0; i<v.size(); i++) + for (size_t j=0; j<v.size(); j++) + d = std::max(d, TargetSpace::distance(v[i],v[j])); + return d; } const double eps = 1e-4; @@ -31,23 +31,23 @@ const double eps = 1e-4; template <class TargetSpace> void testExpLog(const TargetSpace& a, const TargetSpace& b) { - // Check whether exp and log are mutually inverse - typename TargetSpace::EmbeddedTangentVector logarithm = TargetSpace::log(a,b); - TargetSpace exponential = TargetSpace::exp(a, logarithm); - - if (TargetSpace::distance(b, exponential) > eps) - { - std::cout << className(a) << ": Exp and log are not mutually inverse." << std::endl; - std::cout << "exp(a,log(a,b)): " << exponential << std::endl; - std::cout << "b : " << b << std::endl; - assert(false); - } + // Check whether exp and log are mutually inverse + typename TargetSpace::EmbeddedTangentVector logarithm = TargetSpace::log(a,b); + TargetSpace exponential = TargetSpace::exp(a, logarithm); + + if (TargetSpace::distance(b, exponential) > eps) + { + std::cout << className(a) << ": Exp and log are not mutually inverse." << std::endl; + std::cout << "exp(a,log(a,b)): " << exponential << std::endl; + std::cout << "b : " << b << std::endl; + assert(false); + } } template <class TargetSpace> double distanceSquared(const TargetSpace& a, const TargetSpace& b) { - return Dune::power(TargetSpace::distance(a,b), 2); + return Dune::power(TargetSpace::distance(a,b), 2); } // Squared distance between two points slightly off the manifold. @@ -55,169 +55,169 @@ double distanceSquared(const TargetSpace& a, const TargetSpace& b) template <class TargetSpace, int dim> double distanceSquared(const FieldVector<double,dim>& a, const FieldVector<double,dim>& b) { - return Dune::power(TargetSpace::distance(TargetSpace(a),TargetSpace(b)), 2); + return Dune::power(TargetSpace::distance(TargetSpace(a),TargetSpace(b)), 2); } /** \brief Compute the Riemannian Hessian of the squared distance function in global coordinates The formula for the Riemannian Hessian has been taken from Absil, Mahony, Sepulchre: 'Optimization algorithms on matrix manifolds', page 107 -*/ + */ template <class TargetSpace, int worldDim> FieldMatrix<double,worldDim,worldDim> getSecondDerivativeOfSecondArgumentFD(const TargetSpace& a, const TargetSpace& b) { - - const size_t spaceDim = TargetSpace::dim; - - // finite-difference approximation - FieldMatrix<double,spaceDim,spaceDim> d2d2_fd(0); - - FieldMatrix<double,spaceDim,worldDim> B = b.orthonormalFrame(); - - for (size_t i=0; i<spaceDim; i++) { - for (size_t j=0; j<spaceDim; j++) { - - FieldVector<double,worldDim> epsXi = eps * B[i]; - FieldVector<double,worldDim> epsEta = eps * B[j]; - - FieldVector<double,worldDim> minusEpsXi = -1 * epsXi; - FieldVector<double,worldDim> minusEpsEta = -1 * epsEta; - - double forwardValue = distanceSquared(a,TargetSpace::exp(b,epsXi+epsEta)) - distanceSquared(a, TargetSpace::exp(b,epsXi)) - distanceSquared(a,TargetSpace::exp(b,epsEta)); - double centerValue = distanceSquared(a,b) - distanceSquared(a,b) - distanceSquared(a,b); - double backwardValue = distanceSquared(a,TargetSpace::exp(b,minusEpsXi + minusEpsEta)) - distanceSquared(a, TargetSpace::exp(b,minusEpsXi)) - distanceSquared(a,TargetSpace::exp(b,minusEpsEta)); - - d2d2_fd[i][j] = 0.5 * (forwardValue - 2*centerValue + backwardValue) / (eps*eps); - - } + + const size_t spaceDim = TargetSpace::dim; + + // finite-difference approximation + FieldMatrix<double,spaceDim,spaceDim> d2d2_fd(0); + + FieldMatrix<double,spaceDim,worldDim> B = b.orthonormalFrame(); + + for (size_t i=0; i<spaceDim; i++) { + for (size_t j=0; j<spaceDim; j++) { + + FieldVector<double,worldDim> epsXi = eps * B[i]; + FieldVector<double,worldDim> epsEta = eps * B[j]; + + FieldVector<double,worldDim> minusEpsXi = -1 * epsXi; + FieldVector<double,worldDim> minusEpsEta = -1 * epsEta; + + double forwardValue = distanceSquared(a,TargetSpace::exp(b,epsXi+epsEta)) - distanceSquared(a, TargetSpace::exp(b,epsXi)) - distanceSquared(a,TargetSpace::exp(b,epsEta)); + double centerValue = distanceSquared(a,b) - distanceSquared(a,b) - distanceSquared(a,b); + double backwardValue = distanceSquared(a,TargetSpace::exp(b,minusEpsXi + minusEpsEta)) - distanceSquared(a, TargetSpace::exp(b,minusEpsXi)) - distanceSquared(a,TargetSpace::exp(b,minusEpsEta)); + + d2d2_fd[i][j] = 0.5 * (forwardValue - 2*centerValue + backwardValue) / (eps*eps); + } - - //B.invert(); - FieldMatrix<double,worldDim,spaceDim> BT; - for (int i=0; i<worldDim; i++) - for (size_t j=0; j<spaceDim; j++) - BT[i][j] = B[j][i]; - - - FieldMatrix<double,spaceDim,worldDim> ret1; - FMatrixHelp::multMatrix(d2d2_fd,B,ret1); - - FieldMatrix<double,worldDim,worldDim> ret2; - FMatrixHelp::multMatrix(BT,ret1,ret2); - return ret2; + } + + //B.invert(); + FieldMatrix<double,worldDim,spaceDim> BT; + for (int i=0; i<worldDim; i++) + for (size_t j=0; j<spaceDim; j++) + BT[i][j] = B[j][i]; + + + FieldMatrix<double,spaceDim,worldDim> ret1; + FMatrixHelp::multMatrix(d2d2_fd,B,ret1); + + FieldMatrix<double,worldDim,worldDim> ret2; + FMatrixHelp::multMatrix(BT,ret1,ret2); + return ret2; } template <class TargetSpace> void testOrthonormalFrame(const TargetSpace& a) { - const size_t spaceDim = TargetSpace::dim; - const size_t embeddedDim = TargetSpace::embeddedDim; - - FieldMatrix<double,spaceDim,embeddedDim> B = a.orthonormalFrame(); - - for (size_t i=0; i<spaceDim; i++) - for (size_t j=0; j<spaceDim; j++) - assert( std::fabs(a.metric(B[i],B[j]) - (i==j)) < 1e-10 ); + const size_t spaceDim = TargetSpace::dim; + const size_t embeddedDim = TargetSpace::embeddedDim; + + FieldMatrix<double,spaceDim,embeddedDim> B = a.orthonormalFrame(); + + for (size_t i=0; i<spaceDim; i++) + for (size_t j=0; j<spaceDim; j++) + assert( std::fabs(a.metric(B[i],B[j]) - (i==j)) < 1e-10 ); } template <class TargetSpace> void testDerivativeOfDistanceSquared(const TargetSpace& a, const TargetSpace& b) { - static const size_t embeddedDim = TargetSpace::embeddedDim; - - /////////////////////////////////////////////////////////////////// - // Test derivative with respect to second argument - /////////////////////////////////////////////////////////////////// - typename TargetSpace::EmbeddedTangentVector d2 = TargetSpace::derivativeOfDistanceSquaredWRTSecondArgument(a, b); - - // finite-difference approximation - Dune::FieldMatrix<double,TargetSpace::TangentVector::dimension,embeddedDim> B = b.orthonormalFrame(); - - typename TargetSpace::TangentVector d2_fd; - for (size_t i=0; i<TargetSpace::TangentVector::dimension; i++) { - - typename TargetSpace::EmbeddedTangentVector fwVariation = eps * B[i]; - typename TargetSpace::EmbeddedTangentVector bwVariation = -eps * B[i]; - TargetSpace bPlus = TargetSpace::exp(b,fwVariation); - TargetSpace bMinus = TargetSpace::exp(b,bwVariation); - - d2_fd[i] = (distanceSquared(a,bPlus) - distanceSquared(a,bMinus)) / (2*eps); - } + static const size_t embeddedDim = TargetSpace::embeddedDim; - // transform into embedded coordinates - typename TargetSpace::EmbeddedTangentVector d2_fd_embedded; - B.mtv(d2_fd,d2_fd_embedded); + /////////////////////////////////////////////////////////////////// + // Test derivative with respect to second argument + /////////////////////////////////////////////////////////////////// + typename TargetSpace::EmbeddedTangentVector d2 = TargetSpace::derivativeOfDistanceSquaredWRTSecondArgument(a, b); + + // finite-difference approximation + Dune::FieldMatrix<double,TargetSpace::TangentVector::dimension,embeddedDim> B = b.orthonormalFrame(); + + typename TargetSpace::TangentVector d2_fd; + for (size_t i=0; i<TargetSpace::TangentVector::dimension; i++) { + + typename TargetSpace::EmbeddedTangentVector fwVariation = eps * B[i]; + typename TargetSpace::EmbeddedTangentVector bwVariation = -eps * B[i]; + TargetSpace bPlus = TargetSpace::exp(b,fwVariation); + TargetSpace bMinus = TargetSpace::exp(b,bwVariation); + + d2_fd[i] = (distanceSquared(a,bPlus) - distanceSquared(a,bMinus)) / (2*eps); + } + + // transform into embedded coordinates + typename TargetSpace::EmbeddedTangentVector d2_fd_embedded; + B.mtv(d2_fd,d2_fd_embedded); + + if ( (d2 - d2_fd_embedded).infinity_norm() > 200*eps ) { + std::cout << className(a) << ": Analytical gradient does not match fd approximation." << std::endl; + std::cout << "d2 Analytical: " << d2 << std::endl; + std::cout << "d2 FD : " << d2_fd << std::endl; + assert(false); + } - if ( (d2 - d2_fd_embedded).infinity_norm() > 200*eps ) { - std::cout << className(a) << ": Analytical gradient does not match fd approximation." << std::endl; - std::cout << "d2 Analytical: " << d2 << std::endl; - std::cout << "d2 FD : " << d2_fd << std::endl; - assert(false); - } - } template <class TargetSpace> void testHessianOfDistanceSquared(const TargetSpace& a, const TargetSpace& b) { - static const int embeddedDim = TargetSpace::embeddedDim; - - /////////////////////////////////////////////////////////////////// - // Test second derivative with respect to second argument - /////////////////////////////////////////////////////////////////// - FieldMatrix<double,embeddedDim,embeddedDim> d2d2 = (TargetSpace::secondDerivativeOfDistanceSquaredWRTSecondArgument(a, b)).matrix(); - - // finite-difference approximation - FieldMatrix<double,embeddedDim,embeddedDim> d2d2_fd = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(a,b); - - if ( (d2d2 - d2d2_fd).infinity_norm() > 200*eps) { - std::cout << className(a) << ": Analytical second derivative does not match fd approximation." << std::endl; - std::cout << "d2d2 Analytical:" << std::endl << d2d2 << std::endl; - std::cout << "d2d2 FD :" << std::endl << d2d2_fd << std::endl; - assert(false); - } - + static const int embeddedDim = TargetSpace::embeddedDim; + + /////////////////////////////////////////////////////////////////// + // Test second derivative with respect to second argument + /////////////////////////////////////////////////////////////////// + FieldMatrix<double,embeddedDim,embeddedDim> d2d2 = (TargetSpace::secondDerivativeOfDistanceSquaredWRTSecondArgument(a, b)).matrix(); + + // finite-difference approximation + FieldMatrix<double,embeddedDim,embeddedDim> d2d2_fd = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(a,b); + + if ( (d2d2 - d2d2_fd).infinity_norm() > 200*eps) { + std::cout << className(a) << ": Analytical second derivative does not match fd approximation." << std::endl; + std::cout << "d2d2 Analytical:" << std::endl << d2d2 << std::endl; + std::cout << "d2d2 FD :" << std::endl << d2d2_fd << std::endl; + assert(false); + } + } template <class TargetSpace> void testMixedDerivativesOfDistanceSquared(const TargetSpace& a, const TargetSpace& b) { - static const size_t embeddedDim = TargetSpace::embeddedDim; - - ////////////////////////////////////////////////////////////////////////////// - // Test mixed second derivative with respect to first and second argument - ////////////////////////////////////////////////////////////////////////////// - - FieldMatrix<double,embeddedDim,embeddedDim> d1d2 = TargetSpace::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(a, b); - - // finite-difference approximation - FieldMatrix<double,embeddedDim,embeddedDim> d1d2_fd; - - for (size_t i=0; i<embeddedDim; i++) { - for (size_t j=0; j<embeddedDim; j++) { - - FieldVector<double,embeddedDim> aPlus = a.globalCoordinates(); - FieldVector<double,embeddedDim> aMinus = a.globalCoordinates(); - aPlus[i] += eps; - aMinus[i] -= eps; - - FieldVector<double,embeddedDim> bPlus = b.globalCoordinates(); - FieldVector<double,embeddedDim> bMinus = b.globalCoordinates(); - bPlus[j] += eps; - bMinus[j] -= eps; - - d1d2_fd[i][j] = (distanceSquared<TargetSpace>(aPlus,bPlus) + distanceSquared<TargetSpace>(aMinus,bMinus) - - distanceSquared<TargetSpace>(aPlus,bMinus) - distanceSquared<TargetSpace>(aMinus,bPlus)) / (4*eps*eps); - - } - } - - if ( (d1d2 - d1d2_fd).infinity_norm() > 200*eps ) { - std::cout << className(a) << ": Analytical mixed second derivative does not match fd approximation." << std::endl; - std::cout << "d1d2 Analytical:" << std::endl << d1d2 << std::endl; - std::cout << "d1d2 FD :" << std::endl << d1d2_fd << std::endl; - assert(false); + static const size_t embeddedDim = TargetSpace::embeddedDim; + + ////////////////////////////////////////////////////////////////////////////// + // Test mixed second derivative with respect to first and second argument + ////////////////////////////////////////////////////////////////////////////// + + FieldMatrix<double,embeddedDim,embeddedDim> d1d2 = TargetSpace::secondDerivativeOfDistanceSquaredWRTFirstAndSecondArgument(a, b); + + // finite-difference approximation + FieldMatrix<double,embeddedDim,embeddedDim> d1d2_fd; + + for (size_t i=0; i<embeddedDim; i++) { + for (size_t j=0; j<embeddedDim; j++) { + + FieldVector<double,embeddedDim> aPlus = a.globalCoordinates(); + FieldVector<double,embeddedDim> aMinus = a.globalCoordinates(); + aPlus[i] += eps; + aMinus[i] -= eps; + + FieldVector<double,embeddedDim> bPlus = b.globalCoordinates(); + FieldVector<double,embeddedDim> bMinus = b.globalCoordinates(); + bPlus[j] += eps; + bMinus[j] -= eps; + + d1d2_fd[i][j] = (distanceSquared<TargetSpace>(aPlus,bPlus) + distanceSquared<TargetSpace>(aMinus,bMinus) + - distanceSquared<TargetSpace>(aPlus,bMinus) - distanceSquared<TargetSpace>(aMinus,bPlus)) / (4*eps*eps); + } + } + + if ( (d1d2 - d1d2_fd).infinity_norm() > 200*eps ) { + std::cout << className(a) << ": Analytical mixed second derivative does not match fd approximation." << std::endl; + std::cout << "d1d2 Analytical:" << std::endl << d1d2 << std::endl; + std::cout << "d1d2 FD :" << std::endl << d1d2_fd << std::endl; + assert(false); + } } @@ -225,38 +225,38 @@ void testMixedDerivativesOfDistanceSquared(const TargetSpace& a, const TargetSpa template <class TargetSpace> void testDerivativeOfHessianOfDistanceSquared(const TargetSpace& a, const TargetSpace& b) { - static const size_t embeddedDim = TargetSpace::embeddedDim; - - ///////////////////////////////////////////////////////////////////////////////////////////// - // Test mixed third derivative with respect to first (once) and second (twice) argument - ///////////////////////////////////////////////////////////////////////////////////////////// - - Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d2d2d2 = TargetSpace::thirdDerivativeOfDistanceSquaredWRTSecondArgument(a, b); - - Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d2d2d2_fd; - - for (size_t i=0; i<embeddedDim; i++) { - - FieldVector<double,embeddedDim> bPlus = b.globalCoordinates(); - FieldVector<double,embeddedDim> bMinus = b.globalCoordinates(); - bPlus[i] += eps; - bMinus[i] -= eps; - - FieldMatrix<double,embeddedDim,embeddedDim> hPlus = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(a,TargetSpace(bPlus)); - FieldMatrix<double,embeddedDim,embeddedDim> hMinus = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(a,TargetSpace(bMinus)); - - d2d2d2_fd[i] = hPlus; - d2d2d2_fd[i] -= hMinus; - d2d2d2_fd[i] /= 2*eps; - - } + static const size_t embeddedDim = TargetSpace::embeddedDim; - if ( (d2d2d2 - d2d2d2_fd).infinity_norm() > 200*eps) { - std::cout << className(a) << ": Analytical third derivative does not match fd approximation." << std::endl; - std::cout << "d2d2d2 Analytical:" << std::endl << d2d2d2 << std::endl; - std::cout << "d2d2d2 FD :" << std::endl << d2d2d2_fd << std::endl; - assert(false); - } + ///////////////////////////////////////////////////////////////////////////////////////////// + // Test mixed third derivative with respect to first (once) and second (twice) argument + ///////////////////////////////////////////////////////////////////////////////////////////// + + Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d2d2d2 = TargetSpace::thirdDerivativeOfDistanceSquaredWRTSecondArgument(a, b); + + Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d2d2d2_fd; + + for (size_t i=0; i<embeddedDim; i++) { + + FieldVector<double,embeddedDim> bPlus = b.globalCoordinates(); + FieldVector<double,embeddedDim> bMinus = b.globalCoordinates(); + bPlus[i] += eps; + bMinus[i] -= eps; + + FieldMatrix<double,embeddedDim,embeddedDim> hPlus = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(a,TargetSpace(bPlus)); + FieldMatrix<double,embeddedDim,embeddedDim> hMinus = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(a,TargetSpace(bMinus)); + + d2d2d2_fd[i] = hPlus; + d2d2d2_fd[i] -= hMinus; + d2d2d2_fd[i] /= 2*eps; + + } + + if ( (d2d2d2 - d2d2d2_fd).infinity_norm() > 200*eps) { + std::cout << className(a) << ": Analytical third derivative does not match fd approximation." << std::endl; + std::cout << "d2d2d2 Analytical:" << std::endl << d2d2d2 << std::endl; + std::cout << "d2d2d2 FD :" << std::endl << d2d2d2_fd << std::endl; + assert(false); + } } @@ -264,38 +264,38 @@ void testDerivativeOfHessianOfDistanceSquared(const TargetSpace& a, const Target template <class TargetSpace> void testMixedDerivativeOfHessianOfDistanceSquared(const TargetSpace& a, const TargetSpace& b) { - static const size_t embeddedDim = TargetSpace::embeddedDim; - - ///////////////////////////////////////////////////////////////////////////////////////////// - // Test mixed third derivative with respect to first (once) and second (twice) argument - ///////////////////////////////////////////////////////////////////////////////////////////// - - Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d1d2d2 = TargetSpace::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(a, b); - - Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d1d2d2_fd; - - for (size_t i=0; i<embeddedDim; i++) { - - FieldVector<double,embeddedDim> aPlus = a.globalCoordinates(); - FieldVector<double,embeddedDim> aMinus = a.globalCoordinates(); - aPlus[i] += eps; - aMinus[i] -= eps; - - FieldMatrix<double,embeddedDim,embeddedDim> hPlus = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(TargetSpace(aPlus),b); - FieldMatrix<double,embeddedDim,embeddedDim> hMinus = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(TargetSpace(aMinus),b); - - d1d2d2_fd[i] = hPlus; - d1d2d2_fd[i] -= hMinus; - d1d2d2_fd[i] /= 2*eps; - - } + static const size_t embeddedDim = TargetSpace::embeddedDim; - if ( (d1d2d2 - d1d2d2_fd).infinity_norm() > 200*eps ) { - std::cout << className(a) << ": Analytical mixed third derivative does not match fd approximation." << std::endl; - std::cout << "d1d2d2 Analytical:" << std::endl << d1d2d2 << std::endl; - std::cout << "d1d2d2 FD :" << std::endl << d1d2d2_fd << std::endl; - assert(false); - } + ///////////////////////////////////////////////////////////////////////////////////////////// + // Test mixed third derivative with respect to first (once) and second (twice) argument + ///////////////////////////////////////////////////////////////////////////////////////////// + + Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d1d2d2 = TargetSpace::thirdDerivativeOfDistanceSquaredWRTFirst1AndSecond2Argument(a, b); + + Tensor3<double,embeddedDim,embeddedDim,embeddedDim> d1d2d2_fd; + + for (size_t i=0; i<embeddedDim; i++) { + + FieldVector<double,embeddedDim> aPlus = a.globalCoordinates(); + FieldVector<double,embeddedDim> aMinus = a.globalCoordinates(); + aPlus[i] += eps; + aMinus[i] -= eps; + + FieldMatrix<double,embeddedDim,embeddedDim> hPlus = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(TargetSpace(aPlus),b); + FieldMatrix<double,embeddedDim,embeddedDim> hMinus = getSecondDerivativeOfSecondArgumentFD<TargetSpace,embeddedDim>(TargetSpace(aMinus),b); + + d1d2d2_fd[i] = hPlus; + d1d2d2_fd[i] -= hMinus; + d1d2d2_fd[i] /= 2*eps; + + } + + if ( (d1d2d2 - d1d2d2_fd).infinity_norm() > 200*eps ) { + std::cout << className(a) << ": Analytical mixed third derivative does not match fd approximation." << std::endl; + std::cout << "d1d2d2 Analytical:" << std::endl << d1d2d2 << std::endl; + std::cout << "d1d2d2 FD :" << std::endl << d1d2d2_fd << std::endl; + assert(false); + } } @@ -303,36 +303,36 @@ void testMixedDerivativeOfHessianOfDistanceSquared(const TargetSpace& a, const T template <class TargetSpace> void testDerivativesOfDistanceSquared(const TargetSpace& a, const TargetSpace& b) { - - /////////////////////////////////////////////////////////////////// - // Test derivative with respect to second argument - /////////////////////////////////////////////////////////////////// - - testDerivativeOfDistanceSquared<TargetSpace>(a,b); - - /////////////////////////////////////////////////////////////////// - // Test second derivative with respect to second argument - /////////////////////////////////////////////////////////////////// - - testHessianOfDistanceSquared<TargetSpace>(a,b); - - ////////////////////////////////////////////////////////////////////////////// - // Test mixed second derivative with respect to first and second argument - ////////////////////////////////////////////////////////////////////////////// - - testMixedDerivativesOfDistanceSquared<TargetSpace>(a,b); - - ///////////////////////////////////////////////////////////////////////////////////////////// - // Test third derivative with respect to second argument - ///////////////////////////////////////////////////////////////////////////////////////////// - - testDerivativeOfHessianOfDistanceSquared<TargetSpace>(a,b); - - ///////////////////////////////////////////////////////////////////////////////////////////// - // Test mixed third derivative with respect to first (once) and second (twice) argument - ///////////////////////////////////////////////////////////////////////////////////////////// - - testMixedDerivativeOfHessianOfDistanceSquared<TargetSpace>(a,b); + + /////////////////////////////////////////////////////////////////// + // Test derivative with respect to second argument + /////////////////////////////////////////////////////////////////// + + testDerivativeOfDistanceSquared<TargetSpace>(a,b); + + /////////////////////////////////////////////////////////////////// + // Test second derivative with respect to second argument + /////////////////////////////////////////////////////////////////// + + testHessianOfDistanceSquared<TargetSpace>(a,b); + + ////////////////////////////////////////////////////////////////////////////// + // Test mixed second derivative with respect to first and second argument + ////////////////////////////////////////////////////////////////////////////// + + testMixedDerivativesOfDistanceSquared<TargetSpace>(a,b); + + ///////////////////////////////////////////////////////////////////////////////////////////// + // Test third derivative with respect to second argument + ///////////////////////////////////////////////////////////////////////////////////////////// + + testDerivativeOfHessianOfDistanceSquared<TargetSpace>(a,b); + + ///////////////////////////////////////////////////////////////////////////////////////////// + // Test mixed third derivative with respect to first (once) and second (twice) argument + ///////////////////////////////////////////////////////////////////////////////////////////// + + testMixedDerivativeOfHessianOfDistanceSquared<TargetSpace>(a,b); } @@ -346,80 +346,80 @@ void testDerivativesOfDistanceSquared(const TargetSpace& a, const TargetSpace& b // come back. namespace Dune::GFE { - void testUsingIOStream() - { - std::cout << "dummy text" << std::endl; - } + void testUsingIOStream() + { + std::cout << "dummy text" << std::endl; + } } template <class TargetSpace> void test() { - std::cout << "Testing class " << className<TargetSpace>() << std::endl; - - std::vector<TargetSpace> testPoints; - ValueFactory<TargetSpace>::get(testPoints); - - int nTestPoints = testPoints.size(); - - // Test each element in the list - for (int i=0; i<nTestPoints; i++) { - - //testOrthonormalFrame<TargetSpace>(testPoints[i]); - - for (int j=0; j<nTestPoints; j++) { - - std::vector<TargetSpace> testPointPair(2); - testPointPair[0] = testPoints[i]; - testPointPair[1] = testPoints[j]; - if (diameter(testPointPair) > TargetSpace::convexityRadius) - continue; - - // Test the exponential map and the logarithm - testExpLog(testPoints[i], testPoints[j]); - - // Test the various derivatives of the squared distance - testDerivativesOfDistanceSquared<TargetSpace>(testPoints[i], testPoints[j]); - - } - + std::cout << "Testing class " << className<TargetSpace>() << std::endl; + + std::vector<TargetSpace> testPoints; + ValueFactory<TargetSpace>::get(testPoints); + + int nTestPoints = testPoints.size(); + + // Test each element in the list + for (int i=0; i<nTestPoints; i++) { + + //testOrthonormalFrame<TargetSpace>(testPoints[i]); + + for (int j=0; j<nTestPoints; j++) { + + std::vector<TargetSpace> testPointPair(2); + testPointPair[0] = testPoints[i]; + testPointPair[1] = testPoints[j]; + if (diameter(testPointPair) > TargetSpace::convexityRadius) + continue; + + // Test the exponential map and the logarithm + testExpLog(testPoints[i], testPoints[j]); + + // Test the various derivatives of the squared distance + testDerivativesOfDistanceSquared<TargetSpace>(testPoints[i], testPoints[j]); + } - - // Test whether we can rebind to another number type - using FTargetSpace = typename TargetSpace::template rebind<float>::other; - // Can we construct an object of that rebound type? - FTargetSpace fTargetSpace; + } + + // Test whether we can rebind to another number type + using FTargetSpace = typename TargetSpace::template rebind<float>::other; + + // Can we construct an object of that rebound type? + FTargetSpace fTargetSpace; } int main() try { - // Test the RealTuple class - test<RealTuple<double,1> >(); - test<RealTuple<double,3> >(); - - // Test the UnitVector class - test<UnitVector<double,2> >(); - test<UnitVector<double,3> >(); - test<UnitVector<double,4> >(); - - // Test the rotation class - test<Rotation<double,3> >(); - - // Test the ProductManifold class - test<Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2>>>(); - test<Dune::GFE::ProductManifold<Rotation<double,3>,UnitVector<double,5>>>(); - - // Test the RigidBodyMotion class - test<RigidBodyMotion<double,3> >(); -// -// test<HyperbolicHalfspacePoint<double,2> >(); + // Test the RealTuple class + test<RealTuple<double,1> >(); + test<RealTuple<double,3> >(); + + // Test the UnitVector class + test<UnitVector<double,2> >(); + test<UnitVector<double,3> >(); + test<UnitVector<double,4> >(); + + // Test the rotation class + test<Rotation<double,3> >(); -} catch (Exception& e) { + // Test the ProductManifold class + test<Dune::GFE::ProductManifold<RealTuple<double,1>,Rotation<double,3>,UnitVector<double,2> > >(); + test<Dune::GFE::ProductManifold<Rotation<double,3>,UnitVector<double,5> > >(); - std::cout << e.what() << std::endl; + // Test the RigidBodyMotion class + test<RigidBodyMotion<double,3> >(); + // + // test<HyperbolicHalfspacePoint<double,2> >(); } +catch (Exception& e) { + std::cout << e.what() << std::endl; + +} diff --git a/test/test-cylinder.cc b/test/test-cylinder.cc index 76419928..1fb5090c 100644 --- a/test/test-cylinder.cc +++ b/test/test-cylinder.cc @@ -31,84 +31,84 @@ using namespace Dune; int main (int argc, char *argv[]) { - MPIHelper::instance(argc,argv); - // Cylinder - static const double radius = 4; - static const double height = 2; - static const double cylinderFraction = 0.75; - - ///////////////////////////////////////////// - // Create the grid for the cylinder - ///////////////////////////////////////////// - - struct CylinderCreator - : public Dune :: AnalyticalCoordFunction< double, 2, 3, CylinderCreator > + MPIHelper::instance(argc,argv); + // Cylinder + static const double radius = 4; + static const double height = 2; + static const double cylinderFraction = 0.75; + + ///////////////////////////////////////////// + // Create the grid for the cylinder + ///////////////////////////////////////////// + + struct CylinderCreator + : public Dune :: AnalyticalCoordFunction< double, 2, 3, CylinderCreator > + { + FieldVector<double,3> operator() ( const FieldVector<double, 2> &x ) const { - FieldVector<double,3> operator() ( const FieldVector<double, 2> &x ) const - { - FieldVector<double,3> y; - y[0] = radius * std::cos(x[0]); - y[1] = radius * std::sin(x[0]); - y[2] = x[1]; - return y; - } - }; - - using GridType = Dune::GeometryGrid< YaspGrid<gridDim>, CylinderCreator>; - std::shared_ptr<YaspGrid<gridDim>> hostGrid; - hostGrid = Dune::StructuredGridFactory<Dune::YaspGrid<2>>::createCubeGrid({0, 0}, {cylinderFraction*2*pi, height}, {elementsCircle,2}); - auto cylinderCreator = std::make_shared<CylinderCreator>(); - auto grid = std::make_shared<GridType>(hostGrid, cylinderCreator); - auto gridView = grid->leafGridView(); - - auto cylinder = CylinderProjection<double>{radius}; - auto polynomialCylinderGF = analyticDiscreteFunction(cylinder, *grid, approximationOrderAnalytics); - - auto analyticCylinderGF = cylinderGridFunction<GridType>(radius); - auto cylinderLocalFunction = localFunction(analyticCylinderGF); - - auto quadOrder = 10; - for (const auto& element : elements(gridView, Dune::Partitions::interior)) { - using DT = decltype(gridView)::ctype; - - Dune::CurvedGeometry<DT, gridDim, dimWorld, Dune::CurvedGeometryTraits<DT, Dune::LagrangeLFECache<DT,DT,gridDim>>> - polynomialGeometry(Dune::referenceElement(element.geometry()), [element, polynomialCylinderGF](const auto& local) { - auto localGridFunction = localFunction(polynomialCylinderGF); - localGridFunction.bind(element); - return localGridFunction(local); - }, approximationOrderGeometry); - - cylinderLocalFunction.bind(element); - auto localGeometry = Dune::DefaultLocalGeometry<double,2,2>{}; - auto analyticGeometry = Dune::LocalFunctionGeometry{element.type(), cylinderLocalFunction, localGeometry}; - Dune::LagrangeLFECache<DT,DT,gridDim> cache(approximationOrderAnalytics); - auto refEle = referenceElement(element); - auto lFE = cache.get(refEle.type()); - - const auto& quad = Dune::QuadratureRules<DT, gridDim>::rule(element.type(), quadOrder); - for (size_t pt=0; pt<quad.size(); pt++) { - // Check if mean curvature is correct - auto realMeanCurvature = std::abs(cylinder.mean_curvature(polynomialGeometry.global(quad[pt].position()))); - - auto normalDerivativeP = polynomialGeometry.normalGradient(quad[pt].position()); - auto approximatedCurvatureP = 0.5*std::abs(Dune::GFE::trace(normalDerivativeP)); - auto relativeDifferenceP = std::abs((realMeanCurvature - approximatedCurvatureP)/realMeanCurvature); - - auto normalDerivativeA = analyticGeometry.normalGradient(quad[pt].position(), lFE); - auto approximatedCurvatureA = 0.5*std::abs(Dune::GFE::trace(normalDerivativeA)); - std::cout << approximatedCurvatureA << std::endl; - auto relativeDifferenceA = std::abs((realMeanCurvature - approximatedCurvatureA)/realMeanCurvature); - - if (relativeDifferenceP > 0.005){ - std::cerr << "At point " << polynomialGeometry.global(quad[pt].position()) << " the curvature (approximated using a polynomial) is " - << approximatedCurvatureP << " but " << realMeanCurvature << " was expected!" << std::endl; - return 1; - } - if (relativeDifferenceA > 0.005){ - std::cerr << "At point " << polynomialGeometry.global(quad[pt].position()) << " the curvature (approximated using the real analytic cylinder function) is " - << approximatedCurvatureA << " but " << realMeanCurvature << " was expected!" << std::endl; - return 1; - } - } + FieldVector<double,3> y; + y[0] = radius * std::cos(x[0]); + y[1] = radius * std::sin(x[0]); + y[2] = x[1]; + return y; } + }; + + using GridType = Dune::GeometryGrid< YaspGrid<gridDim>, CylinderCreator>; + std::shared_ptr<YaspGrid<gridDim> > hostGrid; + hostGrid = Dune::StructuredGridFactory<Dune::YaspGrid<2> >::createCubeGrid({0, 0}, {cylinderFraction*2*pi, height}, {elementsCircle,2}); + auto cylinderCreator = std::make_shared<CylinderCreator>(); + auto grid = std::make_shared<GridType>(hostGrid, cylinderCreator); + auto gridView = grid->leafGridView(); + + auto cylinder = CylinderProjection<double>{radius}; + auto polynomialCylinderGF = analyticDiscreteFunction(cylinder, *grid, approximationOrderAnalytics); + + auto analyticCylinderGF = cylinderGridFunction<GridType>(radius); + auto cylinderLocalFunction = localFunction(analyticCylinderGF); + + auto quadOrder = 10; + for (const auto& element : elements(gridView, Dune::Partitions::interior)) { + using DT = decltype(gridView)::ctype; + + Dune::CurvedGeometry<DT, gridDim, dimWorld, Dune::CurvedGeometryTraits<DT, Dune::LagrangeLFECache<DT,DT,gridDim> > > + polynomialGeometry(Dune::referenceElement(element.geometry()), [element, polynomialCylinderGF](const auto& local) { + auto localGridFunction = localFunction(polynomialCylinderGF); + localGridFunction.bind(element); + return localGridFunction(local); + }, approximationOrderGeometry); + + cylinderLocalFunction.bind(element); + auto localGeometry = Dune::DefaultLocalGeometry<double,2,2>{}; + auto analyticGeometry = Dune::LocalFunctionGeometry{element.type(), cylinderLocalFunction, localGeometry}; + Dune::LagrangeLFECache<DT,DT,gridDim> cache(approximationOrderAnalytics); + auto refEle = referenceElement(element); + auto lFE = cache.get(refEle.type()); + + const auto& quad = Dune::QuadratureRules<DT, gridDim>::rule(element.type(), quadOrder); + for (size_t pt=0; pt<quad.size(); pt++) { + // Check if mean curvature is correct + auto realMeanCurvature = std::abs(cylinder.mean_curvature(polynomialGeometry.global(quad[pt].position()))); + + auto normalDerivativeP = polynomialGeometry.normalGradient(quad[pt].position()); + auto approximatedCurvatureP = 0.5*std::abs(Dune::GFE::trace(normalDerivativeP)); + auto relativeDifferenceP = std::abs((realMeanCurvature - approximatedCurvatureP)/realMeanCurvature); + + auto normalDerivativeA = analyticGeometry.normalGradient(quad[pt].position(), lFE); + auto approximatedCurvatureA = 0.5*std::abs(Dune::GFE::trace(normalDerivativeA)); + std::cout << approximatedCurvatureA << std::endl; + auto relativeDifferenceA = std::abs((realMeanCurvature - approximatedCurvatureA)/realMeanCurvature); + + if (relativeDifferenceP > 0.005) { + std::cerr << "At point " << polynomialGeometry.global(quad[pt].position()) << " the curvature (approximated using a polynomial) is " + << approximatedCurvatureP << " but " << realMeanCurvature << " was expected!" << std::endl; + return 1; + } + if (relativeDifferenceA > 0.005) { + std::cerr << "At point " << polynomialGeometry.global(quad[pt].position()) << " the curvature (approximated using the real analytic cylinder function) is " + << approximatedCurvatureA << " but " << realMeanCurvature << " was expected!" << std::endl; + return 1; + } + } + } } diff --git a/test/valuefactory.hh b/test/valuefactory.hh index 03f3f0af..867fd452 100644 --- a/test/valuefactory.hh +++ b/test/valuefactory.hh @@ -18,7 +18,7 @@ template <class T> class ValueFactory { public: - static void get(std::vector<T>& values); + static void get(std::vector<T>& values); }; @@ -31,17 +31,17 @@ template <> class ValueFactory<RealTuple<double,1> > { public: - static void get(std::vector<RealTuple<double,1> >& values) { + static void get(std::vector<RealTuple<double,1> >& values) { - std::vector<double> testPoints = {-3, -1, 0, 2, 4}; + std::vector<double> testPoints = {-3, -1, 0, 2, 4}; - values.resize(testPoints.size()); + values.resize(testPoints.size()); - // Set up elements of S^1 - for (size_t i=0; i<values.size(); i++) - values[i] = RealTuple<double,1>(testPoints[i]); + // Set up elements of S^1 + for (size_t i=0; i<values.size(); i++) + values[i] = RealTuple<double,1>(testPoints[i]); - } + } }; @@ -53,20 +53,20 @@ template <> class ValueFactory<RealTuple<double,3> > { public: - static void get(std::vector<RealTuple<double,3> >& values) { + static void get(std::vector<RealTuple<double,3> >& values) { - std::vector<Dune::FieldVector<double,3> > testPoints = {{1,0,0}, {0,1,0}, {-0.838114,0.356751,-0.412667}, - {-0.490946,-0.306456,0.81551},{-0.944506,0.123687,-0.304319}, - {-0.6,0.1,-0.2},{0.45,0.12,0.517}, - {-0.1,0.3,-0.1},{-0.444506,0.123687,0.104319},{-0.7,-0.123687,-0.304319}}; + std::vector<Dune::FieldVector<double,3> > testPoints = {{1,0,0}, {0,1,0}, {-0.838114,0.356751,-0.412667}, + {-0.490946,-0.306456,0.81551},{-0.944506,0.123687,-0.304319}, + {-0.6,0.1,-0.2},{0.45,0.12,0.517}, + {-0.1,0.3,-0.1},{-0.444506,0.123687,0.104319},{-0.7,-0.123687,-0.304319}}; - values.resize(testPoints.size()); + values.resize(testPoints.size()); - // Set up elements of S^1 - for (size_t i=0; i<values.size(); i++) - values[i] = RealTuple<double,3>(testPoints[i]); + // Set up elements of S^1 + for (size_t i=0; i<values.size(); i++) + values[i] = RealTuple<double,3>(testPoints[i]); - } + } }; @@ -78,17 +78,17 @@ template <> class ValueFactory<UnitVector<double,2> > { public: - static void get(std::vector<UnitVector<double,2> >& values) { + static void get(std::vector<UnitVector<double,2> >& values) { - std::vector<std::array<double,2> > testPoints = {{1,0}, {0.5,0.5}, {0,1}, {-0.5,0.5}, {-1,0}, {-0.5,-0.5}, {0,-1}, {0.5,-0.5}, {0.1,1}, {1,.1}}; + std::vector<std::array<double,2> > testPoints = {{1,0}, {0.5,0.5}, {0,1}, {-0.5,0.5}, {-1,0}, {-0.5,-0.5}, {0,-1}, {0.5,-0.5}, {0.1,1}, {1,.1}}; - values.resize(testPoints.size()); + values.resize(testPoints.size()); - // Set up elements of S^1 - for (size_t i=0; i<values.size(); i++) - values[i] = UnitVector<double,2>(testPoints[i]); + // Set up elements of S^1 + for (size_t i=0; i<values.size(); i++) + values[i] = UnitVector<double,2>(testPoints[i]); - } + } }; @@ -101,20 +101,20 @@ template <> class ValueFactory<UnitVector<double,3> > { public: - static void get(std::vector<UnitVector<double,3> >& values) { + static void get(std::vector<UnitVector<double,3> >& values) { - std::vector<std::array<double,3> > testPoints = {{1,0,0}, {0,1,0}, {-0.838114,0.356751,-0.412667}, - {-0.490946,-0.306456,0.81551},{-0.944506,0.123687,-0.304319}, - {-0.6,0.1,-0.2},{0.45,0.12,0.517}, - {-0.1,0.3,-0.1},{-0.444506,0.123687,0.104319},{-0.7,-0.123687,-0.304319}}; + std::vector<std::array<double,3> > testPoints = {{1,0,0}, {0,1,0}, {-0.838114,0.356751,-0.412667}, + {-0.490946,-0.306456,0.81551},{-0.944506,0.123687,-0.304319}, + {-0.6,0.1,-0.2},{0.45,0.12,0.517}, + {-0.1,0.3,-0.1},{-0.444506,0.123687,0.104319},{-0.7,-0.123687,-0.304319}}; - values.resize(testPoints.size()); + values.resize(testPoints.size()); - // Set up elements of S^1 - for (size_t i=0; i<values.size(); i++) - values[i] = UnitVector<double,3>(testPoints[i]); + // Set up elements of S^1 + for (size_t i=0; i<values.size(); i++) + values[i] = UnitVector<double,3>(testPoints[i]); - } + } }; @@ -127,21 +127,21 @@ template <> class ValueFactory<UnitVector<double,4> > { public: - static void get(std::vector<UnitVector<double,4> >& values) { + static void get(std::vector<UnitVector<double,4> >& values) { - std::vector<std::array<double,4> > testPoints = {{1,0,0,0}, {0,1,0,0}, {-0.838114,0.356751,-0.412667,0.5}, - {-0.490946,-0.306456,0.81551,0.23},{-0.944506,0.123687,-0.304319,-0.7}, - {-0.6,0.1,-0.2,0.8},{0.45,0.12,0.517,0}, - {-0.1,0.3,-0.1,0.73},{-0.444506,0.123687,0.104319,-0.23},{-0.7,-0.123687,-0.304319,0.72}}; + std::vector<std::array<double,4> > testPoints = {{1,0,0,0}, {0,1,0,0}, {-0.838114,0.356751,-0.412667,0.5}, + {-0.490946,-0.306456,0.81551,0.23},{-0.944506,0.123687,-0.304319,-0.7}, + {-0.6,0.1,-0.2,0.8},{0.45,0.12,0.517,0}, + {-0.1,0.3,-0.1,0.73},{-0.444506,0.123687,0.104319,-0.23},{-0.7,-0.123687,-0.304319,0.72}}; - values.resize(testPoints.size()); + values.resize(testPoints.size()); - // Set up elements of S^1 - for (size_t i=0; i<values.size(); i++) - values[i] = UnitVector<double,4>(testPoints[i]); + // Set up elements of S^1 + for (size_t i=0; i<values.size(); i++) + values[i] = UnitVector<double,4>(testPoints[i]); - } + } }; @@ -154,21 +154,21 @@ template <> class ValueFactory<Rotation<double,3> > { public: - static void get(std::vector<Rotation<double,3> >& values) { + static void get(std::vector<Rotation<double,3> >& values) { - std::vector<std::array<double,4> > testPoints = {{1,0,0,0}, {0,1,0,0}, {-0.838114,0.356751,-0.412667,0.5}, - {-0.490946,-0.306456,0.81551,0.23},{-0.944506,0.123687,-0.304319,-0.7}, - {-0.6,0.1,-0.2,0.8},{0.45,0.12,0.517,0}, - {-0.1,0.3,-0.1,0.73},{-0.444506,0.123687,0.104319,-0.23},{-0.7,-0.123687,-0.304319,0.72}, - {-0.035669, -0.463824, -0.333265, 0.820079}, {0.0178678, 0.916836, 0.367358, 0.155374}}; + std::vector<std::array<double,4> > testPoints = {{1,0,0,0}, {0,1,0,0}, {-0.838114,0.356751,-0.412667,0.5}, + {-0.490946,-0.306456,0.81551,0.23},{-0.944506,0.123687,-0.304319,-0.7}, + {-0.6,0.1,-0.2,0.8},{0.45,0.12,0.517,0}, + {-0.1,0.3,-0.1,0.73},{-0.444506,0.123687,0.104319,-0.23},{-0.7,-0.123687,-0.304319,0.72}, + {-0.035669, -0.463824, -0.333265, 0.820079}, {0.0178678, 0.916836, 0.367358, 0.155374}}; - values.resize(testPoints.size()); + values.resize(testPoints.size()); - // Set up elements of S^1 - for (std::size_t i=0; i<values.size(); i++) - values[i] = Rotation<double,3>(testPoints[i]); + // Set up elements of S^1 + for (std::size_t i=0; i<values.size(); i++) + values[i] = Rotation<double,3>(testPoints[i]); - } + } }; @@ -180,23 +180,23 @@ template <> class ValueFactory<RigidBodyMotion<double,3> > { public: - static void get(std::vector<RigidBodyMotion<double,3> >& values) { + static void get(std::vector<RigidBodyMotion<double,3> >& values) { - std::vector<RealTuple<double,3> > rValues; - ValueFactory<RealTuple<double,3> >::get(rValues); + std::vector<RealTuple<double,3> > rValues; + ValueFactory<RealTuple<double,3> >::get(rValues); - std::vector<Rotation<double,3> > qValues; - ValueFactory<Rotation<double,3> >::get(qValues); + std::vector<Rotation<double,3> > qValues; + ValueFactory<Rotation<double,3> >::get(qValues); - int nTestPoints = std::min(rValues.size(), qValues.size()); + int nTestPoints = std::min(rValues.size(), qValues.size()); - values.resize(nTestPoints); + values.resize(nTestPoints); - // Set up elements of SE(3) - for (int i=0; i<nTestPoints; i++) - values[i] = RigidBodyMotion<double,3>(rValues[i].globalCoordinates(),qValues[i]); + // Set up elements of SE(3) + for (int i=0; i<nTestPoints; i++) + values[i] = RigidBodyMotion<double,3>(rValues[i].globalCoordinates(),qValues[i]); - } + } }; @@ -208,18 +208,18 @@ template <class T, int N> class ValueFactory<Dune::FieldMatrix<T,N,N> > { public: - static void get(std::vector<Dune::FieldMatrix<T,N,N> >& values) { + static void get(std::vector<Dune::FieldMatrix<T,N,N> >& values) { - int nTestPoints = 10; - values.resize(nTestPoints); + int nTestPoints = 10; + values.resize(nTestPoints); - // Set up elements of T^{N \times N} - for (int i=0; i<nTestPoints; i++) - for (int j=0; j<N; j++) - for (int k=0; k<N; k++) - values[i][j][k] = std::rand()%100 - 50; + // Set up elements of T^{N \times N} + for (int i=0; i<nTestPoints; i++) + for (int j=0; j<N; j++) + for (int k=0; k<N; k++) + values[i][j][k] = std::rand()%100 - 50; - } + } }; @@ -232,49 +232,49 @@ template <class T, int N> class ValueFactory<OrthogonalMatrix<T,N> > { - static Dune::FieldVector<T,N> proj(const Dune::FieldVector<T,N>& u, const Dune::FieldVector<T,N>& v) - { - Dune::FieldVector<T,N> result = u; - result *= (v*u) / (u*u); - return result; - } + static Dune::FieldVector<T,N> proj(const Dune::FieldVector<T,N>& u, const Dune::FieldVector<T,N>& v) + { + Dune::FieldVector<T,N> result = u; + result *= (v*u) / (u*u); + return result; + } public: - static void get(std::vector<OrthogonalMatrix<T,N> >& values) { + static void get(std::vector<OrthogonalMatrix<T,N> >& values) { - // Get general matrices - std::vector<Dune::FieldMatrix<T,N,N> > mValues; - ValueFactory<Dune::FieldMatrix<T,N,N> >::get(mValues); + // Get general matrices + std::vector<Dune::FieldMatrix<T,N,N> > mValues; + ValueFactory<Dune::FieldMatrix<T,N,N> >::get(mValues); - values.resize(mValues.size()); + values.resize(mValues.size()); - // Do Gram-Schmidt orthogonalization of the rows - for (size_t m=0; m<mValues.size(); m++) { + // Do Gram-Schmidt orthogonalization of the rows + for (size_t m=0; m<mValues.size(); m++) { - Dune::FieldMatrix<T,N,N>& v = mValues[m]; + Dune::FieldMatrix<T,N,N>& v = mValues[m]; - if (std::fabs(v.determinant()) < 1e-6) - continue; + if (std::fabs(v.determinant()) < 1e-6) + continue; - for (int j=0; j<N; j++) { + for (int j=0; j<N; j++) { - for (int i=0; i<j; i++) { + for (int i=0; i<j; i++) { - // v_j = v_j - proj_{v_i} v_j - v[j] -= proj(v[i],v[j]); + // v_j = v_j - proj_{v_i} v_j + v[j] -= proj(v[i],v[j]); - } - - // normalize - v[j] /= v[j].two_norm(); - } + } - values[m] = OrthogonalMatrix<T,N>(v); + // normalize + v[j] /= v[j].two_norm(); + } - } + values[m] = OrthogonalMatrix<T,N>(v); } + } + }; /** \brief A class that creates sets of values of various types, to be used in unit tests @@ -285,20 +285,20 @@ template <> class ValueFactory<HyperbolicHalfspacePoint<double,2> > { public: - static void get(std::vector<HyperbolicHalfspacePoint<double,2> >& values) { + static void get(std::vector<HyperbolicHalfspacePoint<double,2> >& values) { - std::vector<Dune::FieldVector<double,2> > testPoints = {{0,2}, {0,1}, {0,0.5}, - {-0.490946,0.81551},{-0.944506,0.304319}, - {-0.6,0.2},{0.45,0.517}, - {-0.1,0.1},{-0.444506,0.104319},{-0.7,0.304319}}; + std::vector<Dune::FieldVector<double,2> > testPoints = {{0,2}, {0,1}, {0,0.5}, + {-0.490946,0.81551},{-0.944506,0.304319}, + {-0.6,0.2},{0.45,0.517}, + {-0.1,0.1},{-0.444506,0.104319},{-0.7,0.304319}}; - values.resize(testPoints.size()); + values.resize(testPoints.size()); - // Set up elements of S^1 - for (size_t i=0; i<values.size(); i++) - values[i] = HyperbolicHalfspacePoint<double,2>(testPoints[i]); + // Set up elements of S^1 + for (size_t i=0; i<values.size(); i++) + values[i] = HyperbolicHalfspacePoint<double,2>(testPoints[i]); - } + } }; @@ -310,20 +310,20 @@ template <> class ValueFactory<HyperbolicHalfspacePoint<double,3> > { public: - static void get(std::vector<HyperbolicHalfspacePoint<double,3> >& values) { + static void get(std::vector<HyperbolicHalfspacePoint<double,3> >& values) { - std::vector<Dune::FieldVector<double,3> > testPoints = {{1,0,0.01}, {0,1,0.01}, {-0.838114,0.356751,0.412667}, - {-0.490946,-0.306456,0.81551},{-0.944506,0.123687,0.304319}, - {-0.6,0.1,0.2},{0.45,0.12,0.517}, - {-0.1,0.3,0.1},{-0.444506,0.123687,0.104319},{-0.7,-0.123687,0.304319}}; + std::vector<Dune::FieldVector<double,3> > testPoints = {{1,0,0.01}, {0,1,0.01}, {-0.838114,0.356751,0.412667}, + {-0.490946,-0.306456,0.81551},{-0.944506,0.123687,0.304319}, + {-0.6,0.1,0.2},{0.45,0.12,0.517}, + {-0.1,0.3,0.1},{-0.444506,0.123687,0.104319},{-0.7,-0.123687,0.304319}}; - values.resize(testPoints.size()); + values.resize(testPoints.size()); - // Set up elements of S^1 - for (size_t i=0; i<values.size(); i++) - values[i] = HyperbolicHalfspacePoint<double,3>(testPoints[i]); + // Set up elements of S^1 + for (size_t i=0; i<values.size(); i++) + values[i] = HyperbolicHalfspacePoint<double,3>(testPoints[i]); - } + } }; @@ -332,26 +332,27 @@ public: /** \brief A class that creates sets of values of various types, to be used in unit tests -* -* This is the specialization for ProducManifold<...> -*/ -template <typename ...TargetSpaces> + * + * This is the specialization for ProducManifold<...> + */ +template <typename ... TargetSpaces> class ValueFactory<Dune::GFE::ProductManifold<TargetSpaces...> > { -using TargetSpace = Dune::GFE::ProductManifold<TargetSpaces...>; + using TargetSpace = Dune::GFE::ProductManifold<TargetSpaces...>; public: - static void get(std::vector<TargetSpace >& values) { + static void get(std::vector<TargetSpace >& values) { - std::vector<typename TargetSpace::CoordinateType > testPoints(10); + std::vector<typename TargetSpace::CoordinateType > testPoints(10); - std::generate(testPoints.begin(), testPoints.end(), [](){ - return Dune::GFE::randomFieldVector<typename TargetSpace::field_type,TargetSpace::CoordinateType::dimension>(0.9,1.1) ; }); + std::generate(testPoints.begin(), testPoints.end(), [](){ + return Dune::GFE::randomFieldVector<typename TargetSpace::field_type,TargetSpace::CoordinateType::dimension>(0.9,1.1) ; + }); - values.resize(testPoints.size()); + values.resize(testPoints.size()); - std::transform(testPoints.cbegin(),testPoints.cend(),values.begin(),[](const auto& testPoint){return TargetSpace(testPoint);}); - } + std::transform(testPoints.cbegin(),testPoints.cend(),values.begin(),[](const auto& testPoint){return TargetSpace(testPoint);}); + } }; -- GitLab