From bed86e1a15dbf59a8b33e35f555eac46c9bfe0bf Mon Sep 17 00:00:00 2001 From: Oliver Sander <oliver.sander@tu-dresden.de> Date: Thu, 10 Dec 2020 17:34:04 +0100 Subject: [PATCH] Complete the implementation of the static relaxed micromorphic model --- problems/relaxed-micromorphic-torsion.parset | 77 +++ src/relaxed-micromorphic-continuum.cc | 674 ++++++++----------- 2 files changed, 343 insertions(+), 408 deletions(-) create mode 100644 problems/relaxed-micromorphic-torsion.parset diff --git a/problems/relaxed-micromorphic-torsion.parset b/problems/relaxed-micromorphic-torsion.parset new file mode 100644 index 0000000..1147cf7 --- /dev/null +++ b/problems/relaxed-micromorphic-torsion.parset @@ -0,0 +1,77 @@ +############################################# +# Grid parameters +############################################# + +structuredGrid = simplex +lower = 0 -0.5 -0.5 +upper = 5 0.5 0.5 +elements = 10 2 2 + +# Number of grid levels +numLevels = 2 + +############################################# +# Solver parameters +############################################# + +timeIntegration = static + +# Currently, only 'direct' is supported. +solverType = direct + +# Tolerance of the trust region solver +#tolerance = 1e-6 + +# Number of multigrid iterations per trust-region step +#numIt = 1000 + +# 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 = 100 + +# Tolerance of the multigrid solver +#mgTolerance = 1e-7 + +# Tolerance of the base grid solver +#baseTolerance = 1e-8 + +############################ +# Material parameters +############################ + +energy = stvenantkirchhoff + +## For the Wriggers L-shape example +[materialParameters] + +lambda = 1e6 +lambda_h = 1e6 +mu_c = 0 +mu_e = 1e6 +mu_h = 1e6 + +alpha = 1e3 1e3 1e3 + +[] + +############################################# +# Boundary values +############################################# + +### Python predicate specifying all Dirichlet grid vertices +# x is the vertex coordinate +dirichletVerticesPredicate = "x[0] < 0.0001 or x[0] > 4.9999" + +### Neumann values, if needed +#neumannValues = 0 5e4 0 + +# Initial iterate (can be a function of x) +initialIterate = "[[x[0]*1.2, 0, 0], [[0,0,0], [0,0,0], [0,0,0]]]" diff --git a/src/relaxed-micromorphic-continuum.cc b/src/relaxed-micromorphic-continuum.cc index c4d10c6..dbca65e 100644 --- a/src/relaxed-micromorphic-continuum.cc +++ b/src/relaxed-micromorphic-continuum.cc @@ -3,6 +3,7 @@ #include <dune/common/bitsetvector.hh> #include <dune/common/parametertree.hh> #include <dune/common/parametertreeparser.hh> +#include <dune/common/transpose.hh> #include <dune/geometry/quadraturerules.hh> @@ -52,414 +53,258 @@ using namespace Dune; - -class RelaxedMicromorphicContinuumAssembler +/** \brief Return the trace of a matrix + if we know that only one row of A has nonzeroes */ +template <class T, int n> +static T trace(const FieldVector<T,n>& Arow, int row) { -#if 0 - // The Lame constants - double mu_; - double lambda_; - - // The penalty parameter of the DG discretization - double eta_; -#endif - // Neumann boundary force density - FieldVector<double,2> neumannValue_; - -public: - - RelaxedMicromorphicContinuumAssembler(const ParameterTree& parameters, - const FieldVector<double,2> neumannValue) - : neumannValue_(neumannValue) - { -#if 0 - mu_ = parameters.get<double>("mu"); - lambda_ = parameters.get<double>("lambda"); - eta_ = parameters.get<double>("eta"); -#endif - } - - // Compute the stiffness matrix for a single element - // This local assembler assumes a nonconforming vector-valued basis, - // and assembles a DG-discretized problem. - template <class LocalView, class Matrix> - void assembleElementMatrix(const LocalView& localView, Matrix& elementMatrix) const - { - using Element = typename LocalView::Element; - auto element = localView.element(); - constexpr int dim = Element::dimension; - auto geometry = element.geometry(); -#if 0 - using Range = typename LocalView::Tree::FiniteElement::Traits::LocalBasisType::Traits::RangeType; - using Gradient = typename LocalView::Tree::FiniteElement::Traits::LocalBasisType::Traits::JacobianType; - - // Get set of shape functions for this element - const auto& localFiniteElement = localView.tree().finiteElement(); - - // Set all matrix entries to zero - elementMatrix.setSize(localFiniteElement.size(),localFiniteElement.size()); - elementMatrix = 0; // Fill the entire matrix with zeros - - // Get a quadrature rule - int order = 2*(dim*localFiniteElement.localBasis().order()-1); - const auto& quadRule = QuadratureRules<double, dim>::rule(element.type(), order); + return Arow[row]; +} - // Loop over all quadrature points - for (const auto& qp : quadRule) +/** \brief Compute the symmetric part of a matrix A, i.e. \f$ \frac 12 (A + A^T) \f$ + if we know that only one row of A has nonzeroes */ +template <class T, int n> +static FieldMatrix<T,n,n> sym(const FieldVector<T,n>& Arow, int row) +{ + FieldMatrix<T,n,n> result; + for (int i=0; i<n; i++) + for (int j=0; j<n; j++) { - // Position of the current quadrature point in the reference element - const auto quadPos = qp.position(); - - // The transposed inverse Jacobian of the map from the reference element to the element - const auto jacobianInverseTransposed = geometry.jacobianInverseTransposed(quadPos); - - /////////////////////////////////////////////////////////////////////////// - // Shape functions - [assume H(div) elements] - /////////////////////////////////////////////////////////////////////////// - - std::vector<Range> values(localFiniteElement.size()); - localFiniteElement.localBasis().evaluateFunction(quadPos, values); - - // Gradients of the shape functions on the reference element - std::vector<Gradient> referenceGradients(localFiniteElement.size()); - localFiniteElement.localBasis().evaluateJacobian(quadPos, referenceGradients); - - // Compute the shape function gradients on the grid element - std::vector<Gradient> gradients(localFiniteElement.size()); - for (size_t i=0; i<gradients.size(); i++) - gradients[i] = transformToElementGradient<Gradient,Gradient>(referenceGradients[i], jacobianInverseTransposed); - - // geometric weight - auto factor = qp.weight() * geometry.integrationElement(quadPos); - - /////////////////////////////////// - // Compute the matrix entries - /////////////////////////////////// - - for (size_t i=0; i<localView.size(); i++) - { - for (size_t j=0; j<localView.size(); j++) - { - // Elastic strains - FieldMatrix<double,dim,dim> strainU, strainV; - for (int ii=0; ii<dim; ii++) - for (int jj=0; jj<dim; jj++) - { - strainU[ii][jj] = 0.5 * (gradients[i][ii][jj] + gradients[i][jj][ii]); - strainV[ii][jj] = 0.5 * (gradients[j][ii][jj] + gradients[j][jj][ii]); - } - - double traceU = 0; - double traceV = 0; - for (int ii=0; ii<dim; ii++) - { - traceU += strainU[ii][ii]; - traceV += strainV[ii][ii]; - } - - double energyDensity = 0; - for (int ii=0; ii<dim; ii++) - for (int jj=0; jj<dim; jj++) - energyDensity += 2 * mu_ * strainU[ii][jj] * strainV[ii][jj]; - - energyDensity += lambda_ * traceU * traceV; - - auto matrixI = localView.tree().localIndex(i); - auto matrixJ = localView.tree().localIndex(j); - elementMatrix[matrixI][matrixJ] += energyDensity*factor; - } - } + T Aij = (i==row) ? Arow[j] : 0; + T Aji = (j==row) ? Arow[i] : 0; + result[i][j] = 0.5 * (Aij + Aji); } -#endif - } - - template <class Intersection, class LocalView, class Matrix> - void assembleIntersectionMatrix(const Intersection& intersection, - const LocalView& localViewInside, - const LocalView& localViewOutside, - Matrix& matInIn, Matrix& matInOut, - Matrix& matOutIn, Matrix& matOutOut) const - { - // Do nothing -- we are not using a DG discretization yet. - } - - template <class Intersection, class LocalView, class Vector> - void assembleSurfaceLoad(const Intersection& intersection, - const LocalView& localView, - Vector& elementRhs) const - { - using Element = typename LocalView::Element; - constexpr int dim = Element::dimension; - - using Range = typename LocalView::Tree::FiniteElement::Traits::LocalBasisType::Traits::RangeType; - - auto geometry = intersection.geometry(); - auto geometryInInside = intersection.geometryInInside(); - - // Get set of shape functions for this element - const auto& localFiniteElement = localView.tree().finiteElement(); - - // Set all vector entries to zero - elementRhs.resize(localFiniteElement.size()); - elementRhs = 0; - - // Get a quadrature rule - int order = 2*(dim*localFiniteElement.localBasis().order()-1); - const auto& quadRule = QuadratureRules<double, Intersection::mydimension>::rule(intersection.type(), order); + return result; +} - // Loop over all quadrature points - for (const auto& qp : quadRule) +/** \brief Compute the antisymmetric part of a matrix A, i.e. \f$ \frac 12 (A - A^T) \f$ + if we know that only one row of A has nonzeroes */ +template <class T, int n> +static FieldMatrix<T,n,n> skew(const FieldVector<T,n>& Arow, int row) +{ + FieldMatrix<T,n,n> result; + for (int i=0; i<n; i++) + for (int j=0; j<n; j++) { - // Position of the current quadrature point in the inside element - const auto quadPosIn = geometryInInside.global(qp.position()); - - /////////////////////////////////////////////////////////////////////////// - // Shape functions - [assume H(div) elements] - /////////////////////////////////////////////////////////////////////////// - - std::vector<Range> valuesIn(localFiniteElement.size()); - localFiniteElement.localBasis().evaluateFunction(quadPosIn, valuesIn); - - // geometric weight - auto factor = qp.weight() * geometry.integrationElement(qp.position()); - - /////////////////////////////////// - // Compute the matrix entries - /////////////////////////////////// - for (size_t i=0; i<localView.size(); i++) - { - auto matrixI = localView.tree().localIndex(i); - elementRhs[matrixI] += valuesIn[i] * neumannValue_ * factor; - } - + T Aij = (i==row) ? Arow[j] : 0; + T Aji = (j==row) ? Arow[i] : 0; + result[i][j] = 0.5 * (Aij - Aji); } - } - -}; + return result; +} -// Get the occupation pattern of the stiffness matrix -template <class Basis, class MatrixType> -void setOccupationPattern(const Basis& basis, MatrixType& matrix) +/** \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) { - // MatrixIndexSets store the occupation pattern of a sparse matrix. - // They are not particularly efficient, but simple to use. - std::array<std::array<MatrixIndexSet, 2>, 2> nb; - - // Set sizes of the 2x2 submatrices - for (size_t i=0; i<2; i++) - for (size_t j=0; j<2; j++) - nb[i][j].resize(basis.size({i}), basis.size({j})); - - // A view on the FE basis on a single element - auto localView = basis.localView(); - - // Loop over all leaf elements - for(const auto& element : elements(basis.gridView())) - { - // Bind the local 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++) { - - // Global index of the i-th local degree of freedom of the current element - auto row = localView.index(i); - - for (size_t j=0; j<localView.size(); j++ ) { + FieldMatrix<T,n,n> result = A; + T trace(0); + for (int i=0; i<n; i++) + trace += A[i][i]; + + for (int i=0; i<n; i++) + result[i][i] -= trace / n; + return result; +} - // Global index of the j-th local degree of freedom of the current element - auto col = localView.index(j); +/** \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); - nb[row[0]][col[0]].add(row[1],col[1]); - } - } - } + for (int i=0; i<n; i++) + for (int j=0; j<n; j++) + result += A[i][j] * B[i][j]; - // Give the matrix the occupation pattern we want. - using namespace Indices; - nb[0][0].exportIdx(matrix[_0][_0]); - nb[0][1].exportIdx(matrix[_0][_1]); - nb[1][0].exportIdx(matrix[_1][_0]); - nb[1][1].exportIdx(matrix[_1][_1]); + return result; } -template<class Matrix, class MultiIndex> -decltype(auto) matrixEntry( - Matrix& matrix, const MultiIndex& row, const MultiIndex& col) +// \begin{equation*} +// (\Curl P)_i +// \colonequals +// \Big( \frac{\partial P_3}{\partial x_2} - \frac{\partial P_2}{\partial x_3}, \quad +// \frac{\partial P_1}{\partial x_3} - \frac{\partial P_3}{\partial x_1}, \quad +// \frac{\partial P_2}{\partial x_1} - \frac{\partial P_1}{\partial x_2} \Big). +// \end{equation*} +template <class T, int n> +static FieldVector<T,n> curl(const FieldMatrix<T,n,n>& dP) { - using namespace Indices; - if ((row[0]==0) and (col[0]==0)) - return matrix[_0][_0][row[1]][col[1]][row[2]][col[2]]; - if ((row[0]==0) and (col[0]==1)) - return matrix[_0][_1][row[1]][col[1]][row[2]][0]; - if ((row[0]==1) and (col[0]==0)) - return matrix[_1][_0][row[1]][col[1]][0][col[2]]; - return matrix[_1][_1][row[1]][col[1]][0][0]; + return {dP[2][1] - dP[1][2], dP[0][2] - dP[2][0], dP[1][0] - dP[0][1]}; } - -/** \brief Assemble the stiffness matrix on the given grid view */ -template <class Basis, class LocalAssembler, class Matrix, class Vector> -void assembleStiffnessMatrix(const Basis& basis, - const LocalAssembler& localAssembler, - const BoundaryPatch<typename Basis::GridView>& neumannBoundary, - Matrix& matrix, - Vector& rhs) +/** \brief Local mass assembler for dune-functions basis + * + * We assume the mass matrix to be symmetric and hence ansatz and trial space to be equal! + * The implementation allows an arbitrary dune-functions compatible basis, as long as there + * is an ISTLBackend available for the resulting matrix. + */ +class RelaxedMicromorphicContinuumAssembler { - auto gridView = basis.gridView(); + double mu_e_; + double mu_c_; + double lambda_; - setOccupationPattern(basis, matrix); + double mu_h_; + double lambda_h_; + std::array<double,3> alpha_; +public: - // Unique element indices for visiting every intersection only once -// using ElementMapper = MultipleCodimMultipleGeomTypeMapper<typename Basis::GridView>; -// ElementMapper elementMapper(gridView, mcmgElementLayout()); + RelaxedMicromorphicContinuumAssembler(const ParameterTree& parameters) + { + mu_e_ = parameters.get<double>("mu_e"); + mu_c_ = parameters.get<double>("mu_c"); + lambda_ = parameters.get<double>("lambda"); - // Set all entries to zero - matrix = 0; + mu_h_ = parameters.get<double>("mu_h"); + lambda_h_ = parameters.get<double>("lambda_h"); + alpha_ = parameters.get<std::array<double,3> >("alpha"); + } - // Set rhs to correct length - auto rhsBackend = Functions::istlVectorBackend(rhs); - rhsBackend.resize(basis); + template<class Element, class LocalMatrix, class LocalView> + void operator()(const Element& element, LocalMatrix& localMatrix, const LocalView& trialLocalView, const LocalView& ansatzLocalView) const + { + using namespace Dune::Indices; - // Set all entries to zero - rhs = 0; + // the mass matrix operator assumes trial == ansatz + if (&trialLocalView != &ansatzLocalView) + DUNE_THROW(Dune::Exception,"The RelaxedMicromorphicContinuumAssembler assumes equal ansatz and trial bases"); - // A loop over all elements of the grid - auto localView = basis.localView(); + // matrix was already resized before + localMatrix = 0.; - for (const auto& element : elements(gridView)) - { - localView.bind(element); + const auto& geometry = element.geometry(); + constexpr int gridDim = LocalView::GridView::dimension; - // Now let's get the element stiffness matrix - // A dense matrix is used for the element stiffness matrix - Dune::Matrix<double> elementMatrix; - localAssembler.assembleElementMatrix(localView, elementMatrix); + const auto& displacementFiniteElement = trialLocalView.tree().child(_0,0).finiteElement(); + const auto& localDisplacementBasis = displacementFiniteElement.localBasis(); + using DisplacementJacobianType = typename std::decay_t<decltype(localDisplacementBasis)>::Traits::JacobianType; - // Add element stiffness matrix onto the global stiffness matrix - for (size_t i=0; i<elementMatrix.N(); i++) - { - // The global index of the i-th local degree of freedom of the element - auto row = localView.index(i); + const auto& distortionFiniteElement = trialLocalView.tree().child(_1,0).finiteElement(); + const auto& localDistortionBasis = distortionFiniteElement.localBasis(); + using DistortionRangeType = typename std::decay_t<decltype(localDistortionBasis)>::Traits::RangeType; + using DistortionJacobianType = typename std::decay_t<decltype(localDistortionBasis)>::Traits::JacobianType; - for (size_t j=0; j<elementMatrix.M(); j++ ) - { - // The global index of the j-th local degree of freedom of the element - auto col = localView.index(j); -#if 0 // If matrix is a MultiTypeBlockMatrix - matrixEntry(matrix, row, col) += elementMatrix[i][j]; -#else - matrix[row][col] += elementMatrix[i][j]; -#endif - } - } - } + std::vector<DisplacementJacobianType> displacementJacobian(localDisplacementBasis.size()); -#if 0 - auto localViewOutside = basis.localView(); + std::vector<DistortionRangeType> microDistortion(localDistortionBasis.size()); + std::vector<DistortionJacobianType> microDistortionJacobian(localDistortionBasis.size()); + std::vector<FieldVector<double,gridDim> > microDistortionCurl(localDistortionBasis.size()); - // A loop over all intersections of the grid - for (const auto& inside : elements(gridView)) - { - localView.bind(inside); + const int quadOrder = gridDim; // TODO: More appropriate order? + const auto& quad = QuadratureRules<double, gridDim>::rule(element.type(), quadOrder); - for (const auto& intersection : intersections(gridView, inside)) + for( const auto& qp : quad ) { - if (intersection.neighbor()) - { - // Visit every intersection only once - if (elementMapper.index(inside) > elementMapper.index(intersection.outside())) - continue; + const auto quadPos = qp.position(); + const auto integrationElement = geometry.integrationElement(quadPos); + const auto geometryJacobianIT = geometry.jacobianInverseTransposed(quadPos); - localViewOutside.bind(intersection.outside()); + localDisplacementBasis.evaluateJacobian(quadPos, displacementJacobian); - Dune::Matrix<double> matInIn; - Dune::Matrix<double> matInOut; - Dune::Matrix<double> matOutIn; - Dune::Matrix<double> matOutOut; + for (std::size_t i=0; i<displacementJacobian.size(); i++) + displacementJacobian[i] = displacementJacobian[i] * transpose(geometryJacobianIT); - localAssembler.assembleIntersectionMatrix(intersection, - localView, localViewOutside, - matInIn, matInOut, matOutIn, matOutOut); + localDistortionBasis.evaluateFunction(quadPos, microDistortion); - for(size_t i=0; i<matInIn.N(); i++) - { - // The global index of the i-th degree of freedom of the element - auto row = localView.index(i); + localDistortionBasis.evaluateJacobian(quadPos, microDistortionJacobian); - for (size_t j=0; j<matInIn.M(); j++ ) - { - // The global index of the j-th degree of freedom of the element - auto col = localView.index(j); - matrix[row][col] += matInIn[i][j]; - } - } + for (std::size_t i=0; i<microDistortionJacobian.size(); i++) + { + microDistortionJacobian[i] = microDistortionJacobian[i] * transpose(geometryJacobianIT); + microDistortionCurl[i] = curl(microDistortionJacobian[i]); + } - for(size_t i=0; i<matInOut.N(); i++) + // The displacement coupling with itself + for (std::size_t i=0; i<localDisplacementBasis.size(); i++) + { + for (std::size_t iDir=0; iDir<gridDim; iDir++) { - // The global index of the i-th degree of freedom of the element - auto row = localView.index(i); + auto rowIndex = trialLocalView.tree().child(_0,iDir).localIndex(i); - for (size_t j=0; j<matInOut.M(); j++ ) + for (std::size_t j=0; j<localDisplacementBasis.size(); j++) { - // The global index of the j-th degree of freedom of the element - auto col = localViewOutside.index(j); - matrix[row][col] += matInOut[i][j]; + for (std::size_t jDir=0; jDir<gridDim; jDir++) + { + auto colIndex = trialLocalView.tree().child(_0,jDir).localIndex(j); + + auto value = mu_e_ * frobeniusProduct(sym(displacementJacobian[i][0], iDir), + sym(displacementJacobian[j][0], jDir)) + + mu_c_ * frobeniusProduct(skew(displacementJacobian[i][0], iDir), + skew(displacementJacobian[j][0], jDir)) + + 0.5 * lambda_ * trace(displacementJacobian[i][0], iDir) * trace(displacementJacobian[j][0], jDir); + localMatrix[rowIndex][colIndex] += value * qp.weight() * integrationElement; + } } } + } - for(size_t i=0; i<matOutIn.N(); i++) + // The displacement coupling with the micro-distortion + for (std::size_t i=0; i<localDisplacementBasis.size(); i++) + { + for (std::size_t iDir=0; iDir<gridDim; iDir++) { - // The global index of the i-th degree of freedom of the element - auto row = localViewOutside.index(i); + auto rowIndex = trialLocalView.tree().child(_0,iDir).localIndex(i); - for (size_t j=0; j<matOutIn.M(); j++ ) + for (std::size_t j=0; j<localDistortionBasis.size(); j++) { - // The global index of the j-th degree of freedom of the element - auto col = localView.index(j); - matrix[row][col] += matOutIn[i][j]; - } - } + for (std::size_t jDir=0; jDir<gridDim; jDir++) + { + auto colIndex = trialLocalView.tree().child(_1,jDir).localIndex(j); - for (size_t i=0; i<matOutOut.N(); i++) - { - // The global index of the i-th degree of freedom of the element - auto row = localViewOutside.index(i); + auto value = -mu_e_ * frobeniusProduct(sym(displacementJacobian[i][0], iDir), + sym(microDistortion[j], jDir)) + - mu_c_ * frobeniusProduct(skew(displacementJacobian[i][0], iDir), + skew(microDistortion[j], jDir)) + - 0.5 * lambda_ * trace(displacementJacobian[i][0], iDir) * trace(microDistortion[j], jDir); - for (size_t j=0; j<matOutOut.M(); j++ ) - { - // The global index of the j-th degree of freedom of the element - auto col = localViewOutside.index(j); - matrix[row][col] += matOutOut[i][j]; + localMatrix[rowIndex][colIndex] += value * qp.weight() * integrationElement; + localMatrix[colIndex][rowIndex] += value * qp.weight() * integrationElement; + } } } + } - else if (neumannBoundary.contains(intersection)) + + // The micro-distortion coupling with itself + for (std::size_t i=0; i<localDistortionBasis.size(); i++) { - Vector elementRhs(localView.size()); - elementRhs = 0; + for (std::size_t iDir=0; iDir<gridDim; iDir++) + { + auto rowIndex = trialLocalView.tree().child(_1,iDir).localIndex(i); - localAssembler.assembleSurfaceLoad(intersection, - localView, - elementRhs); + for (std::size_t j=0; j<localDistortionBasis.size(); j++) + { + for (std::size_t jDir=0; jDir<gridDim; jDir++) + { + auto colIndex = trialLocalView.tree().child(_1,jDir).localIndex(j); + auto value = mu_e_ * frobeniusProduct(sym(microDistortion[i], iDir), sym(microDistortion[j], jDir)) + + mu_c_ * frobeniusProduct(skew(microDistortion[i], iDir), skew(microDistortion[j], jDir)) + + 0.5 * lambda_ * trace(microDistortion[i], iDir) * trace(microDistortion[j], jDir); - for (size_t i=0; i<localView.size(); i++) - { - // The global index of the i-th degree of freedom of the element - auto row = localView.index(i); + value += mu_h_ * frobeniusProduct(sym(microDistortion[i], iDir), sym(microDistortion[j], jDir)) + + 0.5 * lambda_h_ * trace(microDistortion[i], iDir) * trace(microDistortion[j], jDir); + + value += 0.5 * alpha_[0] * frobeniusProduct(dev(sym(microDistortionCurl[i], iDir)), + dev(sym(microDistortionCurl[j], jDir))) + + 0.5 * alpha_[1] * frobeniusProduct(skew(microDistortionCurl[i], iDir), + skew(microDistortionCurl[j], jDir)) + + 0.5 * alpha_[2] * trace(microDistortionCurl[i], iDir) * trace(microDistortionCurl[j], jDir); - rhs[row] += elementRhs[i]; + localMatrix[rowIndex][colIndex] += value * qp.weight() * integrationElement; + } + } } } } } -#endif -} + +}; // The grid dimension -const int dim = 2; +const int dim = 3; int main (int argc, char *argv[]) try { @@ -491,13 +336,6 @@ int main (int argc, char *argv[]) try // read solver settings const std::string solverType = parameterSet["solverType"]; - // Multigrid settings, if the solver is a multigrid one - const int maxIterations = parameterSet.get<int>("maxIterations"); - const int nu1 = parameterSet.get<int>("nu1"); - const int nu2 = parameterSet.get<int>("nu2"); - const int mu = parameterSet.get<int>("mu"); - const double tolerance = parameterSet.get<double>("tolerance"); - /////////////////////////////// // Generate the grid /////////////////////////////// @@ -548,7 +386,6 @@ int main (int argc, char *argv[]) try using namespace Indices; auto displacementBasis = Functions::subspaceBasis(basis, _0); - auto microDistortionBasis = Functions::subspaceBasis(basis, _1); /////////////////////////////////////////// @@ -571,50 +408,12 @@ int main (int argc, char *argv[]) try BoundaryPatch<GridView> dirichletBoundary(gridView, dirichletVertices); - /////////////////////////////////////////// - // Construct Neumann boundary - /////////////////////////////////////////// - BitSetVector<1> neumannVertices(gridView.size(dim), false); - - // Make Python function that computes which vertices are on the Dirichlet boundary, - // based on the vertex positions. - lambda = std::string("lambda x: (") + parameterSet.get<std::string>("neumannVerticesPredicate") + 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; - } - - BoundaryPatch<GridView> neumannBoundary(gridView, neumannVertices); - - BitSetVector<1> dirichletNodes(basis.size(), false); - constructBoundaryDofs(dirichletBoundary,basis,dirichletNodes); + BitSetVector<1> dirichletDofs(basis.size(), false); + constructBoundaryDofs(dirichletBoundary,basis,dirichletDofs); // Vector and matrix types -#if 0 - // Right now there is no real need for MultiTypeBlockVector, because both displacement - // and micro-distortion use the same vector type. However, I suspect we will have to - // Raviart-Thomas elements for the displacement eventually, so let's prepare for the - // future and use MultiTypeBlockVector right away. - using DisplacementVector = BlockVector<FieldVector<double,dim> >; - using microDistortionVector = BlockVector<FieldVector<double,dim> >; - - using Vector = MultiTypeBlockVector<DisplacementVector, microDistortionVector>; - - using Matrix00 = BCRSMatrix<FieldMatrix<double,dim,dim>>; - using Matrix01 = BCRSMatrix<FieldMatrix<double,dim,dim>>; - using Matrix10 = BCRSMatrix<FieldMatrix<double,dim,dim>>; - using Matrix11 = BCRSMatrix<FieldMatrix<double,dim,dim>>; - using MatrixRow0 = MultiTypeBlockVector<Matrix00, Matrix01>; - using MatrixRow1 = MultiTypeBlockVector<Matrix10, Matrix11>; - using Matrix = MultiTypeBlockMatrix<MatrixRow0,MatrixRow1>; -#else using Vector = BlockVector<double>; using Matrix = BCRSMatrix<double>; -#endif - //////////////////////////// // Initial iterate @@ -633,22 +432,27 @@ int main (int argc, char *argv[]) try rhs = 0; // Assemble elasticity problem - RelaxedMicromorphicContinuumAssembler localAssembler(parameterSet.sub("materialParameters"), - parameterSet.get<FieldVector<double,dim> >("neumannValue")); + RelaxedMicromorphicContinuumAssembler localAssembler(parameterSet.sub("materialParameters")); Matrix stiffnessMatrix; - assembleStiffnessMatrix(basis, localAssembler, neumannBoundary, stiffnessMatrix, rhs); + auto assembler = Fufem::duneFunctionsOperatorAssembler(basis, basis); + + Timer watch; + assembler.assembleBulk(Fufem::istlMatrixBackend(stiffnessMatrix), localAssembler); + std::cout << "Assembly took " << watch.elapsed() << " seconds" << std::endl; -#if 0 /////////////////////////////////////////// // Modify Dirichlet rows /////////////////////////////////////////// for (size_t i=0; i<stiffnessMatrix.N(); i++) - if (dirichletNodes[i][0]) + if (dirichletDofs[i][0]) + { for (auto&& [entry, j] : sparseRange(stiffnessMatrix[i])) entry = (i==j) ? 1.0 : 0.0; -#endif + + rhs[i] = x[i]; + } ///////////////////////////////// // Assemble the mass matrix @@ -664,15 +468,22 @@ int main (int argc, char *argv[]) try // Create a solver ///////////////////////////// + std::shared_ptr<Solvers::LinearSolver<Matrix,Vector> > solver; + if (solverType == "direct") { - Solvers::UMFPackSolver<Matrix,Vector> solver(stiffnessMatrix, x, rhs); - - solver.solve(); + solver = std::make_shared<Solvers::UMFPackSolver<Matrix,Vector> >(); } #if 0 else if (solverType == "standardMG") { + // Multigrid settings, if the solver is a multigrid one + const int maxIterations = parameterSet.get<int>("maxIterations"); + const int nu1 = parameterSet.get<int>("nu1"); + const int nu2 = parameterSet.get<int>("nu2"); + const int mu = parameterSet.get<int>("mu"); + const double tolerance = parameterSet.get<double>("tolerance"); + // Make pre and postsmoothers auto presmoother = Solvers::BlockGSStepFactory<Matrix, Vector>::create( Solvers::BlockGS::LocalSolvers::direct(0.0)); @@ -725,15 +536,62 @@ int main (int argc, char *argv[]) try else DUNE_THROW(NotImplemented, "Unknown solver type!"); - /////////////////////////// - // Write result to file - /////////////////////////// + if (parameterSet["timeIntegration"] == "static") + { + solver->setProblem(stiffnessMatrix, x, rhs); + solver->solve(); + + /////////////////////////// + // Write result to file + /////////////////////////// + + auto displacementFunction = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(displacementBasis, x); + + auto director0Function = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(Functions::subspaceBasis(basis, _1, 0), x); + auto director1Function = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(Functions::subspaceBasis(basis, _1, 1), x); + auto director2Function = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(Functions::subspaceBasis(basis, _1, 2), x); + + VTKWriter<GridView> vtkWriter(grid->leafGridView(), VTK::nonconforming); + vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::vector, dim)); + vtkWriter.addVertexData(director0Function, VTK::FieldInfo("director0", VTK::FieldInfo::Type::vector, dim)); + vtkWriter.addVertexData(director1Function, VTK::FieldInfo("director1", VTK::FieldInfo::Type::vector, dim)); + vtkWriter.addVertexData(director2Function, VTK::FieldInfo("director2", VTK::FieldInfo::Type::vector, dim)); + vtkWriter.write("relaxed-micromorphic-continuum-result"); + } + else if (parameterSet["timeIntegration"] == "implicitMidpoint") + { + double timeStep = parameterSet.get<double>("timeStep"); + + // Set up the matrix for the increment problem + auto matrix = massMatrix; + massMatrix.axpy(power(timeStep,2) * 0.25, stiffnessMatrix); +#if 0 + for (int i=0; i<numTimeSteps; i++) + { + // First half-step: Solve for the new configuration + auto rhs = M * oldConfiguration + + timeStep * 0.5 * (oldMomentum - timeStep * 0.5 * stiffnessMatrix * oldConfiguration - timeStep * b); + + solver->setProblem(matrix, newConfiguration, rhs); + solver->solve(); - auto displacementFunction = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(displacementBasis, x); + // Second half-step: Update momenta + newMomentum = momentum - timeStep * 0.5 * stiffnessMatrix * (oldConfiguration + newConfiguration) - 2*b; + + /////////////////////////// + // Write result to file + /////////////////////////// + + auto displacementFunction = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(displacementBasis, x); + + VTKWriter<GridView> vtkWriter(grid->leafGridView(), VTK::nonconforming); + vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::vector, dim)); + vtkWriter.write("relaxed-micromorphic-continuum-result"); + } +#endif + } else + DUNE_THROW(NotImplemented, "Time integration method '" << parameterSet["timeIntegration"] << "' not implemented!"); - VTKWriter<GridView> vtkWriter(grid->leafGridView(), VTK::nonconforming); - vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::vector, dim)); - vtkWriter.write("relaxed-micromorphic-continuum-result"); } catch (Exception& e) { std::cout << e.what() << std::endl; -- GitLab