From 7550c4a71098f4b51dbce2231d31d5bd9f906c50 Mon Sep 17 00:00:00 2001 From: Lisa Julia Nebel <lisa_julia.nebel@tu-dresden.de> Date: Tue, 14 May 2019 17:36:30 +0200 Subject: [PATCH] Add film-on-substrate Added program to solve a combined minimization problem: Minmize the sum of an elastic energy (representing the substrate) and a cosserat energy (representing a thin film on the substrate). --- dune/gfe/sumcosseratenergy.hh | 61 +++ dune/gfe/surfacecosseratenergy.hh | 599 +++++++++++++++++++++++++++++ src/cantilever-dirichlet-values.py | 17 + src/film-on-substrate.cc | 482 +++++++++++++++++++++++ src/film-on-substrate.parset | 123 ++++++ 5 files changed, 1282 insertions(+) create mode 100644 dune/gfe/sumcosseratenergy.hh create mode 100644 dune/gfe/surfacecosseratenergy.hh create mode 100644 src/cantilever-dirichlet-values.py create mode 100644 src/film-on-substrate.cc create mode 100644 src/film-on-substrate.parset diff --git a/dune/gfe/sumcosseratenergy.hh b/dune/gfe/sumcosseratenergy.hh new file mode 100644 index 00000000..0f146a84 --- /dev/null +++ b/dune/gfe/sumcosseratenergy.hh @@ -0,0 +1,61 @@ +#ifndef DUNE_GFE_SUMCOSSERATENERGY_HH +#define DUNE_GFE_SUMCOSSERATENERGY_HH + +#include <dune/elasticity/assemblers/localfestiffness.hh> + +#include <dune/gfe/localgeodesicfestiffness.hh> + +namespace Dune { + +namespace GFE { +template<class Basis, class TargetSpace, class field_type=double, class GradientRT=double> +class SumCosseratEnergy +: public LocalGeodesicFEStiffness<Basis, TargetSpace> +{ + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype ctype; + typedef typename Basis::LocalView::Tree::FiniteElement LocalFiniteElement; + typedef typename GridView::ctype DT; + typedef typename TargetSpace::ctype RT; + + enum {dim=GridView::dimension}; + +public: + + /** \brief Constructor with elastic energy and cosserat energy + * \param elasticEnergy The elastic energy + * \param cosseratEnergy The cosserat energy + */ + SumCosseratEnergy(std::shared_ptr<LocalFEStiffness<GridView,LocalFiniteElement,std::vector<Dune::FieldVector<field_type,dim> > , std::vector<GradientRT> > > elasticEnergy, + std::shared_ptr<LocalGeodesicFEStiffness<Basis, TargetSpace>> cosseratEnergy) + + : elasticEnergy_(elasticEnergy), + cosseratEnergy_(cosseratEnergy) + {} + + /** \brief Assemble the energy for a single element */ + RT energy (const typename Basis::LocalView& localView, + const std::vector<TargetSpace>& localSolution) const + { + auto&& element = localView.element(); + auto&& localFiniteElement = localView.tree().finiteElement(); + std::vector<Dune::FieldVector<field_type,dim>> localConfiguration(localSolution.size()); + for (int i = 0; i < localSolution.size(); i++) { + localConfiguration[i] = localSolution[i].r; + } + return elasticEnergy_->energy(element, localFiniteElement, localConfiguration) + cosseratEnergy_->energy(localView, localSolution); + } + +private: + + std::shared_ptr<LocalFEStiffness<GridView,LocalFiniteElement,std::vector<Dune::FieldVector<field_type,dim> >, std::vector<GradientRT> > > elasticEnergy_; + + std::shared_ptr<LocalGeodesicFEStiffness<Basis, TargetSpace> > cosseratEnergy_; +}; + +} // namespace GFE + +} // namespace Dune + +#endif //#ifndef DUNE_GFE_SUMCOSSERATENERGY_HH diff --git a/dune/gfe/surfacecosseratenergy.hh b/dune/gfe/surfacecosseratenergy.hh new file mode 100644 index 00000000..905ab45d --- /dev/null +++ b/dune/gfe/surfacecosseratenergy.hh @@ -0,0 +1,599 @@ +#ifndef DUNE_GFE_SURFACECOSSERATENERGY_HH +#define DUNE_GFE_SURFACECOSSERATENERGY_HH + +#include <dune/geometry/quadraturerules.hh> + +#include <dune/fufem/functions/virtualgridfunction.hh> +#include <dune/fufem/boundarypatch.hh> + +#include <dune/gfe/cosseratstrain.hh> +#include <dune/gfe/localgeodesicfefunction.hh> +#include <dune/gfe/localprojectedfefunction.hh> +#include <dune/gfe/mixedlocalgeodesicfestiffness.hh> +#include <dune/gfe/orthogonalmatrix.hh> +#include <dune/gfe/rigidbodymotion.hh> +#include <dune/gfe/tensor3.hh> +#include <dune/gfe/vertexnormals.hh> + +template <class Basis, std::size_t i> +class LocalFiniteElementFactory +{ +public: + static auto get(const typename Basis::LocalView& localView, + std::integral_constant<std::size_t, i> iType) + -> decltype(localView.tree().child(iType).finiteElement()) + { + return localView.tree().child(iType).finiteElement(); + } +}; + +/** \brief Specialize for scalar bases, here we cannot call tree().child() */ +template <class GridView, int order, std::size_t i> +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()) + { + return localView.tree().finiteElement(); + } +}; + +template<class Basis, class TargetSpace, class field_type=double, class GradientRT=double> +class SurfaceCosseratEnergy +: public LocalGeodesicFEStiffness<Basis,TargetSpace> +{ + // grid types + typedef typename Basis::GridView GridView; + typedef typename GridView::ctype ctype; + typedef typename Basis::LocalView::Tree::FiniteElement LocalFiniteElement; + 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; + + enum {dim=GridView::dimension}; + enum {dimworld=GridView::dimensionworld}; + enum {gridDim=GridView::dimension}; + static constexpr int boundaryDim = gridDim - 1; + + + /** \brief Compute the symmetric part of a matrix A, i.e. \f$ \frac 12 (A + A^T) \f$ */ + static Dune::FieldMatrix<field_type,dim,dim> sym(const Dune::FieldMatrix<field_type,dim,dim>& A) + { + Dune::FieldMatrix<field_type,dim,dim> result; + for (int i=0; i<dim; i++) + for (int j=0; j<dim; 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$ */ + static Dune::FieldMatrix<field_type,dim,dim> skew(const Dune::FieldMatrix<field_type,dim,dim>& A) + { + Dune::FieldMatrix<field_type,dim,dim> result; + for (int i=0; i<dim; i++) + for (int j=0; j<dim; j++) + result[i][j] = 0.5 * (A[i][j] - A[j][i]); + return result; + } + + /** \brief Compute the deviator of a matrix A */ + static Dune::FieldMatrix<field_type,dim,dim> dev(const Dune::FieldMatrix<field_type,dim,dim>& A) + { + Dune::FieldMatrix<field_type,dim,dim> result = A; + auto t = trace(A); + for (int i=0; i<dim; i++) + result[i][i] -= t / dim; + return result; + } + + /** \brief Return the trace of a matrix */ + template <class T, int N> + static T trace(const Dune::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 <int N> + static field_type traceSquared(const Dune::FieldMatrix<field_type,N,N>& A) + { + field_type trace = 0; + for (int i=0; i<N; i++) + trace += A[i][i]; + return trace*trace; + } + + template <class T, int N> + static T frobeniusProduct(const Dune::FieldMatrix<T,N,N>& A, const Dune::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]; + + return result; + } + + template <int N> + static auto dyadicProduct(const Dune::FieldVector<adouble,N>& A, const Dune::FieldVector<adouble,N>& B) + -> Dune::FieldMatrix<adouble,N,N> + { + Dune::FieldMatrix<adouble,N,N> result; + + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + result[i][j] = A[i]*B[j]; + + return result; + } + + template <int N> + static auto dyadicProduct(const Dune::FieldVector<double,N>& A, const Dune::FieldVector<double,N>& B) + -> Dune::FieldMatrix<double,N,N> + { + Dune::FieldMatrix<double,N,N> result; + + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + result[i][j] = A[i]*B[j]; + + return result; + } + + template <int N> + static auto dyadicProduct(const Dune::FieldVector<adouble,N>& A, const Dune::FieldVector<double,N>& B) + -> Dune::FieldMatrix<adouble,N,N> + { + Dune::FieldMatrix<adouble,N,N> result; + + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + result[i][j] = A[i]*B[j]; + + return result; + } + + template <class T, int N> + static Dune::FieldMatrix<T,N,N> transpose(const Dune::FieldMatrix<T,N,N>& A) + { + Dune::FieldMatrix<T,N,N> result; + + for (int i=0; i<N; i++) + for (int j=0; j<N; j++) + result[i][j] = A[j][i]; + + return result; + } + + + /** \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 RigidBodyMotion<field_type,dimworld>& value, + const Dune::FieldMatrix<field_type,7,boundaryDim>& derivative, + Tensor3<field_type,3,3,boundaryDim>& DR) + { + // 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<field_type,3 , 3, 4> dd_dq; + value.q.getFirstDerivativesOfDirectors(dd_dq); + + DR = field_type(0); + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + for (int k=0; k<boundaryDim; k++) + for (int l=0; l<4; l++) + DR[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 + */ + SurfaceCosseratEnergy(const Dune::ParameterTree& parameters, const std::vector<UnitVector<double,3> >& vertexNormals, const BoundaryPatch<GridView>* shellBoundary) + : shellBoundary_(shellBoundary), + vertexNormals_(vertexNormals) + { + // The shell thickness + thickness_ = parameters.template get<double>("thickness"); + + // Lame constants + mu_ = parameters.template get<double>("mu_cosserat"); + lambda_ = parameters.template get<double>("lambda_cosserat"); + + // 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"); + + // Shear correction factor + kappa_ = parameters.template get<double>("kappa"); + + 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 + */ + RT quadraticMembraneEnergy(const Dune::GFE::CosseratStrain<field_type,3,dim>& U) const + { + Dune::FieldMatrix<field_type,3,3> UMinus1 = U.matrix(); + for (int i=0; i<dim; i++) + UMinus1[i][i] -= 1; + + return mu_ * sym(UMinus1).frobenius_norm2() + + mu_c_ * skew(UMinus1).frobenius_norm2() + + (mu_*lambda_)/(2*mu_ + lambda_) * traceSquared(sym(UMinus1)); + } + + /** \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,dim>& 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); + + 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]); + + 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]); + + // elongational stretch energy + result += mu_*lambda_ / (2*mu_ + lambda_) * traceSquared(sym2x2); + + return result; + } + + /** \brief Energy for large-deformation problems (private communication by Patrizio Neff) + */ + RT nonquadraticMembraneEnergy(const Dune::GFE::CosseratStrain<field_type,3,dim>& 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(); + + return mu_ * sym(UMinus1).frobenius_norm2() + mu_c_ * 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,dim,dim-1>& 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); + + 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]); + + 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]); + + // 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; + } + + RT curvatureEnergy(const Tensor3<field_type,3,3,dim-1>& DR) const + { +#ifdef DONT_USE_CURL + return mu_ * std::pow(L_c_ * L_c_ * DR.frobenius_norm2(),q_/2.0); +#else + return mu_ * std::pow(L_c_ * L_c_ * curl(DR).frobenius_norm2(),q_/2.0); +#endif + } + + RT bendingEnergy(const Dune::FieldMatrix<field_type,dim,dim>& R, const Tensor3<field_type,3,3,dim-1>& 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<dim; j++) + for (int k=0; k<3; k++) + RT_DR3[i][j] += R[k][i] * DR[k][2][j]; + + return mu_ * sym(RT_DR3).frobenius_norm2() + + mu_c_ * skew(RT_DR3).frobenius_norm2() + + mu_*lambda_/(2*mu_+lambda_) * traceSquared(RT_DR3); + } + +RT energy(const typename Basis::LocalView& localView, + const std::vector<RigidBodyMotion<field_type,dim> >& localSolution) const +{ + // The element geometry + auto element = localView.element(); + auto geometry = element.geometry(); + + // The set of shape functions on this element + const auto& localFiniteElement = localView.tree().finiteElement(); + + //////////////////////////////////////////////////////////////////////////////////// + // Construct a linear (i.e., non-constant!) normal field on each element + //////////////////////////////////////////////////////////////////////////////////// + auto gridView = localView.globalBasis().gridView(); + + assert(vertexNormals_.size() == gridView.indexSet().size(gridDim)); + std::vector<UnitVector<double,3> > cornerNormals(element.subEntities(gridDim)); + for (size_t i=0; i<cornerNormals.size(); i++) + cornerNormals[i] = vertexNormals_[gridView.indexSet().subIndex(element,i,gridDim)]; + + typedef typename Dune::PQkLocalFiniteElementCache<DT, double, gridDim, 1> P1FiniteElementCache; + typedef typename P1FiniteElementCache::FiniteElementType P1LocalFiniteElement; + //it.type()? + P1FiniteElementCache p1FiniteElementCache; + const auto& p1LocalFiniteElement = p1FiniteElementCache.get(element.type()); + Dune::GFE::LocalProjectedFEFunction<gridDim, DT, P1LocalFiniteElement, UnitVector<double,3> > unitNormals(p1LocalFiniteElement, cornerNormals); + + //////////////////////////////////////////////////////////////////////////////////// + // Set up the local nonlinear finite element function + //////////////////////////////////////////////////////////////////////////////////// + typedef LocalGeodesicFEFunction<gridDim, DT, LocalFiniteElement, TargetSpace> LocalGFEFunctionType; + LocalGFEFunctionType localGeodesicFEFunction(localFiniteElement,localSolution); + + RT energy = 0; + + for (auto&& it : intersections(shellBoundary_->gridView(), element)) { + if (not shellBoundary_->contains(it)) + continue; + + auto quadOrder = (it.type().isSimplex()) ? localFiniteElement.localBasis().order() + : localFiniteElement.localBasis().order() * gridDim; + + 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());; + + const DT integrationElement = it.geometry().integrationElement(quad[pt].position()); + + // The value of the local function + RigidBodyMotion<field_type,dim> value = localGeodesicFEFunction.evaluate(quadPos); + + // The derivative of the local function + auto derivative = localGeodesicFEFunction.evaluateDerivative(quadPos,value); + + Dune::FieldMatrix<RT,7,2> derivative2D; + for (int i = 0; i < 7; i++) { + for (int j = 0; j < 2; j++) { + derivative2D[i][j] = derivative[i][j]; + } + } + ////////////////////////////////////////////////////////// + // The rotation and its derivative + // Note: we need it in matrix coordinates + ////////////////////////////////////////////////////////// + + Dune::FieldMatrix<field_type,dim,dim> R; + value.q.matrix(R); + auto RT = transpose(R); + + Tensor3<field_type,3,3,boundaryDim> DR; + computeDR(value, derivative2D, DR); + + ////////////////////////////////////////////////////////// + // Fundamental forms and curvature + ////////////////////////////////////////////////////////// + + // First fundamental form + Dune::FieldMatrix<double,3,3> 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 = it.geometry().jacobianTransposed(quad[pt].position()); + // auto jacobianTransposed = geometry.jacobianTransposed(quadPos); + + 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; i++) + aCovariant[i][j] = 0.0; + } + + aCovariant[2] = Dune::MatrixVector::crossProduct(aCovariant[0], aCovariant[1]); + aCovariant[2] /= aCovariant[2].two_norm(); + + auto aContravariant = aCovariant; + aContravariant.invert(); + + Dune::FieldMatrix<double,3,3> a(0); + for (int alpha=0; alpha<boundaryDim; alpha++) + a += 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 += sqrt(aScalar) * eps[alpha][beta] * dyadicProduct(aContravariant[alpha], aContravariant[beta]); + + // Second fundamental form + // The derivative of the normal field + auto normalDerivative = unitNormals.evaluateDerivative(quadPos); + + Dune::FieldMatrix<double,3,3> b(0); + for (int alpha=0; alpha<boundaryDim; alpha++) + { + Dune::FieldVector<double,3> vec; + for (int i=0; i<3; i++) + vec[i] = normalDerivative[i][alpha]; + b -= dyadicProduct(vec, aContravariant[alpha]); + } + + // Gauss curvature + auto K = b.determinant(); + + // Mean curvatue + auto H = 0.5 * trace(b); + + ////////////////////////////////////////////////////////// + // Strain tensors + ////////////////////////////////////////////////////////// + + // Elastic shell strain + Dune::FieldMatrix<field_type,3,3> Ee(0); + Dune::FieldMatrix<field_type,3,3> grad_s_m(0); + for (int alpha=0; alpha<boundaryDim; alpha++) + { + Dune::FieldVector<field_type,3> vec; + for (int i=0; i<3; i++) + vec[i] = derivative[i][alpha]; + grad_s_m += dyadicProduct(vec, aContravariant[alpha]); + } + + Ee = RT * grad_s_m - a; + + // Elastic shell bending-curvature strain + Dune::FieldMatrix<field_type,3,3> Ke(0); + for (int alpha=0; alpha<boundaryDim; alpha++) + { + Dune::FieldMatrix<field_type,3,3> tmp; + for (int i=0; i<3; i++) + for (int j=0; j<3; j++) + tmp[i][j] = DR[i][j][alpha]; + auto tmp2 = RT * tmp; + Ke += dyadicProduct(SkewMatrix<field_type,3>(tmp2).axial(), aContravariant[alpha]); + } + + ////////////////////////////////////////////////////////// + // Add the local energy density + ////////////////////////////////////////////////////////// + + // Add the membrane energy density + auto energyDensity = (thickness_ - K*Dune::Power<3>::eval(thickness_) / 12.0) * W_m(Ee); + energyDensity += (Dune::Power<3>::eval(thickness_) / 12.0 - K * Dune::Power<5>::eval(thickness_) / 80.0)*W_m(Ee*b + c*Ke); + energyDensity += Dune::Power<3>::eval(thickness_) / 6.0 * W_mixt(Ee, c*Ke*b - 2*H*c*Ke); + energyDensity += Dune::Power<5>::eval(thickness_) / 80.0 * W_mp( (Ee*b + c*Ke)*b); + + // Add the bending energy density + energyDensity += (thickness_ - K*Dune::Power<3>::eval(thickness_) / 12.0) * W_curv(Ke) + + (Dune::Power<3>::eval(thickness_) / 12.0 - K * Dune::Power<5>::eval(thickness_) / 80.0)*W_curv(Ke*b) + + Dune::Power<5>::eval(thickness_) / 80.0 * W_curv(Ke*b*b); + + // Add energy density + energy += quad[pt].weight() * integrationElement * energyDensity; + } + } + + return energy; +} + + RT W_m(const Dune::FieldMatrix<field_type,3,3>& S) const + { + return W_mixt(S,S); + } + + RT W_mixt(const Dune::FieldMatrix<field_type,3,3>& S, const Dune::FieldMatrix<field_type,3,3>& T) const + { + return mu_ * frobeniusProduct(sym(S), sym(T)) + + mu_c_ * frobeniusProduct(skew(S), skew(T)) + + lambda_ * mu_ / (lambda_ + 2*mu_) * trace(S) * trace(T); + } + + RT W_mp(const Dune::FieldMatrix<field_type,3,3>& S) const + { + return mu_ * sym(S).frobenius_norm2() + mu_c_ * skew(S).frobenius_norm2() + lambda_ * 0.5 * traceSquared(S); + } + + RT W_curv(const Dune::FieldMatrix<field_type,3,3>& S) const + { + return mu_ * L_c_ * L_c_ * (b1_ * dev(sym(S)).frobenius_norm2() + b2_ * skew(S).frobenius_norm2() + b3_ * traceSquared(S)); + } + +private: + /** \brief The Neumann boundary */ + const BoundaryPatch<GridView>* shellBoundary_; + + /** \brief The normal vectors at the grid vertices. This are used to compute the reference surface curvature. */ + std::vector<UnitVector<double,3> > vertexNormals_; + + /** \brief The shell thickness */ + double thickness_; + + /** \brief Lame constants */ + double mu_, lambda_; + + /** \brief Cosserat couple modulus, preferably 0 */ + double mu_c_; + + /** \brief Length scale parameter */ + double L_c_; + + /** \brief Curvature exponent */ + double q_; + + /** \brief Shear correction factor */ + double kappa_; + + /** \brief Curvature parameters */ + double b1_, b2_, b3_; + +}; + +#endif //#ifndef DUNE_GFE_SURFACECOSSERATENERGY_HH diff --git a/src/cantilever-dirichlet-values.py b/src/cantilever-dirichlet-values.py new file mode 100644 index 00000000..4de4795d --- /dev/null +++ b/src/cantilever-dirichlet-values.py @@ -0,0 +1,17 @@ +import math + +class DirichletValues: + def __init__(self, homotopyParameter): + self.homotopyParameter = homotopyParameter + + def deformation(self, x): + # Dirichlet b.c. simply clamp the shell in the reference configuration + out = x + + return out + + + def orientation(self, x): + rotation = [[1,0,0], [0, 1, 0], [0, 0, 1]] + return rotation + diff --git a/src/film-on-substrate.cc b/src/film-on-substrate.cc new file mode 100644 index 00000000..16dbc219 --- /dev/null +++ b/src/film-on-substrate.cc @@ -0,0 +1,482 @@ +#include <iostream> +#include <fstream> + +#include <config.h> + +// Includes for the ADOL-C automatic differentiation library +// Need to come before (almost) all others. +#include <adolc/adouble.h> +#include <adolc/drivers/drivers.h> // use of "Easy to Use" drivers +#include <adolc/taping.h> + +#include <dune/fufem/utilities/adolcnamespaceinjections.hh> + +#include <dune/common/bitsetvector.hh> +#include <dune/common/parametertree.hh> +#include <dune/common/parametertreeparser.hh> + +#include <dune/grid/uggrid.hh> +#include <dune/grid/utility/structuredgridfactory.hh> + +#include <dune/grid/io/file/gmshreader.hh> +#include <dune/grid/io/file/vtk.hh> + +#include <dune/elasticity/materials/exphenckyenergy.hh> +#include <dune/elasticity/materials/henckyenergy.hh> +#include <dune/elasticity/materials/mooneyrivlinenergy.hh> +#include <dune/elasticity/materials/neohookeenergy.hh> +#include <dune/elasticity/materials/neumannenergy.hh> +#include <dune/elasticity/materials/sumenergy.hh> +#include <dune/elasticity/materials/stvenantkirchhoffenergy.hh> + +#include <dune/functions/functionspacebases/interpolate.hh> +#include <dune/functions/functionspacebases/lagrangebasis.hh> +#include <dune/functions/gridfunctions/discreteglobalbasisfunction.hh> +#include <dune/functions/functionspacebases/powerbasis.hh> + +#include <dune/fufem/boundarypatch.hh> +#include <dune/fufem/functiontools/boundarydofs.hh> +#include <dune/fufem/functiontools/basisinterpolator.hh> +#include <dune/fufem/functionspacebases/dunefunctionsbasis.hh> +#include <dune/fufem/dunepython.hh> + +#include <dune/gfe/rigidbodymotion.hh> +#include <dune/gfe/localgeodesicfeadolcstiffness.hh> +#include <dune/gfe/cosseratvtkwriter.hh> +#include <dune/gfe/geodesicfeassembler.hh> +#include <dune/gfe/riemanniantrsolver.hh> +#include <dune/gfe/vertexnormals.hh> +#include <dune/gfe/surfacecosseratenergy.hh> +#include <dune/gfe/sumcosseratenergy.hh> + +#include <dune/solvers/solvers/iterativesolver.hh> +#include <dune/solvers/norms/energynorm.hh> + +// grid dimension +#ifndef WORLD_DIM +# define WORLD_DIM 3 +#endif +const int dim = WORLD_DIM; +const int order = 1; + +//differentiation method +typedef adouble ValueType; +typedef adouble GradientValueType; + +using namespace Dune; + +/** \brief A constant vector-valued function, for simple Neumann boundary values */ +struct NeumannFunction + : public Dune::VirtualFunction<FieldVector<double,dim>, FieldVector<double,dim> > +{ + NeumannFunction(const FieldVector<double,dim> values, + double homotopyParameter) + : values_(values), + homotopyParameter_(homotopyParameter) + {} + + void evaluate(const FieldVector<double, dim>& x, FieldVector<double,dim>& out) const + { + out = 0; + out.axpy(-homotopyParameter_, values_); + } + + FieldVector<double,dim> values_; + double homotopyParameter_; +}; + +struct VolumeLoad + : public Dune::VirtualFunction<FieldVector<double,dim>, FieldVector<double,3> > +{ + VolumeLoad(const FieldVector<double,3> values, + double homotopyParameter) + : values_(values), + homotopyParameter_(homotopyParameter) + {} + + void evaluate(const FieldVector<double, dim>& x, FieldVector<double,3>& out) const { + out = 0; + out.axpy(homotopyParameter_, values_); + } + + FieldVector<double,3> values_; + double homotopyParameter_; +}; + + +int main (int argc, char *argv[]) try +{ + // initialize MPI, finalize is done automatically on exit + Dune::MPIHelper& mpiHelper = MPIHelper::instance(argc, argv); + + // 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() + '/../../src/')" + << std::endl; + + typedef BlockVector<FieldVector<double,dim> > SolutionType; + typedef RigidBodyMotion<double,dim> TargetSpace; + typedef std::vector<TargetSpace> SolutionTypeCosserat; + + const int blocksize = TargetSpace::TangentVector::dimension; + + // parse data file + ParameterTree parameterSet; + + ParameterTreeParser::readINITree(argv[1], 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 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", ""); + + // /////////////////////////////////////// + // Create the grid + // /////////////////////////////////////// + typedef UGGrid<dim> GridType; + + std::shared_ptr<GridType> grid; + + FieldVector<double,dim> lower(0), upper(1); + + if (parameterSet.get<bool>("structuredGrid")) { + + lower = parameterSet.get<FieldVector<double,dim> >("lower"); + upper = parameterSet.get<FieldVector<double,dim> >("upper"); + + std::array<unsigned int,dim> 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"); + grid = std::shared_ptr<GridType>(GmshReader<GridType>::read(path + "/" + gridFile)); + } + + grid->globalRefine(numLevels-1); + + grid->loadBalance(); + + if (mpiHelper.rank()==0) + std::cout << "There are " << grid->leafGridView().comm().size() << " processes" << std::endl; + + typedef GridType::LeafGridView GridView; + GridView gridView = grid->leafGridView(); + + // FE basis spanning the FE space that we are working in + typedef Dune::Functions::LagrangeBasis<GridView, order> FEBasis; + FEBasis feBasis(gridView); + + // dune-fufem-style FE basis for the transition from dune-fufem to dune-functions + typedef DuneFunctionsBasis<FEBasis> FufemFEBasis; + FufemFEBasis fufemFEBasis(feBasis); + + // ///////////////////////////////////////// + // Read Dirichlet values + // ///////////////////////////////////////// + + BitSetVector<1> dirichletVertices(gridView.size(dim), false); + BitSetVector<1> neumannVertices(gridView.size(dim), false); + BitSetVector<1> surfaceShellVertices(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(")"); + PythonFunction<FieldVector<double,dim>, bool> pythonDirichletVertices(Python::evaluate(lambda)); + + // Same for the Neumann boundary + lambda = std::string("lambda x: (") + parameterSet.get<std::string>("neumannVerticesPredicate", "0") + std::string(")"); + PythonFunction<FieldVector<double,dim>, bool> pythonNeumannVertices(Python::evaluate(lambda)); + + // Same for the boundary that will get wrinkled + lambda = std::string("lambda x: (") + parameterSet.get<std::string>("surfaceShellVerticesPredicate", "0") + std::string(")"); + PythonFunction<FieldVector<double,dim>, bool> pythonSurfaceShellVertices(Python::evaluate(lambda)); + for (auto&& v : vertices(gridView)) + { + bool isDirichlet; + pythonDirichletVertices.evaluate(v.geometry().corner(0), isDirichlet); + dirichletVertices[indexSet.index(v)] = isDirichlet; + + bool isNeumann; + pythonNeumannVertices.evaluate(v.geometry().corner(0), isNeumann); + neumannVertices[indexSet.index(v)] = isNeumann; + + bool isSurfaceShell; + pythonSurfaceShellVertices.evaluate(v.geometry().corner(0), isSurfaceShell); + surfaceShellVertices[indexSet.index(v)] = isSurfaceShell; + } + + BoundaryPatch<GridView> dirichletBoundary(gridView, dirichletVertices); + BoundaryPatch<GridView> neumannBoundary(gridView, neumannVertices); + BoundaryPatch<GridView> surfaceShellBoundary(gridView, surfaceShellVertices); + + if (mpiHelper.rank()==0) + std::cout << "Neumann boundary has " << neumannBoundary.numFaces() << " faces\n"; + + + BitSetVector<1> dirichletNodes(feBasis.size(), false); + constructBoundaryDofs(dirichletBoundary,feBasis,dirichletNodes); + + BitSetVector<1> neumannNodes(feBasis.size(), false); + constructBoundaryDofs(neumannBoundary,feBasis,neumannNodes); + + BitSetVector<1> surfaceShellNodes(feBasis.size(), false); + constructBoundaryDofs(surfaceShellBoundary,feBasis,surfaceShellNodes); + + BitSetVector<blocksize> dirichletDofs(feBasis.size(), false); + for (size_t i=0; i<feBasis.size(); i++) + for (int j=0; j<blocksize; j++) { + if (dirichletNodes[i][0]) + dirichletDofs[i][j] = true; + } + for (size_t i=0; i<feBasis.size(); i++) + for (int j=3; j<blocksize; j++) { + if (not surfaceShellNodes[i][0]) + dirichletDofs[i][j] = true; + } + + // ////////////////////////// + // Initial iterate + // ////////////////////////// + + SolutionTypeCosserat x(feBasis.size()); + + //Solution in 3D, without the Cosserat directors on the boundary + BlockVector<FieldVector<double,3> > v; + + lambda = std::string("lambda x: (") + parameterSet.get<std::string>("initialDeformation") + std::string(")"); + PythonFunction<FieldVector<double,dim>, FieldVector<double,dim> > pythonInitialDeformation(Python::evaluate(lambda)); + ::Functions::interpolate(fufemFEBasis, v, pythonInitialDeformation); + + //Copy over the parts of the boundary + for (size_t i=0; i<x.size(); i++) { + x[i].r = v[i]; + } + + //////////////////////////////////////////////////////// + // Main homotopy loop + //////////////////////////////////////////////////////// + + using namespace Dune::Functions::BasisFactory; + auto powerBasis = makeBasis( + gridView, + power<dim>( + lagrange<order>(), + blockedInterleaved() + )); + + BlockVector<FieldVector<double,dim>> identity; + Dune::Functions::interpolate(powerBasis, identity, [](FieldVector<double,dim> x){ return x; }); + + BlockVector<FieldVector<double,dim> > displacement(v.size()); + for (int i = 0; i < v.size(); i++) { + displacement[i] = v[i]; + } + displacement -= identity; + + auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(feBasis, displacement); + auto localDisplacementFunction = localFunction(displacementFunction); + + auto neumannValues = parameterSet.get<FieldVector<double,dim> >("neumannValues"); + std::cout << "Neumann values: " << neumannValues << std::endl; + + // We need to subsample, because VTK cannot natively display real second-order functions + SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(order)); + vtkWriter.addVertexData(localDisplacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dim)); + vtkWriter.write(resultPath + "finite-strain_homotopy_" + parameterSet.get<std::string>("energy") + "_" + std::to_string(neumannValues[0]) + "_0"); + + for (int i=0; i<numHomotopySteps; i++) + { + double homotopyParameter = (i+1)*(1.0/numHomotopySteps); + + // //////////////////////////////////////////////////////////// + // Create an assembler for the energy functional + // //////////////////////////////////////////////////////////// + + const ParameterTree& materialParameters = parameterSet.sub("materialParameters"); + + std::shared_ptr<VolumeLoad> volumeLoad; + + std::shared_ptr<NeumannFunction> neumannFunction; + if (parameterSet.hasKey("neumannValues")) + { + neumannFunction = make_shared<NeumannFunction>(parameterSet.get<FieldVector<double,dim> >("neumannValues"), + homotopyParameter); + + std::cout << "Neumann values: " << parameterSet.get<FieldVector<double,dim> >("neumannValues") << std::endl; + } + + if (mpiHelper.rank() == 0) + { + std::cout << "Homotopy step: " << i << ", parameter: " << homotopyParameter << std::endl; + std::cout << "Material parameters:" << std::endl; + materialParameters.report(); + } + + // Assembler using ADOL-C + std::cout << "Selected energy is: " << parameterSet.get<std::string>("energy") << std::endl; + std::shared_ptr<LocalFEStiffness<GridView, + FEBasis::LocalView::Tree::FiniteElement, + std::vector<Dune::FieldVector<ValueType, dim>>, + std::vector<GradientValueType> > > elasticEnergy; + + if (parameterSet.get<std::string>("energy") == "stvenantkirchhoff") + elasticEnergy = std::make_shared<StVenantKirchhoffEnergy<GridView, + FEBasis::LocalView::Tree::FiniteElement, + ValueType, GradientValueType> >(materialParameters); + if (parameterSet.get<std::string>("energy") == "mooneyrivlin") + elasticEnergy = std::make_shared<MooneyRivlinEnergy<GridView, + FEBasis::LocalView::Tree::FiniteElement, + ValueType, GradientValueType> >(materialParameters); + + if (parameterSet.get<std::string>("energy") == "neohooke") + elasticEnergy = std::make_shared<NeoHookeEnergy<GridView, + FEBasis::LocalView::Tree::FiniteElement, + ValueType, GradientValueType> >(materialParameters); + + if (parameterSet.get<std::string>("energy") == "hencky") + elasticEnergy = std::make_shared<HenckyEnergy<GridView, + FEBasis::LocalView::Tree::FiniteElement, + ValueType, GradientValueType> >(materialParameters); + + if (parameterSet.get<std::string>("energy") == "exphencky") + elasticEnergy = std::make_shared<ExpHenckyEnergy<GridView, + FEBasis::LocalView::Tree::FiniteElement, + ValueType, GradientValueType> >(materialParameters); + + if(!elasticEnergy) + DUNE_THROW(Exception, "Error: Selected energy not available!"); + + auto neumannEnergy = std::make_shared<NeumannEnergy<GridView, + FEBasis::LocalView::Tree::FiniteElement, + ValueType, GradientValueType> >(&neumannBoundary,neumannFunction.get()); + + auto elasticAndNeumann = std::make_shared<SumEnergy<GridView, + FEBasis::LocalView::Tree::FiniteElement, + ValueType, GradientValueType>>(elasticEnergy, neumannEnergy); + + + using LocalEnergyBase = LocalGeodesicFEStiffness<FEBasis,RigidBodyMotion<adouble, dim> >; + + std::shared_ptr<LocalEnergyBase> surfaceCosseratEnergy; + std::vector<UnitVector<double,3> > vertexNormals(gridView.size(3)); + Dune::FieldVector<double,3> vertexNormalRaw = {0,0,1}; + for (int i = 0; i< vertexNormals.size(); i++) { + UnitVector vertexNormal(vertexNormalRaw); + vertexNormals[i] = vertexNormal; + } + surfaceCosseratEnergy = std::make_shared<SurfaceCosseratEnergy<FEBasis,RigidBodyMotion<adouble, dim>, adouble, adouble>>(materialParameters, std::move(vertexNormals), &surfaceShellBoundary); + + std::shared_ptr<LocalEnergyBase> totalEnergy; + totalEnergy = std::make_shared<GFE::SumCosseratEnergy<FEBasis,RigidBodyMotion<adouble, dim>, adouble, adouble>> (elasticAndNeumann, surfaceCosseratEnergy); + + + LocalGeodesicFEADOLCStiffness<FEBasis, + TargetSpace> localGFEADOLCStiffness(totalEnergy.get()); + + GeodesicFEAssembler<FEBasis,TargetSpace> assembler(gridView, &localGFEADOLCStiffness); + + // ///////////////////////////////////////////////// + // Create a Riemannian trust-region solver + // ///////////////////////////////////////////////// + + RiemannianTrustRegionSolver<FEBasis,TargetSpace> solver; + solver.setup(*grid, + &assembler, + x, + dirichletDofs, + tolerance, + maxTrustRegionSteps, + initialTrustRegionRadius, + multigridIterations, + mgTolerance, + mu, nu1, nu2, + baseIterations, + baseTolerance, + instrumented); + + solver.setScaling(parameterSet.get<FieldVector<double,6> >("trustRegionScaling")); + + //////////////////////////////////////////////////////// + // 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 + PythonFunction<FieldVector<double,dim>, FieldVector<double,3> > deformationDirichletValues(dirichletValuesPythonObject.get("deformation")); + PythonFunction<FieldVector<double,dim>, FieldMatrix<double,3,3> > orientationDirichletValues(dirichletValuesPythonObject.get("orientation")); + + std::vector<FieldVector<double,3> > ddV; + ::Functions::interpolate(fufemFEBasis, ddV, deformationDirichletValues, dirichletDofs); + + std::vector<FieldMatrix<double,3,3> > dOV; + ::Functions::interpolate(fufemFEBasis, dOV, orientationDirichletValues, dirichletDofs); + + for (size_t j=0; j<x.size(); j++) + if (dirichletNodes[j][0]) + { + x[j].r = ddV[j]; + x[j].q.set(dOV[j]); + } + + // ///////////////////////////////////////////////////// + // Solve! + // ///////////////////////////////////////////////////// + + solver.setInitialIterate(x); + solver.solve(); + + x = solver.getSol(); + + ///////////////////////////////// + // Output result + ///////////////////////////////// + for (size_t i=0; i<x.size(); i++) + v[i] = x[i].r; + + // Compute the displacement + BlockVector<FieldVector<double,dim> > displacement(v.size()); + for (int i = 0; i < v.size(); i++) { + displacement[i] = v[i]; + } + displacement -= identity; + + auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(feBasis, displacement); + auto localDisplacementFunction = localFunction(displacementFunction); + + // We need to subsample, because VTK cannot natively display real second-order functions + SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(order)); + vtkWriter.addVertexData(localDisplacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dim)); + vtkWriter.write(resultPath + "finite-strain_homotopy_" + parameterSet.get<std::string>("energy") + "_" + std::to_string(neumannValues[0]) + "_" + std::to_string(i+1)); + } +} catch (Exception& e) { + std::cout << e.what() << std::endl; +} \ No newline at end of file diff --git a/src/film-on-substrate.parset b/src/film-on-substrate.parset new file mode 100644 index 00000000..63b64fc6 --- /dev/null +++ b/src/film-on-substrate.parset @@ -0,0 +1,123 @@ +############################################# +# Grid parameters +############################################# + +structuredGrid = true + +# bounding box +lower = 0 0 0 +upper = 10 10 0.5 + +elements = 10 10 5 + +# Number of grid levels +numLevels = 2 + +############################################# +# Solver parameters +############################################# + +# Number of homotopy steps for the Dirichlet boundary conditions +numHomotopySteps = 1 + +# Tolerance of the trust region solver +tolerance = 1e-3 + +# Max number of steps of the trust region solver +maxTrustRegionSteps = 20 + +trustRegionScaling = 1 1 1 0.01 0.01 0.01 + +# Initial trust-region radius +initialTrustRegionRadius = 3.125 + +# Number of multigrid iterations per trust-region step +numIt = 400 + +# Number of presmoothing steps +nu1 = 3 + +# Number of postsmoothing steps +nu2 = 3 + +# Number of coarse grid corrections +mu = 1 + +# Number of base solver iterations +baseIt = 1 + +# Tolerance of the multigrid solver +mgTolerance = 1e-5 + +# Tolerance of the base grid solver +baseTolerance = 1e-8 + +# Measure convergence +instrumented = 0 + +############################ +# Material parameters +############################ + +energy = stvenantkirchhoff + +## For the Wriggers L-shape example +[materialParameters] + +# shell thickness +thickness = 0.6 + +# Lame parameters +# corresponds to E = 71240 N/mm^2, nu=0.31 +# However, we use units N/m^2 +mu = 2.7191e+4 +lambda = 4.4364e+4 + +#Lame parameters for the cosserat material +mu_cosserat = 2.7191e+4 +lambda_cosserat = 4.4364e+4 + +# Cosserat couple modulus +mu_c = 0 + +# Length scale parameter +L_c = 0.6 + +# Curvature exponent +q = 2 + +# Shear correction factor +kappa = 1 + +b1 = 1 +b2 = 1 +b3 = 1 + +[] + +############################################# +# Boundary values +############################################# + +problem = cantilever + +### Python predicate specifying all Dirichlet grid vertices +# x is the vertex coordinate +dirichletVerticesPredicate = "x[0] < 0.01" + +### Python predicate specifying all Neumann grid vertices +# x is the vertex coordinate +surfaceShellVerticesPredicate = "x[2] > 0.49" + +### Python predicate specifying all Neumann grid vertices +# x is the vertex coordinate +neumannVerticesPredicate = "x[0] > 9.99" + +## Neumann values +neumannValues = 1e5 0 0 + +# Initial deformation +initialDeformation = "x" + +#startFromFile = yes +#initialIterateFilename = initial_iterate.vtu -- GitLab