Newer
Older
#ifndef COSSERAT_ENERGY_LOCAL_STIFFNESS_HH
#define COSSERAT_ENERGY_LOCAL_STIFFNESS_HH
#include <dune/common/fmatrix.hh>
#include <dune/geometry/quadraturerules.hh>
#include <dune/fufem/functions/virtualgridfunction.hh>
#include "localgeodesicfestiffness.hh"
#include "localgeodesicfefunction.hh"

Oliver Sander
committed
#include <dune/gfe/rigidbodymotion.hh>
#include <dune/gfe/tensor3.hh>

Oliver Sander
committed
#include <dune/gfe/orthogonalmatrix.hh>

Oliver Sander
committed
#define DONT_USE_CURL

Oliver Sander
committed
template<class GridView, class LocalFiniteElement, int dim>
class CosseratEnergyLocalStiffness
: public LocalGeodesicFEStiffness<GridView,LocalFiniteElement,RigidBodyMotion<double,dim> >
{
// grid types
typedef typename GridView::Grid::ctype DT;
typedef RigidBodyMotion<double,dim> TargetSpace;
typedef typename TargetSpace::ctype RT;
typedef typename GridView::template Codim<0>::Entity Entity;
// some other sizes
enum {gridDim=GridView::dimension};

Oliver Sander
committed
/** \brief Compute the symmetric part of a matrix A, i.e. \f$ \frac 12 (A + A^T) \f$ */
static Dune::FieldMatrix<double,dim,dim> sym(const Dune::FieldMatrix<double,dim,dim>& A)
{
Dune::FieldMatrix<double,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<double,dim,dim> skew(const Dune::FieldMatrix<double,dim,dim>& A)
{
Dune::FieldMatrix<double,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 Return the square of the trace of a matrix */
template <int N>
static double traceSquared(const Dune::FieldMatrix<double,N,N>& A)

Oliver Sander
committed
{
double trace = 0;
for (int i=0; i<N; i++)

Oliver Sander
committed
trace += A[i][i];
return trace*trace;
}
/** \brief Compute the (row-wise) curl of a matrix R \f$
\param DR The partial derivatives of the matrix R
*/
static Dune::FieldMatrix<double,dim,dim> curl(const Tensor3<double,dim,dim,dim>& DR)
{
Dune::FieldMatrix<double,dim,dim> result;
for (int i=0; i<dim; i++) {
result[i][0] = DR[i][2][1] - DR[i][1][2];
result[i][1] = DR[i][0][2] - DR[i][2][0];
result[i][2] = DR[i][1][0] - DR[i][0][1];
}
return result;
}
public: // for testing
/** \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<double,3>& value,
const Dune::FieldMatrix<double,7,gridDim>& derivative,
Tensor3<double,3,3,3>& 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, if I am not mistaken, DR[i][j][k] contains \partial R_ij / \partial k
Tensor3<double,3 , 3, 4> dd_dq;
value.q.getFirstDerivativesOfDirectors(dd_dq);
DR = 0;
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<gridDim; k++)
for (int l=0; l<4; l++)
DR[i][j][k] += dd_dq[j][i][l] * derivative[l+3][k];
}
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/** \brief Compute the derivative of the rotation (with respect to the corner coefficients), but wrt matrix coordinates
\param value Value of the gfe function at a certain point
\param derivative First derivative of the gfe function wrt the coefficients at that point, in quaternion coordinates
*/
static void computeDR_dv(const RigidBodyMotion<double,3>& value,
const Dune::FieldMatrix<double,7,7>& derivative,
Tensor3<double,3,3,4>& dR_dv)
{
// 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, if I am not mistaken, DR[i][j][k] contains \partial R_ij / \partial k
Tensor3<double,3 , 3, 4> dd_dq;
value.q.getFirstDerivativesOfDirectors(dd_dq);
dR_dv = 0;
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<4; k++)
for (int l=0; l<4; l++)
dR_dv[i][j][k] += dd_dq[j][i][l] * derivative[k+3][l+3];
}

Oliver Sander
committed
/** \brief Compute the derivative of the gradient of the rotation with respect to the corner coefficients,
* but in matrix coordinates
*
\param value Value of the gfe function at a certain point
\param derivative First derivative of the gfe function wrt the coefficients at that point, in quaternion coordinates
*/
static void compute_dDR_dv(const RigidBodyMotion<double,3>& value,
const Dune::FieldMatrix<double,7,gridDim>& derOfValueWRTx,
const Dune::FieldMatrix<double,7,7>& derOfValueWRTCoefficient,
const Tensor3<double,7,7,gridDim>& derOfGradientWRTCoefficient,
Dune::array<Tensor3<double,3,3,4>, 3>& dDR_dv,
Tensor3<double,3,gridDim,4>& dDR3_dv)

Oliver Sander
committed
{
// 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, if I am not mistaken, DR[i][j][k] contains \partial R_ij / \partial k
Tensor3<double,3 , 3, 4> dd_dq;
value.q.getFirstDerivativesOfDirectors(dd_dq);
/** \todo This is a constant -- don't recompute it every time */
Dune::array<Tensor3<double,3,4,4>, 3> dd_dq_dq;
Rotation<double,3>::getSecondDerivativesOfDirectors(dd_dq_dq);

Oliver Sander
committed
dDR_dv[i] = 0;
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<gridDim; k++)
for (int v_i=0; v_i<4; v_i++)
for (int l=0; l<4; l++)
for (int m=0; m<4; m++)
dDR_dv[i][j][k][v_i] += dd_dq_dq[j][i][l][m] * derOfValueWRTx[l+3][k] * derOfValueWRTCoefficient[v_i+3][m+3];

Oliver Sander
committed
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
for (int k=0; k<gridDim; k++)
for (int v_i=0; v_i<4; v_i++)
for (int l=0; l<4; l++)
dDR_dv[i][j][k][v_i] += dd_dq[j][i][l] * derOfGradientWRTCoefficient[v_i+3][l+3][k];

Oliver Sander
committed
///////////////////////////////////////////////////////////////////////////////////////////
// Compute covariant derivative for the third director
// by projecting onto the tangent space at S^2. Yes, that is something different!
///////////////////////////////////////////////////////////////////////////////////////////

Oliver Sander
committed
Dune::FieldMatrix<double,3,3> Mtmp;
value.q.matrix(Mtmp);
OrthogonalMatrix<double,3> M(Mtmp);
Dune::FieldVector<double,3> tmpDirector;
for (int i=0; i<3; i++)
tmpDirector[i] = M.data()[i][2];
UnitVector<double,3> director(tmpDirector);
for (int k=0; k<gridDim; k++)
for (int v_i=0; v_i<4; v_i++) {
Dune::FieldVector<double,3> unprojected;
for (int i=0; i<3; i++)
unprojected[i] = dDR_dv[i][2][k][v_i];
Dune::FieldVector<double,3> projected = director.projectOntoTangentSpace(unprojected);
for (int i=0; i<3; i++)
dDR3_dv[i][k][v_i] = projected[i];
}
///////////////////////////////////////////////////////////////////////////////////////////
// Compute covariant derivative on SO(3) by projecting onto the tangent space at M(q).
// This has to come second, as it overwrites the dDR_dv variable.
///////////////////////////////////////////////////////////////////////////////////////////

Oliver Sander
committed
for (int k=0; k<gridDim; k++)
for (int v_i=0; v_i<4; v_i++) {
Dune::FieldMatrix<double,3,3> unprojected;
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
unprojected[i][j] = dDR_dv[i][j][k][v_i];
Dune::FieldMatrix<double,3,3> projected = M.projectOntoTangentSpace(unprojected);
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
dDR_dv[i][j][k][v_i] = projected[i][j];
}

Oliver Sander
committed
}

Oliver Sander
committed
/** \brief Constructor with a set of material parameters
* \param parameters The material parameters
*/
CosseratEnergyLocalStiffness(const Dune::ParameterTree& parameters,
const BoundaryPatch<GridView>* neumannBoundary,
const Dune::VirtualFunction<Dune::FieldVector<double,gridDim>, Dune::FieldVector<double,3> >* neumannFunction)
: neumannBoundary_(neumannBoundary),
neumannFunction_(neumannFunction)

Oliver Sander
committed
{
// The shell thickness
thickness_ = parameters.template get<double>("thickness");
// Lame constants
mu_ = parameters.template get<double>("mu");
lambda_ = parameters.template get<double>("lambda");
// Cosserat couple modulus, preferably 0
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");

Oliver Sander
committed
}
/** \brief Assemble the energy for a single element */
RT energy (const Entity& e,
const LocalFiniteElement& localFiniteElement,
const std::vector<TargetSpace>& localSolution) const;
/** \brief Assemble the element gradient of the energy functional */
virtual void assembleGradient(const Entity& element,
const LocalFiniteElement& localFiniteElement,
const std::vector<TargetSpace>& localSolution,
std::vector<typename TargetSpace::TangentVector>& localGradient) const;
/** \brief The energy \f$ W_{mp}(\overline{U}) \f$, as written in
* the first equation of (4.4) in Neff's paper
*/

Oliver Sander
committed
RT quadraticMembraneEnergy(const Dune::FieldMatrix<double,3,3>& U) const
{
Dune::FieldMatrix<double,3,3> UMinus1 = U;
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::FieldMatrix<double,3,3>& U) const
{
RT result = 0;
// shear-stretch energy
Dune::FieldMatrix<double,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[i][j] + U[j][i]) - (i==j);
result += mu_ * sym2x2.frobenius_norm2();
// first order drill energy
Dune::FieldMatrix<double,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[i][j] - U[j][i]);
result += mu_c_ * skew2x2.frobenius_norm2();
// classical transverse shear energy
result += kappa_ * (mu_ + mu_c_)/2 * (U[2][0]*U[2][0] + U[2][1]*U[2][1]);
// elongational stretch energy
result += mu_*lambda_ / (2*mu_ + lambda_) * traceSquared(sym2x2);
return result;
}

Oliver Sander
committed
RT curvatureEnergy(const Tensor3<double,3,3,3>& DR) const
{

Oliver Sander
committed
#ifdef DONT_USE_CURL
return mu_ * std::pow(L_c_ * DR.frobenius_norm(),q_);
#else
return mu_ * std::pow(L_c_ * curl(DR).frobenius_norm(),q_);

Oliver Sander
committed
#endif

Oliver Sander
committed
}
RT bendingEnergy(const Dune::FieldMatrix<double,dim,dim>& R, const Tensor3<double,3,3,3>& DR) const
{
// left-multiply the derivative of the third director (in DR[][2][]) with R^T

Oliver Sander
committed
Dune::FieldMatrix<double,3,3> RT_DR3;
for (int i=0; i<3; i++)
for (int j=0; j<3; j++) {
RT_DR3[i][j] = 0;
for (int k=0; k<3; k++)
RT_DR3[i][j] += R[k][i] * DR[k][2][j];

Oliver Sander
committed
}
return mu_ * sym(RT_DR3).frobenius_norm2()
+ mu_c_ * skew(RT_DR3).frobenius_norm2()
+ mu_*lambda_/(2*mu_+lambda_) * traceSquared(RT_DR3);

Oliver Sander
committed
}
///////////////////////////////////////////////////////////////////////////////////////
// Methods to compute the energy gradient
///////////////////////////////////////////////////////////////////////////////////////
void longQuadraticMembraneEnergyGradient(typename TargetSpace::EmbeddedTangentVector& localGradient,
const Dune::FieldMatrix<double,3,3>& R,
const Tensor3<double,3,3,4>& dR_dv,
const Dune::FieldMatrix<double,7,gridDim>& derivative,
const Tensor3<double,7,7,gridDim>& derOfGradientWRTCoefficient,
const Dune::FieldMatrix<double,3,3>& U) const;

Oliver Sander
committed
void curvatureEnergyGradient(typename TargetSpace::EmbeddedTangentVector& embeddedLocalGradient,
const Dune::FieldMatrix<double,3,3>& R,
const Tensor3<double,3,3,3>& DR,
const Dune::array<Tensor3<double,3,3,4>, 3>& dDR_dv) const;
void bendingEnergyGradient(typename TargetSpace::EmbeddedTangentVector& embeddedLocalGradient,
const Dune::FieldMatrix<double,3,3>& R,
const Tensor3<double,3,3,4>& dR_dv,
const Tensor3<double,3,3,3>& DR,
const Tensor3<double,3,gridDim,4>& dDR3_dv) const;

Oliver Sander
committed

Oliver Sander
committed
/** \brief The shell thickness */
double thickness_;
/** \brief Lame constants */
double mu_, lambda_;

Oliver Sander
committed
/** \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_;

Oliver Sander
committed
/** \brief The Neumann boundary */
const BoundaryPatch<GridView>* neumannBoundary_;
/** \brief The function implementing the Neumann data */
const Dune::VirtualFunction<Dune::FieldVector<double,gridDim>, Dune::FieldVector<double,3> >* neumannFunction_;
template <class GridView, class LocalFiniteElement, int dim>
typename CosseratEnergyLocalStiffness<GridView,LocalFiniteElement,dim>::RT
CosseratEnergyLocalStiffness<GridView,LocalFiniteElement,dim>::
energy(const Entity& element,
const LocalFiniteElement& localFiniteElement,
const std::vector<RigidBodyMotion<double,dim> >& localSolution) const
{
RT energy = 0;
LocalGeodesicFEFunction<gridDim, double, LocalFiniteElement, TargetSpace> localGeodesicFEFunction(localFiniteElement,
localSolution);
int quadOrder = (element.type().isSimplex()) ? localFiniteElement.localBasis().order()
: localFiniteElement.localBasis().order() * gridDim;
const Dune::QuadratureRule<double, gridDim>& quad
= Dune::QuadratureRules<double, gridDim>::rule(element.type(), quadOrder);
for (size_t pt=0; pt<quad.size(); pt++) {
// Local position of the quadrature point
const Dune::FieldVector<double,gridDim>& quadPos = quad[pt].position();
const double integrationElement = element.geometry().integrationElement(quadPos);
const Dune::FieldMatrix<double,gridDim,gridDim>& jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos);
double weight = quad[pt].weight() * integrationElement;

Oliver Sander
committed
// The value of the local function
RigidBodyMotion<double,dim> value = localGeodesicFEFunction.evaluate(quadPos);
// The derivative of the local function defined on the reference element

Oliver Sander
committed
Dune::FieldMatrix<double, TargetSpace::EmbeddedTangentVector::dimension, gridDim> referenceDerivative = localGeodesicFEFunction.evaluateDerivative(quadPos,value);
// The derivative of the function defined on the actual element
Dune::FieldMatrix<double, TargetSpace::EmbeddedTangentVector::dimension, gridDim> derivative(0);
for (size_t comp=0; comp<referenceDerivative.N(); comp++)
jacobianInverseTransposed.umv(referenceDerivative[comp], derivative[comp]);

Oliver Sander
committed
/////////////////////////////////////////////////////////
// compute U, the Cosserat strain
/////////////////////////////////////////////////////////
dune_static_assert(dim>=gridDim, "Codim of the grid must be nonnegative");

Oliver Sander
committed
//
Dune::FieldMatrix<double,dim,dim> R;
value.q.matrix(R);
/* Compute F, the deformation gradient.
In the continuum case this is
\f$ \hat{F} = \nabla m \f$
In the case of a shell it is
\f$ \hat{F} = (\nabla m | \overline{R}_3) \f$
*/

Oliver Sander
committed
Dune::FieldMatrix<double,dim,dim> F;
for (int i=0; i<dim; i++)
for (int j=0; j<gridDim; j++)
F[i][j] = derivative[i][j];
for (int i=0; i<dim; i++)
for (int j=gridDim; j<dim; j++)
F[i][j] = R[i][j];

Oliver Sander
committed
// U = R^T F
Dune::FieldMatrix<double,dim,dim> U;
for (int i=0; i<dim; i++)
for (int j=0; j<dim; j++) {
U[i][j] = 0;
for (int k=0; k<dim; k++)
U[i][j] += R[k][i] * F[k][j];
}
//////////////////////////////////////////////////////////
// Compute the derivative of the rotation
// Note: we need it in matrix coordinates
//////////////////////////////////////////////////////////
Tensor3<double,3,3,3> DR;
computeDR(value, derivative, DR);

Oliver Sander
committed
// Add the local energy density

Oliver Sander
committed
if (gridDim==2) {
//energy += weight * thickness_ * quadraticMembraneEnergy(U);
energy += weight * thickness_ * longQuadraticMembraneEnergy(U);

Oliver Sander
committed
energy += weight * thickness_ * curvatureEnergy(DR);
energy += weight * std::pow(thickness_,3) / 12.0 * bendingEnergy(R,DR);

Oliver Sander
committed
} else if (gridDim==3) {
energy += weight * quadraticMembraneEnergy(U);
energy += weight * curvatureEnergy(DR);
} else
DUNE_THROW(Dune::NotImplemented, "CosseratEnergyStiffness for 1d grids");
//////////////////////////////////////////////////////////////////////////////
// Assemble boundary contributions
//////////////////////////////////////////////////////////////////////////////
assert(neumannFunction_);
for (typename Entity::LeafIntersectionIterator it = element.ileafbegin(); it != element.ileafend(); ++it) {
if (not neumannBoundary_ or not neumannBoundary_->contains(*it))
continue;
const Dune::QuadratureRule<double, gridDim-1>& quad
= Dune::QuadratureRules<double, gridDim-1>::rule(it->type(), quadOrder);
for (size_t pt=0; pt<quad.size(); pt++) {
// Local position of the quadrature point
const Dune::FieldVector<double,gridDim>& quadPos = it->geometryInInside().global(quad[pt].position());
const double integrationElement = it->geometry().integrationElement(quad[pt].position());
// The value of the local function
RigidBodyMotion<double,dim> value = localGeodesicFEFunction.evaluate(quadPos);
// Value of the Neumann data at the current position
Dune::FieldVector<double,3> neumannValue;
if (dynamic_cast<const VirtualGridViewFunction<GridView,Dune::FieldVector<double,3> >*>(neumannFunction_))
dynamic_cast<const VirtualGridViewFunction<GridView,Dune::FieldVector<double,3> >*>(neumannFunction_)->evaluateLocal(element, quadPos, neumannValue);
else
neumannFunction_->evaluate(it->geometry().global(quad[pt].position()), neumannValue);

Oliver Sander
committed
// Only translational dofs are affected by the Neumann force
energy += thickness_ * (neumannValue * value.r) * quad[pt].weight() * integrationElement;
}
}

Oliver Sander
committed
return energy;
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
template <class GridView, class LocalFiniteElement, int dim>
void CosseratEnergyLocalStiffness<GridView, LocalFiniteElement, dim>::
longQuadraticMembraneEnergyGradient(typename TargetSpace::EmbeddedTangentVector& embeddedLocalGradient,
const Dune::FieldMatrix<double,3,3>& R,
const Tensor3<double,3,3,4>& dR_dv,
const Dune::FieldMatrix<double,7,gridDim>& derivative,
const Tensor3<double,7,7,gridDim>& derOfGradientWRTCoefficient,
const Dune::FieldMatrix<double,3,3>& U
) const
{
// Compute partial/partial v ((R_1|R_2)^T\nablam)
Tensor3<double,2,2,7> dR1R2Tnablam_dv(0);
for (size_t i=0; i<2; i++) {
for (size_t j=0; j<2; j++) {
for (size_t k=0; k<3; k++) {
// Translational dofs of the coefficient
for (size_t v_i=0; v_i<3; v_i++)
dR1R2Tnablam_dv[i][j][v_i] += R[k][i] * derOfGradientWRTCoefficient[k][v_i][j];
// Rotational dofs of the coefficient
for (size_t v_i=0; v_i<4; v_i++)
dR1R2Tnablam_dv[i][j][v_i+3] += dR_dv[k][i][v_i] * derivative[k][j];
}
}
}
////////////////////////////////////////////////////////////////////////////////////
// Shear--Stretch Energy
////////////////////////////////////////////////////////////////////////////////////
for (size_t v_i=0; v_i<7; v_i++) {
for (size_t i=0; i<2; i++)
for (size_t j=0; j<2; j++) {
double symUMinusI = 0.5*U[i][j] + 0.5*U[j][i] - (i==j);
embeddedLocalGradient[v_i] += mu_ * symUMinusI * (dR1R2Tnablam_dv[i][j][v_i] + dR1R2Tnablam_dv[j][i][v_i]);
}
}
////////////////////////////////////////////////////////////////////////////////////
// First-order Drill Energy
////////////////////////////////////////////////////////////////////////////////////
for (size_t v_i=0; v_i<7; v_i++) {
for (size_t i=0; i<2; i++)
for (size_t j=0; j<2; j++)
embeddedLocalGradient[v_i] += mu_c_ * 0.5* (U[i][j]-U[j][i]) * (dR1R2Tnablam_dv[i][j][v_i] - dR1R2Tnablam_dv[j][i][v_i]);
}
////////////////////////////////////////////////////////////////////////////////////
// Classical Transverse Shear Energy
////////////////////////////////////////////////////////////////////////////////////
for (size_t v_i=0; v_i<7; v_i++) {
for (size_t j=0; j<2; j++) {
double sp = 0;
for (size_t k=0; k<3; k++)
sp += R[k][2]*derivative[k][j];
double spDer = 0;
if (v_i < 3)
for (size_t k=0; k<3; k++)
spDer += R[k][2] * derOfGradientWRTCoefficient[k][v_i][j];
else
for (size_t k=0; k<3; k++)
spDer += dR_dv[k][2][v_i-3] * derivative[k][j];
embeddedLocalGradient[v_i] += 0.5 * kappa_ * (mu_ + mu_c_) * 2 * sp * spDer;
}
}
////////////////////////////////////////////////////////////////////////////////////
// Elongational Stretch Energy
////////////////////////////////////////////////////////////////////////////////////
double trace = U[0][0] + U[1][1] - 2;
for (size_t v_i=0; v_i<7; v_i++)
for (size_t i=0; i<2; i++)
embeddedLocalGradient[v_i] += 2 * mu_*lambda_ / (2*mu_+lambda_)* trace * dR1R2Tnablam_dv[i][i][v_i];
}

Oliver Sander
committed
template <class GridView, class LocalFiniteElement, int dim>
void CosseratEnergyLocalStiffness<GridView, LocalFiniteElement, dim>::
curvatureEnergyGradient(typename TargetSpace::EmbeddedTangentVector& embeddedLocalGradient,
const Dune::FieldMatrix<double,3,3>& R,
const Tensor3<double,3,3,3>& DR,
const Dune::array<Tensor3<double,3,3,4>, 3>& dDR_dv) const
{

Oliver Sander
committed
#ifndef DONT_USE_CURL
#error curvatureEnergyGradient not implemented for the curl curvature energy
#endif

Oliver Sander
committed
embeddedLocalGradient = 0;
for (size_t v_i=0; v_i<4; v_i++) {
for (size_t i=0; i<3; i++)
for (size_t j=0; j<3; j++)
for (size_t k=0; k<3; k++)
embeddedLocalGradient[v_i+3] += DR[i][j][k] * dDR_dv[i][j][k][v_i];
}
// multiply with constant pre-factor

Oliver Sander
committed
embeddedLocalGradient *= mu_ * q_ * std::pow(L_c_, q_) * std::pow(DR.frobenius_norm(), q_ - 2);
}
template <class GridView, class LocalFiniteElement, int dim>
void CosseratEnergyLocalStiffness<GridView, LocalFiniteElement, dim>::
bendingEnergyGradient(typename TargetSpace::EmbeddedTangentVector& embeddedLocalGradient,
const Dune::FieldMatrix<double,3,3>& R,
const Tensor3<double,3,3,4>& dR_dv,
const Tensor3<double,3,3,3>& DR,
const Tensor3<double,3,gridDim,4>& dDR3_dv) const

Oliver Sander
committed
{
embeddedLocalGradient = 0;
// left-multiply the derivative of the third director (in DR[][2][]) with R^T
Dune::FieldMatrix<double,3,3> RT_DR3(0);

Oliver Sander
committed
for (int i=0; i<3; i++)

Oliver Sander
committed
for (int k=0; k<3; k++)
RT_DR3[i][j] += R[k][i] * DR[k][2][j];
for (int i=0; i<gridDim; i++)
assert(std::fabs(RT_DR3[2][i]) < 1e-7);

Oliver Sander
committed
// -----------------------------------------------------------------------------
Tensor3<double,3,3,4> d_RT_DR3(0);
for (size_t v_i=0; v_i<4; v_i++)
for (int i=0; i<3; i++)

Oliver Sander
committed
for (int j=0; j<gridDim; j++)

Oliver Sander
committed
for (int k=0; k<3; k++)
d_RT_DR3[i][j][v_i] += dR_dv[k][i][v_i] * DR[k][2][j] + R[k][i] * dDR3_dv[k][j][v_i];
// Project onto the tangent space at (0,0,1). I don't really understand why this is needed,
// but it does make the test pass in all cases. I suppose that the chain rule demands it
// in some way or the other.
for (int i=0; i<gridDim; i++)
for (size_t v_i=0; v_i<4; v_i++)
d_RT_DR3[2][i][v_i] = 0;
////////////////////////////////////////////////////////////////////////////////
// "Shear--Stretch Energy"
////////////////////////////////////////////////////////////////////////////////

Oliver Sander
committed
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
for (size_t v_i=0; v_i<4; v_i++)
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
embeddedLocalGradient[v_i+3] += mu_ * 0.5 * (RT_DR3[i][j]+RT_DR3[j][i]) * (d_RT_DR3[i][j][v_i] + d_RT_DR3[j][i][v_i]);
////////////////////////////////////////////////////////////////////////////////
// "First-order Drill Energy"
////////////////////////////////////////////////////////////////////////////////
for (size_t v_i=0; v_i<4; v_i++)
for (int i=0; i<3; i++)
for (int j=0; j<3; j++)
embeddedLocalGradient[v_i+3] += mu_c_ * 0.5 * (RT_DR3[i][j]-RT_DR3[j][i]) * (d_RT_DR3[i][j][v_i] - d_RT_DR3[j][i][v_i]);
////////////////////////////////////////////////////////////////////////////////
// "Elongational Stretch Energy"
////////////////////////////////////////////////////////////////////////////////
double factor = 2 * mu_*lambda_ / (2*mu_ + lambda_) * (RT_DR3[0][0] + RT_DR3[1][1] + RT_DR3[2][2]);
for (size_t v_i=0; v_i<4; v_i++)
for (int i=0; i<3; i++)
embeddedLocalGradient[v_i+3] += factor * d_RT_DR3[i][i][v_i];
}
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
template <class GridView, class LocalFiniteElement, int dim>
void CosseratEnergyLocalStiffness<GridView, LocalFiniteElement, dim>::
assembleGradient(const Entity& element,
const LocalFiniteElement& localFiniteElement,
const std::vector<TargetSpace>& localSolution,
std::vector<typename TargetSpace::TangentVector>& localGradient) const
{
std::vector<typename TargetSpace::EmbeddedTangentVector> embeddedLocalGradient(localSolution.size());
std::fill(embeddedLocalGradient.begin(), embeddedLocalGradient.end(), 0);
LocalGeodesicFEFunction<gridDim, double, LocalFiniteElement, TargetSpace> localGeodesicFEFunction(localFiniteElement,
localSolution);
/** \todo Is this larger than necessary? */
int quadOrder = (element.type().isSimplex()) ? localFiniteElement.localBasis().order()
: localFiniteElement.localBasis().order() * gridDim;
const Dune::QuadratureRule<double, gridDim>& quad
= Dune::QuadratureRules<double, gridDim>::rule(element.type(), quadOrder);
for (size_t pt=0; pt<quad.size(); pt++) {
// Local position of the quadrature point
const Dune::FieldVector<double,gridDim>& quadPos = quad[pt].position();
const double integrationElement = element.geometry().integrationElement(quadPos);
const Dune::FieldMatrix<double,gridDim,gridDim>& jacobianInverseTransposed = element.geometry().jacobianInverseTransposed(quadPos);
double weight = quad[pt].weight() * integrationElement;
// The value of the local function
RigidBodyMotion<double,dim> value = localGeodesicFEFunction.evaluate(quadPos);
// The derivative of the local function defined on the reference element
Dune::FieldMatrix<double, TargetSpace::EmbeddedTangentVector::dimension, gridDim> referenceDerivative = localGeodesicFEFunction.evaluateDerivative(quadPos,value);
// The derivative of the function defined on the actual element
Dune::FieldMatrix<double, TargetSpace::EmbeddedTangentVector::dimension, gridDim> derivative(0);
for (size_t comp=0; comp<referenceDerivative.N(); comp++)
jacobianInverseTransposed.umv(referenceDerivative[comp], derivative[comp]);
/////////////////////////////////////////////////////////
// compute U, the Cosserat strain
/////////////////////////////////////////////////////////
dune_static_assert(dim>=gridDim, "Codim of the grid must be nonnegative");
//
Dune::FieldMatrix<double,dim,dim> R;
value.q.matrix(R);
/* Compute F, the deformation gradient.
In the continuum case this is
\f$ \hat{F} = \nabla m \f$
In the case of a shell it is
\f$ \hat{F} = (\nabla m | \overline{R}_3) \f$
*/
Dune::FieldMatrix<double,dim,dim> F;
for (int i=0; i<dim; i++)
for (int j=0; j<gridDim; j++)
F[i][j] = derivative[i][j];
for (int i=0; i<dim; i++)
for (int j=gridDim; j<dim; j++)
F[i][j] = R[i][j];
// U = R^T F
Dune::FieldMatrix<double,dim,dim> U;
for (int i=0; i<dim; i++)
for (int j=0; j<dim; j++) {
U[i][j] = 0;
for (int k=0; k<dim; k++)
U[i][j] += R[k][i] * F[k][j];
}
//////////////////////////////////////////////////////////
// Compute the derivative of the rotation
// Note: we need it in matrix coordinates
//////////////////////////////////////////////////////////
Tensor3<double,3,3,3> DR;
computeDR(value, derivative, DR);
// loop over all the element's degrees of freedom and compute the gradient wrt it
for (size_t i=0; i<localSolution.size(); i++) {
// --------------------------------------------------------------------
Dune::FieldMatrix<double,7,7> derOfValueWRTCoefficient;
localGeodesicFEFunction.evaluateDerivativeOfValueWRTCoefficient(quadPos, i, derOfValueWRTCoefficient);
Tensor3<double,3,3,4> dR_dv;
computeDR_dv(value, derOfValueWRTCoefficient, dR_dv);
// --------------------------------------------------------------------
Tensor3<double, TargetSpace::EmbeddedTangentVector::dimension,TargetSpace::EmbeddedTangentVector::dimension,gridDim> referenceDerivativeDerivative;
localGeodesicFEFunction.evaluateDerivativeOfGradientWRTCoefficient(quadPos, i, referenceDerivativeDerivative);
// multiply the transformation from the reference element to the actual element
Tensor3<double, TargetSpace::EmbeddedTangentVector::dimension,TargetSpace::EmbeddedTangentVector::dimension,gridDim> derOfGradientWRTCoefficient;
for (int ii=0; ii<TargetSpace::EmbeddedTangentVector::dimension; ii++)
for (int jj=0; jj<TargetSpace::EmbeddedTangentVector::dimension; jj++)
for (int kk=0; kk<gridDim; kk++) {
derOfGradientWRTCoefficient[ii][jj][kk] = 0;
for (int ll=0; ll<gridDim; ll++)
derOfGradientWRTCoefficient[ii][jj][kk] += referenceDerivativeDerivative[ii][jj][ll] * jacobianInverseTransposed[kk][ll];
}

Oliver Sander
committed
// ---------------------------------------------------------------------
Dune::array<Tensor3<double,3,3,4>, 3> dDR_dv;
Tensor3<double,3,gridDim,4> dDR3_dv;
compute_dDR_dv(value, derivative, derOfValueWRTCoefficient, derOfGradientWRTCoefficient, dDR_dv, dDR3_dv);

Oliver Sander
committed
// Add the local energy density
if (gridDim==2) {
typename TargetSpace::EmbeddedTangentVector tmp(0);
longQuadraticMembraneEnergyGradient(tmp,R,dR_dv,derivative,derOfGradientWRTCoefficient,U);
embeddedLocalGradient[i].axpy(weight * thickness_, tmp);

Oliver Sander
committed
tmp = 0;
curvatureEnergyGradient(tmp,R,DR,dDR_dv);
embeddedLocalGradient[i].axpy(weight * thickness_, tmp);

Oliver Sander
committed
tmp = 0;
bendingEnergyGradient(tmp,R,dR_dv,DR,dDR3_dv);
embeddedLocalGradient[i].axpy(weight * std::pow(thickness_,3) / 12.0, tmp);
} else if (gridDim==3) {
assert(gridDim==2); // 3d not implemented yet
// energy += weight * quadraticMembraneEnergyGradient(U);
// energy += weight * curvatureEnergyGradient(DR);
} else
DUNE_THROW(Dune::NotImplemented, "CosseratEnergyStiffness for 1d grids");

Oliver Sander
committed
}
}
//////////////////////////////////////////////////////////////////////////////
// Assemble boundary contributions
//////////////////////////////////////////////////////////////////////////////
assert(neumannFunction_);

Oliver Sander
committed
for (typename Entity::LeafIntersectionIterator it = element.ileafbegin(); it != element.ileafend(); ++it) {
if (not neumannBoundary_ or not neumannBoundary_->contains(*it))
continue;
const Dune::QuadratureRule<double, gridDim-1>& quad
= Dune::QuadratureRules<double, gridDim-1>::rule(it->type(), quadOrder);
for (size_t pt=0; pt<quad.size(); pt++) {
// Local position of the quadrature point
const Dune::FieldVector<double,gridDim>& quadPos = it->geometryInInside().global(quad[pt].position());
const double integrationElement = it->geometry().integrationElement(quad[pt].position());
// Value of the Neumann data at the current position
Dune::FieldVector<double,3> neumannValue;
if (dynamic_cast<const VirtualGridViewFunction<GridView,Dune::FieldVector<double,3> >*>(neumannFunction_))
dynamic_cast<const VirtualGridViewFunction<GridView,Dune::FieldVector<double,3> >*>(neumannFunction_)->evaluateLocal(element, quadPos, neumannValue);
else
neumannFunction_->evaluate(it->geometry().global(quad[pt].position()), neumannValue);

Oliver Sander
committed
// loop over all the element's degrees of freedom and compute the gradient wrt it
for (size_t i=0; i<localSolution.size(); i++) {
// --------------------------------------------------------------------
Dune::FieldMatrix<double,7,7> derOfValueWRTCoefficient;
localGeodesicFEFunction.evaluateDerivativeOfValueWRTCoefficient(quadPos, i, derOfValueWRTCoefficient);
// Only translational dofs are affected by the Neumann force
for (size_t v_i=0; v_i<3; v_i++)
for (size_t j=0; j<3; j++)
#warning Try whether the arguments of derOfValueWRTCoefficient are swapped

Oliver Sander
committed
embeddedLocalGradient[i][v_i] += thickness_ * (neumannValue[j] * derOfValueWRTCoefficient[j][v_i]) * quad[pt].weight() * integrationElement;

Oliver Sander
committed
}
}
}

Oliver Sander
committed
// transform to coordinates on the tangent space
localGradient.resize(embeddedLocalGradient.size());
for (size_t i=0; i<localGradient.size(); i++)
localSolution[i].orthonormalFrame().mv(embeddedLocalGradient[i], localGradient[i]);
}
#endif //#ifndef COSSERAT_ENERGY_LOCAL_STIFFNESS_HH