From d712478aacf93397e93d5691577280ab32fd0ef1 Mon Sep 17 00:00:00 2001 From: Oliver Sander <oliver.sander@tu-dresden.de> Date: Thu, 9 Sep 2021 10:25:46 +0200 Subject: [PATCH] Construct compatible deformation gradient from incompatible one By an L^2-projection onto the space of deformation gradients. --- dune/elasticity/materials/pdistanceenergy.hh | 118 ++++++++ src/quasiconvexity-test-micromorphic.cc | 303 +++++++++++++++++++ 2 files changed, 421 insertions(+) create mode 100644 dune/elasticity/materials/pdistanceenergy.hh diff --git a/dune/elasticity/materials/pdistanceenergy.hh b/dune/elasticity/materials/pdistanceenergy.hh new file mode 100644 index 0000000..3a0ac65 --- /dev/null +++ b/dune/elasticity/materials/pdistanceenergy.hh @@ -0,0 +1,118 @@ +// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vi: set et ts=4 sw=2 sts=2: +#ifndef DUNE_ELASTICITY_MATERIALS_PDISTANCEENERGY_HH +#define DUNE_ELASTICITY_MATERIALS_PDISTANCEENERGY_HH + +#include <dune/common/fmatrix.hh> +#include <dune/common/transpose.hh> + +#include <dune/geometry/quadraturerules.hh> + +#include <dune/elasticity/assemblers/localenergy.hh> + +namespace Dune::Elasticity { + +template<class LocalView, class DataFunction, class field_type=double> +class PDistanceEnergy + : public Elasticity::LocalEnergy<LocalView,field_type> +{ + using GridView = typename LocalView::GridView; + using DT = typename GridView::Grid::ctype; + + enum {gridDim=GridView::dimension}; + +public: + + /** \brief Constructor with a data term + */ + PDistanceEnergy(const DataFunction& dataFunction, + FieldMatrix<double,2,2> F0, + double k) + : dataFunction_(dataFunction), + F0_(F0), + k_(k) + {} + + /** \brief Virtual destructor */ + virtual ~PDistanceEnergy() + {} + + /** \brief Assemble the energy for a single element */ + field_type energy(const LocalView& localView, + const std::vector<field_type>& localConfiguration) const override; + + /** \brief Set scaling factor */ + void setK(double k) + { + k_ = k; + } + +protected: + const DataFunction dataFunction_; + + FieldMatrix<double,gridDim,gridDim> F0_; + + /** \brief Scaling factor */ + double k_; +}; + +template <class LocalView, class DataFunction, class field_type> +field_type +PDistanceEnergy<LocalView, DataFunction, field_type>:: +energy(const LocalView& localView, + const std::vector<field_type>& localConfiguration) const +{ + // powerBasis: grab the finite element of the first child + const auto& localFiniteElement = localView.tree().child(0).finiteElement(); + const auto& element = localView.element(); + + auto localDataFunction = localFunction(dataFunction_); + localDataFunction.bind(element); + + field_type energy = 0; + + // store gradients of shape functions and base functions + using FiniteElement = typename LocalView::Tree::ChildType::FiniteElement; + using Jacobian = typename FiniteElement::Traits::LocalBasisType::Traits::JacobianType; + + std::vector<Jacobian> jacobians(localFiniteElement.size()); + + int quadOrder = (element.type().isSimplex()) ? localFiniteElement.localBasis().order() + : localFiniteElement.localBasis().order() * gridDim; + + const auto& quad = QuadratureRules<DT, gridDim>::rule(element.type(), quadOrder); + + for (const auto& qp : quad) + { + const DT integrationElement = element.geometry().integrationElement(qp.position()); + const auto geometryJacobianIT = element.geometry().jacobianInverseTransposed(qp.position()); + + // Get gradients of shape functions + localFiniteElement.localBasis().evaluateJacobian(qp.position(), jacobians); + + // compute gradients of base functions + for (size_t i=0; i<jacobians.size(); ++i) + jacobians[i] = jacobians[i] * transpose(geometryJacobianIT); + + // Deformation gradient + FieldMatrix<field_type,gridDim,gridDim> deformationGradient(0); + for (size_t i=0; i<jacobians.size(); i++) + for (int j=0; j<gridDim; j++) + deformationGradient[j].axpy(localConfiguration[ localView.tree().child(j).localIndex(i) ], jacobians[i][0]); + + deformationGradient += F0_; + + FieldMatrix<double,2,2> P = localDataFunction(qp.position()); + + auto diff = deformationGradient - P; + + // Integrate the micromorphic regularization + energy += qp.weight() * integrationElement * k_ * diff.frobenius_norm2(); + } + + return energy; +} + +} // namespace Dune::Elasticity + +#endif //#ifndef DUNE_ELASTICITY_MATERIALS_PDISTANCEENERGY_HH diff --git a/src/quasiconvexity-test-micromorphic.cc b/src/quasiconvexity-test-micromorphic.cc index 98dbaf1..f4df370 100644 --- a/src/quasiconvexity-test-micromorphic.cc +++ b/src/quasiconvexity-test-micromorphic.cc @@ -38,6 +38,7 @@ #include <dune/elasticity/assemblers/localadolcstiffness.hh> #include <dune/elasticity/assemblers/feassembler.hh> #include <dune/elasticity/materials/micromorphicallyrelaxedenergy.hh> +#include <dune/elasticity/materials/pdistanceenergy.hh> #include <dune/elasticity/materials/quasiconvexitytestdensities.hh> // grid dimension @@ -108,6 +109,302 @@ void writeDeformationGradientInfo(const Basis& basis, const Vector& deformation, vtkWriter.write(filename); } +template <class Basis, class Vector> +void writeDisplacement(const Basis& basis, const Vector& x, const FieldMatrix<double,2,2>& F0, std::string filename) +{ + using GridView = typename Basis::GridView; + + // Add the underlying affine displacement. + Vector affineDisplacement; + Dune::Functions::interpolate(basis, affineDisplacement, [&F0](FieldVector<double,dim> pos) + { + return FieldVector<double,2>{ (F0[0][0]-1)*pos[0] + F0[0][1]*pos[1], F0[1][0]*pos[0] + (F0[1][1]-1)*pos[1] }; + }); + + Vector displacement = x; + displacement += affineDisplacement; + + // Compute the determinant per element, for better understanding of the solution + Functions::LagrangeBasis<GridView,0> p0Basis(basis.gridView()); + std::vector<double> deformationDeterminant(p0Basis.size()); + // K = 0.5 F.frobenius_norm2() / F.determinant(); + std::vector<double> K(p0Basis.size()); + + auto localView = basis.localView(); + auto localP0View = p0Basis.localView(); + + for (const auto& element : elements(basis.gridView())) + { + localView.bind(element); + localP0View.bind(element); + + const auto& localFiniteElement = localView.tree().child(0).finiteElement(); + + Vector localConfiguration(localView.size()); + + for (size_t i=0; i<localConfiguration.size(); i++) + localConfiguration[i] = x[localView.index(i)[0]]; + + // store gradients of shape functions and base functions + std::vector<Dune::FieldMatrix<double,1,dim> > jacobians(localFiniteElement.size()); + + auto p = referenceElement(element).position(0,0); // Element center in local coordinates + + const auto geometryJacobianIT = element.geometry().jacobianInverseTransposed(p); + + // get gradients of shape functions + localFiniteElement.localBasis().evaluateJacobian(p, jacobians); + + // compute Jacobians of basis functions + for (size_t i=0; i<jacobians.size(); ++i) + jacobians[i] = jacobians[i] * transpose(geometryJacobianIT); + + FieldMatrix<double,dim,dim> derivative(0); + for (size_t i=0; i<jacobians.size(); i++) + for (int j=0; j<dim; j++) + derivative[j].axpy(localConfiguration[i][j], jacobians[i][0]); + + derivative += F0; + + deformationDeterminant[localP0View.index(0)] = derivative.determinant(); + K[localP0View.index(0)] = 0.5 * derivative.frobenius_norm2() / derivative.determinant(); + } + + std::cout << "K range: " << (*std::min_element(K.begin(), K.end())) + << ", " << (*std::max_element(K.begin(), K.end())) << std::endl; + + auto deformationDeterminantFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,1>>(p0Basis, deformationDeterminant); + auto KFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,1>>(p0Basis, K); + + // Actually write the displacement function + auto displacementFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim>>(basis, displacement); + + VTKWriter<GridView> vtkWriter(basis.gridView()); + vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::vector, dim)); + vtkWriter.addCellData(deformationDeterminantFunction, + VTK::FieldInfo("determinant", VTK::FieldInfo::Type::scalar, 1)); + vtkWriter.addCellData(KFunction, + VTK::FieldInfo("K", VTK::FieldInfo::Type::scalar, 1)); + vtkWriter.write(filename); +} + + +template <typename Grid, typename IncompatibleDeformationGradientFunction> +void constructDeformation(const std::shared_ptr<Grid>& grid, + const ParameterTree& parameterSet, + const IncompatibleDeformationGradientFunction& incompatibleDeformationGradient) +{ + using Vector = BlockVector<FieldVector<double,dim> >; + + // 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"); + std::string resultPath = parameterSet.get("resultPath", ""); + + ///////////////////////////////////////// + // Create the grid + ///////////////////////////////////////// + +#if HAVE_DUNE_PARMG + using GridView = typename Grid::LevelGridView; + GridView gridView = grid->levelGridView(0); +#else + using GridView = typename Grid::LeafGridView; + GridView gridView = grid->leafGridView(); +#endif + + // FE basis spanning the FE space that we are working in + using namespace Dune::Functions::BasisFactory; + auto feBasis = makeBasis( + gridView, + power<dim>( + lagrange<order>(), + blockedInterleaved() + )); + using FEBasis = decltype(feBasis); + + std::cout << "Basis has " << feBasis.size() << " degrees of freedom" << std::endl; + + /////////////////////////////////////////// + // Set Dirichlet values + /////////////////////////////////////////// + + BoundaryPatch<GridView> dirichletBoundary(gridView, true); + + BitSetVector<dim> dirichletDofs(feBasis.size(), false); + constructBoundaryDofs(dirichletBoundary,feBasis,dirichletDofs); + + //////////////////////////// + // Initial iterate + //////////////////////////// + + Vector x(feBasis.size()); + + // Set the underlying affine deformation + auto x0 = parameterSet.get<double>("x0"); + + FieldMatrix<double,2,2> F0 = { {sqrt(x0), 0}, + { 0, 1.0 / sqrt(x0)} }; + + // The actual unknown is a perturbation of the affine transformation + // Start here with the zero function + std::fill(x.begin(), x.end(), FieldVector<double,2>(0)); + + // Output initial iterate + writeDisplacement(feBasis, x, F0, resultPath + "quasiconvexity-test-micromorphic-deformation-initial"); + + ////////////////////////////////////////////////////////////// + // Create an assembler for the energy functional + ////////////////////////////////////////////////////////////// + + const ParameterTree& materialParameters = parameterSet.sub("materialParameters"); + + std::cout << "Material parameters:" << std::endl; + materialParameters.report(); + + // Assembler using ADOL-C + auto energy = std::make_shared<Elasticity::PDistanceEnergy<typename FEBasis::LocalView, + IncompatibleDeformationGradientFunction, + adouble> >(incompatibleDeformationGradient, + F0, + parameterSet.get<double>("k")); + + auto localADOLCStiffness = std::make_shared<Elasticity::LocalADOLCStiffness<typename FEBasis::LocalView> >(energy); + + auto assembler = std::make_shared<Elasticity::FEAssembler<FEBasis,Vector> >(feBasis, localADOLCStiffness); + + /////////////////////////////////////////////////// + // Create a trust-region solver + /////////////////////////////////////////////////// + + using Matrix = BCRSMatrix<FieldMatrix<double, dim, dim> >; + +#ifdef HAVE_IPOPT + // First create an IPOpt base solver + auto baseSolver = std::make_shared<QuadraticIPOptSolver<Matrix,Vector> >( + baseTolerance, + baseIterations, + NumProc::QUIET, + "mumps"); +#else + // First create a Gauss-seidel base solver + TrustRegionGSStep<Matrix, Vector>* baseSolverStep = new TrustRegionGSStep<Matrix, Vector>; + + // Hack: the two-norm may not scale all that well, but it is fast! + TwoNorm<CorrectionType>* baseNorm = new TwoNorm<Vector>; + + auto baseSolver = std::make_shared<::LoopSolver<Vector>>(baseSolverStep, + baseIterations, + baseTolerance, + baseNorm, + Solver::QUIET); +#endif + // Make pre and postsmoothers + auto presmoother = std::make_shared< TrustRegionGSStep<Matrix, Vector> >(); + auto postsmoother = std::make_shared< TrustRegionGSStep<Matrix, Vector> >(); + + auto mmgStep = std::make_shared<MonotoneMGStep<Matrix, Vector> >(); + + mmgStep->setVerbosity(NumProc::FULL); + mmgStep->setMGType(mu, nu1, nu2); + mmgStep->ignoreNodes_ = &dirichletDofs; + mmgStep->setBaseSolver(baseSolver); + mmgStep->setSmoother(presmoother, postsmoother); + mmgStep->setObstacleRestrictor(std::make_shared<MandelObstacleRestrictor<Vector> >()); + + ////////////////////////////////////// + // Create the transfer operators + ////////////////////////////////////// + + using TransferOperatorType = typename TruncatedCompressedMGTransfer<Vector>::TransferOperatorType; + std::vector<std::shared_ptr<TruncatedCompressedMGTransfer<Vector>>> transferOperators(numLevels-1); + + // For the restriction operators: FE bases on all levels + auto dummyLevelBasis = makeBasis( + grid->levelGridView(0), + power<dim>( + lagrange<order>(), + blockedInterleaved() + )); + + using LevelBasis = decltype(dummyLevelBasis); + + // Construct the actual level bases + std::vector<std::shared_ptr<LevelBasis> > levelBases(numLevels); + for (int j=0; j<numLevels; j++) + { + levelBases[j] = std::make_shared<LevelBasis>(makeBasis( + grid->levelGridView(j), + power<dim>( + lagrange<order>(), + blockedInterleaved() + ))); + } + + for (int j=0; j<numLevels-1; j++) + { + auto transferMatrix = std::make_shared<TransferOperatorType>(); + assembleGlobalBasisTransferMatrix(*transferMatrix, *levelBases[j], *levelBases[j+1]); + + // Construct the local multigrid transfer matrix + transferOperators[j] = std::make_shared<TruncatedCompressedMGTransfer<Vector> >(); + transferOperators[j]->setMatrix(transferMatrix); + } + + // What does this block do? + { + auto transferMatrix = std::make_shared<TransferOperatorType>(); + assembleGlobalBasisTransferMatrix(*transferMatrix, *levelBases.back(), *levelBases.back()); + + // Construct the local multigrid transfer matrix + transferOperators.push_back(std::make_shared<TruncatedCompressedMGTransfer<Vector> >()); + transferOperators.back()->setMatrix(transferMatrix); + } + + mmgStep->setTransferOperators(transferOperators); + + TrustRegionSolver<FEBasis,Vector> solver; + solver.setup(*grid, + assembler, + x, + dirichletDofs, + tolerance, + maxTrustRegionSteps, + initialTrustRegionRadius, + mmgStep, + mgTolerance, + multigridIterations); + + + /////////////////////////////////////////////////////// + // Solve! + // ///////////////////////////////////////////////////// + + std::cout << "2-Distance to incompatible strain: " << std::sqrt(assembler->computeEnergy(x)) << std::endl; + + solver.setInitialIterate(x); + solver.solve(); + + x = solver.getSol(); + + ///////////////////////////////// + // Output result + ///////////////////////////////// + + std::cout << "2-Distance to incompatible strain: " << std::sqrt(assembler->computeEnergy(x)) << std::endl; + + writeDisplacement(feBasis, x, F0, resultPath + "quasiconvexity-test-micromorphic-deformation"); +} + int main (int argc, char *argv[]) try { // initialize MPI, finalize is done automatically on exit @@ -404,6 +701,12 @@ int main (int argc, char *argv[]) try writeDeformationGradientInfo(feBasis, x, F0, resultPath + "quasiconvexity-test-micromorphic-result_" + std::to_string(L_c) + "_" + std::to_string(x0)); + ////////////////////////////////////////////////////////////////// + // Construct a deformation field that has a deformation gradient + // close to the one we just constructed. + ////////////////////////////////////////////////////////////////// + auto incompatibleDeformationGradient = Functions::makeDiscreteGlobalBasisFunction<FieldMatrix<double,2,2>>(feBasis, x); + constructDeformation(grid, parameterSet, incompatibleDeformationGradient); } catch (Exception& e) { std::cout << e.what() << std::endl; } -- GitLab