Skip to content
Snippets Groups Projects
planarcosseratshelltest.cc 9.74 KiB
Newer Older
#include <config.h>

// Includes for the ADOL-C automatic differentiation library
// Need to come before (almost) all others.
#include <adolc/adouble.h>
#include <dune/fufem/utilities/adolcnamespaceinjections.hh>

#include <dune/common/parametertree.hh>
#include <dune/common/bitsetvector.hh>

#include <dune/grid/io/file/gmshreader.hh>
#include <dune/grid/uggrid.hh>

#include <dune/functions/functionspacebases/lagrangebasis.hh>
#include <dune/functions/functionspacebases/compositebasis.hh>
#include <dune/functions/functionspacebases/powerbasis.hh>
#include <dune/functions/functionspacebases/interpolate.hh>
#include <dune/functions/functionspacebases/subspacebasis.hh>

#include <dune/fufem/boundarypatch.hh>

#include <dune/gfe/assemblers/localgeodesicfeadolcstiffness.hh>
#include <dune/gfe/assemblers/localintegralenergy.hh>
#include <dune/gfe/assemblers/mixedgfeassembler.hh>
#include <dune/gfe/assemblers/sumenergy.hh>
#include <dune/gfe/densities/planarcosseratshelldensity.hh>
#include <dune/gfe/localgeodesicfefunction.hh>
#include <dune/gfe/mixedriemanniantrsolver.hh>
#include <dune/gfe/neumannenergy.hh>

// Dimension of the world space
const int dimworld = 3;

// Order of the approximation space for the displacement
const int displacementOrder = 2;

// Order of the approximation space for the microrotations
const int rotationOrder = 1;

using namespace Dune;

int main (int argc, char *argv[])
{
  MPIHelper::instance(argc, argv);

  using Configuration = TupleVector<std::vector<RealTuple<double,3> >, std::vector<Rotation<double,3> > >;

  // solver settings
  const double tolerance                = 1e-4;
  const int maxSolverSteps              = 20;
  const double initialTrustRegionRadius = 3.125;
  const int multigridIterations         = 100;
  const int baseIterations              = 100;
  const double mgTolerance              = 1e-10;
  const double baseTolerance            = 1e-8;

  /////////////////////////////////////////
  //   Create the grid
  /////////////////////////////////////////

  const int dim = 2;
  using GridType = UGGrid<dim>;

  const std::string path = std::string(DUNE_GRID_EXAMPLE_GRIDS_PATH) + "gmsh/";
  auto grid = GmshReader<GridType>::read(path + "hybrid-testgrid-2d.msh");

  //grid->globalRefine(1);

  using GridView = GridType::LeafGridView;
  GridView gridView = grid->leafGridView();

  // ///////////////////////////////////////////
  //  Construct all needed function space bases
  // ///////////////////////////////////////////
  using namespace Dune::Indices;
  using namespace Functions::BasisFactory;

  const int dimRotation = Rotation<double,dim>::embeddedDim;
  auto compositeBasis = makeBasis(
    gridView,
    composite(
      power<dimworld>(
        lagrange<displacementOrder>()
        ),
      power<dimRotation>(
        lagrange<rotationOrder>()
        )
      ));
  using CompositeBasis = decltype(compositeBasis);

  using DeformationFEBasis = Functions::LagrangeBasis<GridView,displacementOrder>;
  using OrientationFEBasis = Functions::LagrangeBasis<GridView,rotationOrder>;

  DeformationFEBasis deformationFEBasis(gridView);
  OrientationFEBasis orientationFEBasis(gridView);

  //////////////////////////////////////////////
  //  Determine Dirichlet dofs
  //////////////////////////////////////////////

  // This identityBasis is only needed to compute the positions of the Lagrange points
  auto identityBasis = makeBasis(
    gridView,
    composite(
      power<dim>(
        lagrange<displacementOrder>()
        ),
      power<dim>(
        lagrange<rotationOrder>()
        )
      ));
  auto deformationPowerBasis = Functions::subspaceBasis(identityBasis, _0);
  auto rotationPowerBasis = Functions::subspaceBasis(identityBasis, _1);

  MultiTypeBlockVector<BlockVector<FieldVector<double,dim> >,BlockVector<FieldVector<double,dim> > > identity;
  Functions::interpolate(deformationPowerBasis, identity, [&](FieldVector<double,dim> x){
    return x;
  });
  Functions::interpolate(rotationPowerBasis, identity, [&](FieldVector<double,dim> x){
    return x;
  });

  BitSetVector<RealTuple<double,dimworld>::TangentVector::dimension> deformationDirichletDofs(deformationFEBasis.size(), false);
  BitSetVector<Rotation<double,dimworld>::TangentVector::dimension> orientationDirichletDofs(orientationFEBasis.size(), false);

  const GridView::IndexSet& indexSet = gridView.indexSet();

  // Make predicate function that computes which vertices are on the Dirichlet boundary, based on the vertex positions.
  auto isDirichlet = [](FieldVector<double,dim> coordinate)
                     {
                       return coordinate[0] < 0.01;
                     };

  for (size_t i=0; i<deformationFEBasis.size(); i++)
    deformationDirichletDofs[i] = isDirichlet(identity[_0][i]);

  for (size_t i=0; i<orientationFEBasis.size(); i++)
    orientationDirichletDofs[i] = isDirichlet(identity[_1][i]);

  ///////////////////////////////////////////
  //  Determine Neumann dofs and values
  ///////////////////////////////////////////

  std::function<bool(FieldVector<double,dim>)> isNeumann
    = [](FieldVector<double,dim> coordinate)
      {
        return coordinate[0] > 0.99 && coordinate[1] > 0.49;
      };

  BitSetVector<1> neumannVertices(gridView.size(dim), false);

  for (auto&& vertex : vertices(gridView))
    neumannVertices[indexSet.index(vertex)] = isNeumann(vertex.geometry().corner(0));

  auto neumannBoundary = std::make_shared<BoundaryPatch<GridType::LeafGridView> >(gridView, neumannVertices);

  FieldVector<double,dimworld> values_ = {0,0,60};
  auto neumannFunction = [&](FieldVector<double, dim>){
                           return values_;
                         };

  ////////////////////////////
  //  Initial iterate
  ////////////////////////////

  Configuration x;

  x[_0].resize(compositeBasis.size({0}));
  x[_1].resize(compositeBasis.size({1}));

  for (std::size_t i=0; i<x[_0].size(); ++i)
    x[_0][i] = {identity[_0][i][0], identity[_0][i][1], 0.0};

  for (auto& rotation : x[_1])
    rotation = Rotation<double,dimworld>::identity();

  //////////////////////////////////
  //  Parameters for the problem
  //////////////////////////////////

  ParameterTree parameters;
  parameters["thickness"] = "0.06";
  parameters["mu"] = "2.7191e+4";
  parameters["lambda"] = "4.4364e+4";
  parameters["mu_c"] = "0";
  parameters["L_c"] = "0.06";
  parameters["q"] = "2";
  parameters["kappa"] = "1";         // Shear correction factor

  //////////////////////////////
  //  Create an assembler
  //////////////////////////////

  // The target space, with 'double' and 'adouble' as number types
  using RigidBodyMotion = GFE::ProductManifold<RealTuple<double,dimworld>,Rotation<double,dimworld> >;
  using ARigidBodyMotion = typename RigidBodyMotion::template rebind<adouble>::other;
  auto sumEnergy = std::make_shared<GFE::SumEnergy<CompositeBasis, RealTuple<adouble,dimworld>,Rotation<adouble,dimworld> > >();

  // The Cosserat shell energy
  using ScalarDeformationLocalFiniteElement = decltype(compositeBasis.localView().tree().child(_0,0).finiteElement());
  using ScalarRotationLocalFiniteElement = decltype(compositeBasis.localView().tree().child(_1,0).finiteElement());

  using AInterpolationRule = std::tuple<LocalGeodesicFEFunction<dim, double, ScalarDeformationLocalFiniteElement, RealTuple<adouble,3> >,
      LocalGeodesicFEFunction<dim, double, ScalarRotationLocalFiniteElement, Rotation<adouble,3> > >;

  auto cosseratDensity = std::make_shared<GFE::PlanarCosseratShellDensity<GridType::Codim<0>::Entity::Geometry::LocalCoordinate, adouble> >(parameters);

  auto planarCosseratShellEnergy = std::make_shared<GFE::LocalIntegralEnergy<CompositeBasis,AInterpolationRule,ARigidBodyMotion> >(cosseratDensity);

  sumEnergy->addLocalEnergy(planarCosseratShellEnergy);

  // The Neumann surface load term
  auto neumannEnergy = std::make_shared<GFE::NeumannEnergy<CompositeBasis, RealTuple<adouble,dimworld>, Rotation<adouble,dimworld> > >(neumannBoundary,neumannFunction);
  sumEnergy->addLocalEnergy(neumannEnergy);

  LocalGeodesicFEADOLCStiffness<CompositeBasis,RigidBodyMotion> localGFEADOLCStiffness(sumEnergy);
  MixedGFEAssembler<CompositeBasis,RigidBodyMotion> mixedAssembler(compositeBasis, localGFEADOLCStiffness);

  MixedRiemannianTrustRegionSolver<GridType,
      CompositeBasis,
      DeformationFEBasis, RealTuple<double,dimworld>,
      OrientationFEBasis, Rotation<double,dimworld> > solver;
  solver.setup(*grid,
               &mixedAssembler,
               deformationFEBasis,
               orientationFEBasis,
               x,
               deformationDirichletDofs,
               orientationDirichletDofs,
               tolerance,
               maxSolverSteps,
               initialTrustRegionRadius,
               multigridIterations,
               mgTolerance,
               3, 3, 1, // Multigrid V-cycle
               baseIterations,
               baseTolerance,
               false);

  solver.setScaling({1,1,1,0.01,0.01,0.01});

  solver.setInitialIterate(x);
  solver.solve();
  x = solver.getSol();

  ////////////////////////////////////////////////
  //  Check if solver returns the expected values
  ////////////////////////////////////////////////

  size_t expectedFinalIteration = 9;
  double expectedEnergy = -6.11748397;

  if (solver.getStatistics().finalIteration != expectedFinalIteration)
  {
    std::cerr << "Trust-region solver did " << solver.getStatistics().finalIteration+1
              << " iterations, instead of the expected '" << expectedFinalIteration+1 << "'!" << std::endl;
    return 1;
  }

  if ( std::abs(solver.getStatistics().finalEnergy - expectedEnergy) > 1e-7)
  {
    std::cerr << std::setprecision(9);
    std::cerr << "Final energy is " << solver.getStatistics().finalEnergy
              << " but '" << expectedEnergy << "' was expected!" << std::endl;
    return 1;
  }

  return 0;
}