Skip to content
Snippets Groups Projects
mixedriemannianpnsolvertest.cc 11.90 KiB
#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/bitsetvector.hh>
#include <dune/common/typetraits.hh>
#include <dune/common/tuplevector.hh>

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

#include <dune/fufem/boundarypatch.hh>
#include <dune/fufem/functiontools/boundarydofs.hh>

#include <dune/grid/utility/structuredgridfactory.hh>
#include <dune/grid/uggrid.hh>

#include <dune/gfe/assemblers/geodesicfeassembler.hh>
#include <dune/gfe/assemblers/geodesicfeassemblerwrapper.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/mixedriemannianpnsolver.hh>
#include <dune/gfe/neumannenergy.hh>
#include <dune/gfe/riemannianpnsolver.hh>
#include <dune/gfe/spaces/productmanifold.hh>
#include <dune/gfe/spaces/realtuple.hh>
#include <dune/gfe/spaces/rotation.hh>

/** \file
 * \brief This test compares the MixedRiemannianPNSolver to the RiemannianPNSolver.
 */

// grid dimension
const int gridDim = 2;

// target dimension
const int dim = 3;

//order of the finite element spaces, they need to be the same to compare!
const int displacementOrder = 2;
const int rotationOrder = displacementOrder;

using namespace Dune;
using namespace Indices;

//differentiation method: ADOL-C
using ValueType = adouble;

//Types for the mixed space
using DisplacementVector = std::vector<RealTuple<double,dim> >;
using RotationVector =  std::vector<Rotation<double,dim> >;
using Vector = TupleVector<DisplacementVector, RotationVector>;
using BlockTupleVector = TupleVector<BlockVector<RealTuple<double,dim> >, BlockVector<Rotation<double,dim> > >;
const int dimRotationTangent = Rotation<double,dim>::TangentVector::dimension;


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

  /////////////////////////////////////////////////////////////////////////
  //    Create the grid
  /////////////////////////////////////////////////////////////////////////
  using GridType = UGGrid<gridDim>;
  auto grid = StructuredGridFactory<GridType>::createCubeGrid({0.0,0.0}, {1.0,1.0}, {2,2});
  grid->globalRefine(2);
  grid->loadBalance();

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

  /////////////////////////////////////////////////////////////////////////
  //  Create a composite basis and a Lagrange FE basis
  /////////////////////////////////////////////////////////////////////////

  using namespace Functions::BasisFactory;

  auto compositeBasis = makeBasis(
    gridView,
    composite(
      power<dim>(
        lagrange<displacementOrder>()
        ),
      power<dim>(
        lagrange<rotationOrder>()
        )
      ));

  using CompositeBasis = decltype(compositeBasis);

  using DeformationFEBasis = Functions::LagrangeBasis<GridView,displacementOrder>;
  DeformationFEBasis deformationFEBasis(gridView);

  /////////////////////////////////////////////////////////////////////////
  //  Create the Neumann and Dirichlet boundary
  /////////////////////////////////////////////////////////////////////////

  std::function<bool(FieldVector<double,gridDim>)> isNeumann = [](FieldVector<double,gridDim> coordinate) {
                                                                 return coordinate[0] > 0.99; //Neumann for upper boundary
                                                               };
  std::function<bool(FieldVector<double,gridDim>)> isDirichlet = [](FieldVector<double,gridDim> coordinate) {
                                                                   return coordinate[0] < 0.01; //Dircichlet for lower boundary
                                                                 };

  BitSetVector<1> neumannVertices(gridView.size(gridDim), false);
  BitSetVector<1> dirichletVertices(gridView.size(gridDim), false);
  const GridView::IndexSet& indexSet = gridView.indexSet();

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

  auto neumannBoundary = std::make_shared<BoundaryPatch<GridView> >(gridView, neumannVertices);
  BoundaryPatch<GridView> dirichletBoundary(gridView, dirichletVertices);

  BitSetVector<1> dirichletNodes(compositeBasis.size({0}),false);
#if DUNE_VERSION_GTE(DUNE_FUFEM, 2, 10)
  Fufem::markBoundaryPatchDofs(dirichletBoundary, deformationFEBasis, dirichletNodes);
#else
  constructBoundaryDofs(dirichletBoundary, deformationFEBasis, dirichletNodes);
#endif

  typedef MultiTypeBlockVector<std::vector<FieldVector<double,dim> >, std::vector<FieldVector<double,dimRotationTangent> > > VectorForBit;
  using BitVector = Solvers::DefaultBitVector_t<VectorForBit>;

  BitVector dirichletDofs;
  dirichletDofs[_0].resize(compositeBasis.size({0}));
  dirichletDofs[_1].resize(compositeBasis.size({1}));
  for (size_t i = 0; i < compositeBasis.size({0}); i++) {
    for (size_t j = 0; j < dim; j++)
      dirichletDofs[_0][i][j] = dirichletNodes[i][0];
    for (size_t j = 0; j < dimRotationTangent; j++)
      dirichletDofs[_1][i][j] = dirichletNodes[i][0];
  }

  /////////////////////////////////////////////////////////////////////////
  //  Create the energy functions with their parameters
  /////////////////////////////////////////////////////////////////////////

  //Surface-Cosserat-Energy-Parameters
  ParameterTree parameters;
  parameters["thickness"] = "1";
  parameters["mu"] = "2.7191e+4";
  parameters["lambda"] = "4.4364e+4";
  parameters["mu_c"] = "0";
  parameters["L_c"] = "0.01";
  parameters["q"] = "2";
  parameters["kappa"] = "1";


  FieldVector<double,dim> values_ = {3e4,2e4,1e4};
  auto neumannFunction = [&](FieldVector<double, gridDim>){
                           return values_;
                         };

  // The target space, with 'double' and 'adouble' as number types
  using RBM = GFE::ProductManifold<RealTuple<double,dim>,Rotation<double,dim> >;
  using ARBM = typename RBM::template rebind<adouble>::other;

  // The total energy
  auto sumEnergy = std::make_shared<GFE::SumEnergy<CompositeBasis, RealTuple<adouble,dim>,Rotation<adouble,dim> > >();

  // 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<gridDim, double, ScalarDeformationLocalFiniteElement, RealTuple<adouble,3> >,
      LocalGeodesicFEFunction<gridDim, 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,ARBM> >(cosseratDensity);

  sumEnergy->addLocalEnergy(planarCosseratShellEnergy);

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

  // The local assembler
  LocalGeodesicFEADOLCStiffness<CompositeBasis,RBM> mixedLocalGFEADOLCStiffness(sumEnergy);

  // The global assembler
  MixedGFEAssembler<CompositeBasis, RBM> mixedAssembler(compositeBasis, mixedLocalGFEADOLCStiffness);

  using GFEAssemblerWrapper = GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, RBM>;
  GFEAssemblerWrapper assembler(&mixedAssembler, deformationFEBasis);

  /////////////////////////////////////////////////////////////////////////
  //  Prepare the iterate x where we want to assemble - identity in 2D with z = 0
  /////////////////////////////////////////////////////////////////////////
  auto deformationPowerBasis = makeBasis(
    gridView,
    power<gridDim>(
      lagrange<displacementOrder>()
      ));
  BlockVector<FieldVector<double,gridDim> > identity(compositeBasis.size({0}));
  Functions::interpolate(deformationPowerBasis, identity, [](FieldVector<double,gridDim> x){
    return x;
  });
  BlockVector<FieldVector<double,dim> > initialDeformation(compositeBasis.size({0}));
  initialDeformation = 0;

  Vector x;
  x[_0].resize(compositeBasis.size({0}));
  x[_1].resize(compositeBasis.size({1}));
  std::vector<RBM> xRBM(compositeBasis.size({0}));
  BitSetVector<RBM::TangentVector::dimension> dirichletDofsRBM(compositeBasis.size({0}), false);

  for (std::size_t i = 0; i < compositeBasis.size({0}); i++) {
    for (int j = 0; j < gridDim; j++)
      initialDeformation[i][j] = identity[i][j];
    x[_0][i] = initialDeformation[i];
    xRBM[i][_0] = x[_0][i];
    for (int j = 0; j < dim; j ++) { // Displacement part
      dirichletDofsRBM[i][j] = dirichletDofs[_0][i][j];
    }
    xRBM[i][_1] = x[_1][i]; // Rotation part
    for (int j = dim; j < RBM::TangentVector::dimension; j ++)
      dirichletDofsRBM[i][j] = dirichletDofs[_1][i][j-dim];
  }

  //////////////////////////////////////////////////////////////////////////////
  //  Create a MixedRiemannianPNSolver and a normal RiemannianPNSolver
  //  and compare one solver step!
  //////////////////////////////////////////////////////////////////////////////

  const double tolerance = 1e-7;
  const int maxSolverSteps = 1;
  const double initialRegularization = 100;
  const bool instrumented = false;
  GFE::MixedRiemannianProximalNewtonSolver<CompositeBasis, DeformationFEBasis, RealTuple<double,dim>, DeformationFEBasis, Rotation<double,dim>, BitVector> mixedSolver;
  mixedSolver.setup(*grid,
                    &mixedAssembler,
                    x,
                    dirichletDofs,
                    tolerance,
                    maxSolverSteps,
                    initialRegularization,
                    instrumented);
  mixedSolver.setInitialIterate(x);
  mixedSolver.solve();
  x = mixedSolver.getSol();

  RiemannianProximalNewtonSolver<DeformationFEBasis, RBM, GFEAssemblerWrapper> solver;
  solver.setup(*grid,
               &assembler,
               xRBM,
               dirichletDofsRBM,
               tolerance,
               maxSolverSteps,
               initialRegularization,
               instrumented);
  solver.setInitialIterate(xRBM);
  solver.solve();
  xRBM = solver.getSol();

  BlockTupleVector xMixed;
  BlockTupleVector xNotMixed;
  xNotMixed[_0].resize(compositeBasis.size({0}));
  xNotMixed[_1].resize(compositeBasis.size({1}));
  xMixed[_0].resize(compositeBasis.size({0}));
  xMixed[_1].resize(compositeBasis.size({1}));
  for (std::size_t i = 0; i < xRBM.size(); i++)
  {
    xNotMixed[_0][i] = xRBM[i][_0];
    xNotMixed[_1][i] = xRBM[i][_1];
    xMixed[_0][i] = x[_0][i];
    xMixed[_1][i] = x[_1][i];
    auto difference0 = xMixed[_0][i].globalCoordinates();
    auto difference1 = xMixed[_1][i].globalCoordinates();
    difference0 -= xNotMixed[_0][i].globalCoordinates();
    difference1 -= xNotMixed[_1][i].globalCoordinates();
    if (difference0.two_norm() > 1e-1 || difference1.two_norm() > 1e-1) {
      std::cerr << std::setprecision(9);
      std::cerr << "At index " << i << " the solution calculated by the MixedRiemannianPNSolver is "
                << xMixed[_0][i] << " and " << xMixed[_1][i] << " but "
                << xNotMixed[_0][i] << " and " << xNotMixed[_1][i]
                << " (calculated by the RiemannianProximalNewtonSolver) was expected!" << std::endl;
      return 1;
    }
  }
}