Skip to content
Snippets Groups Projects
filmonsubstratetest.cc 20.31 KiB
#include <iostream>
#include <fstream>

#include <config.h>

// Includes for the ADOL-C automatic differentiation library
// Need to come before (almost) all others.
#include <adolc/adouble.h>
#include <adolc/drivers/drivers.h>    // use of "Easy to Use" drivers
#include <adolc/taping.h>

#include <dune/fufem/utilities/adolcnamespaceinjections.hh>

#include <dune/common/bitsetvector.hh>
#include <dune/common/indices.hh>
#include <dune/common/parametertree.hh>
#include <dune/common/parametertreeparser.hh>
#include <dune/common/timer.hh>
#include <dune/common/tuplevector.hh>
#include <dune/common/version.hh>

#include <dune/elasticity/materials/exphenckydensity.hh>
#include <dune/elasticity/materials/henckydensity.hh>
#include <dune/elasticity/materials/mooneyrivlindensity.hh>
#include <dune/elasticity/materials/neohookedensity.hh>
#include <dune/elasticity/materials/stvenantkirchhoffdensity.hh>

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

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

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

#include <dune/gfe/cosseratvtkwriter.hh>
#include <dune/gfe/assemblers/localintegralenergy.hh>
#include <dune/gfe/assemblers/mixedgfeassembler.hh>
#include <dune/gfe/assemblers/localgeodesicfeadolcstiffness.hh>
#include <dune/gfe/neumannenergy.hh>
#include <dune/gfe/assemblers/surfacecosseratenergy.hh>
#include <dune/gfe/assemblers/sumenergy.hh>

#if MIXED_SPACE
#include <dune/gfe/mixedriemanniantrsolver.hh>
#else
#include <dune/gfe/assemblers/geodesicfeassemblerwrapper.hh>
#include <dune/gfe/riemannianpnsolver.hh>
#include <dune/gfe/riemanniantrsolver.hh>
#include <dune/gfe/spaces/productmanifold.hh>
#endif

#include <dune/istl/multitypeblockvector.hh>

#include <dune/solvers/solvers/iterativesolver.hh>
#include <dune/solvers/norms/energynorm.hh>


const int dim = 3;
const int targetDim = 3;

const int displacementOrder = 2;
#if MIXED_SPACE
const int rotationOrder = 1;
#else
const int rotationOrder = 2;
#endif

const int stressFreeDataOrder = 2;

#if !MIXED_SPACE
static_assert(displacementOrder==rotationOrder, "displacement and rotation order do not match!");
#endif

//differentiation method
typedef adouble ValueType;

using namespace Dune;

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

  if (mpiHelper.rank()==0) {
    std::cout << "DISPLACEMENTORDER = " << displacementOrder << ", ROTATIONORDER = " << rotationOrder << std::endl;
#if MIXED_SPACE
    std::cout << "MIXED_SPACE = 1" << std::endl;
#else
    std::cout << "MIXED_SPACE = 0" << std::endl;
#endif
  }

  // read solver settings
  const double tolerance                = 1e-7;
  const int maxSolverSteps              = 100;
  const double initialTrustRegionRadius = 1;
  const int multigridIterations         = 100;
  const int nu1                         = 3;
  const int nu2                         = 3;
  const int mu                          = 1;
  const int baseIterations              = 100;
  const double mgTolerance              = 1e-7;
  const double baseTolerance            = 1e-7;

  /////////////////////////////////////////////////////////////
  //                      CREATE THE GRID
  /////////////////////////////////////////////////////////////
  using GridType = UGGrid<dim>;

  FieldVector<double,dim> lower(0), upper({4.0,1.0,1.0});

  std::array<unsigned int,dim> elements = {8,2,2};
  auto grid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements);

  grid->setRefinementType(GridType::RefinementType::COPY);

  grid->loadBalance();

  if (mpiHelper.rank()==0)
    std::cout << "There are " << grid->leafGridView().comm().size() << " processes" << std::endl;

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

  /////////////////////////////////////////////////////////////
  //                      DATA TYPES
  /////////////////////////////////////////////////////////////

  using namespace Dune::Indices;

  typedef std::vector<RealTuple<double,dim> > DisplacementVector;
  typedef std::vector<Rotation<double,dim> > RotationVector;
  const int dimRotation = Rotation<double,dim>::embeddedDim;
  typedef TupleVector<DisplacementVector, RotationVector> SolutionType;
  /////////////////////////////////////////////////////////////
  //                      FUNCTION SPACE
  /////////////////////////////////////////////////////////////

  using namespace Dune::Functions::BasisFactory;
  auto compositeBasis = makeBasis(
    gridView,
    composite(
      power<dim>(
        lagrange<displacementOrder>()
        ),
      power<dimRotation>(
        lagrange<rotationOrder>()
        )
      ));

  auto deformationPowerBasis = makeBasis(
    gridView,
    power<dim>(
      lagrange<displacementOrder>()
      ));

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

  using CompositeBasis = decltype(compositeBasis);

  /////////////////////////////////////////////////////////////
  //                      BOUNDARY DATA
  /////////////////////////////////////////////////////////////

  auto dirichletPredicate = [](const auto& x) {
                              return x[0] < 1e-4;
                            };
  auto neumannPredicate = [](const auto& x) {
                            return x[0] > 4-1e-4;
                          };
  auto surfaceShellPredicate = [](const auto& x) {
                                 return x[2] > 1-1e-4;
                               };

  BitSetVector<1> dirichletVerticesX(gridView.size(dim), false);
  BitSetVector<1> dirichletVerticesY(gridView.size(dim), false);
  BitSetVector<1> dirichletVerticesZ(gridView.size(dim), false);

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

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

  for (auto&& v : vertices(gridView))
  {
    bool isDirichlet = dirichletPredicate(v.geometry().corner(0));
    dirichletVerticesX[indexSet.index(v)] = isDirichlet;
    dirichletVerticesY[indexSet.index(v)] = isDirichlet;
    dirichletVerticesZ[indexSet.index(v)] = isDirichlet;

    neumannVertices[indexSet.index(v)] = neumannPredicate(v.geometry().corner(0));

    surfaceShellVertices[indexSet.index(v)] = surfaceShellPredicate(v.geometry().corner(0));
  }

  BoundaryPatch<GridView> dirichletBoundaryX(gridView, dirichletVerticesX);
  BoundaryPatch<GridView> dirichletBoundaryY(gridView, dirichletVerticesY);
  BoundaryPatch<GridView> dirichletBoundaryZ(gridView, dirichletVerticesZ);
  auto neumannBoundary = std::make_shared<BoundaryPatch<GridView> >(gridView, neumannVertices);
  BoundaryPatch<GridView> surfaceShellBoundary(gridView, surfaceShellVertices);
  std::cout << "On rank " << mpiHelper.rank() << ": Dirichlet boundary has [" << dirichletBoundaryX.numFaces() <<
    ", " << dirichletBoundaryY.numFaces() <<
    ", " << dirichletBoundaryZ.numFaces() <<"] faces\n";
  std::cout << "On rank " << mpiHelper.rank() << ": Neumann boundary has " << neumannBoundary->numFaces() << " faces\n";
  std::cout << "On rank " << mpiHelper.rank() << ": Shell boundary has " << surfaceShellBoundary.numFaces() << " faces\n";

  BitSetVector<1> dirichletNodesX(compositeBasis.size({0}),false);
  BitSetVector<1> dirichletNodesY(compositeBasis.size({0}),false);
  BitSetVector<1> dirichletNodesZ(compositeBasis.size({0}),false);
  BitSetVector<1> surfaceShellNodes(compositeBasis.size({1}),false);
#if DUNE_VERSION_GTE(DUNE_FUFEM, 2, 10)
  Fufem::markBoundaryPatchDofs(dirichletBoundaryX,deformationFEBasis,dirichletNodesX);
  Fufem::markBoundaryPatchDofs(dirichletBoundaryY,deformationFEBasis,dirichletNodesY);
  Fufem::markBoundaryPatchDofs(dirichletBoundaryZ,deformationFEBasis,dirichletNodesZ);
  Fufem::markBoundaryPatchDofs(surfaceShellBoundary,orientationFEBasis,surfaceShellNodes);
#else
  constructBoundaryDofs(dirichletBoundaryX,deformationFEBasis,dirichletNodesX);
  constructBoundaryDofs(dirichletBoundaryY,deformationFEBasis,dirichletNodesY);
  constructBoundaryDofs(dirichletBoundaryZ,deformationFEBasis,dirichletNodesZ);
  constructBoundaryDofs(surfaceShellBoundary,orientationFEBasis,surfaceShellNodes);
#endif

  //Create BitVector matching the tangential space
  const int dimRotationTangent = Rotation<double,dim>::TangentVector::dimension;
  typedef MultiTypeBlockVector<std::vector<FieldVector<double,dim> >, std::vector<FieldVector<double,dimRotationTangent> > > VectorForBit;
  typedef Solvers::DefaultBitVector_t<VectorForBit> BitVector;

  BitVector dirichletDofs;
  dirichletDofs[_0].resize(compositeBasis.size({0}));
  dirichletDofs[_1].resize(compositeBasis.size({1}));
  for (size_t i = 0; i < compositeBasis.size({0}); i++) {
    dirichletDofs[_0][i][0] = dirichletNodesX[i][0];
    dirichletDofs[_0][i][1] = dirichletNodesY[i][0];
    dirichletDofs[_0][i][2] = dirichletNodesZ[i][0];
  }
  for (size_t i = 0; i < compositeBasis.size({1}); i++) {
    for (int j = 0; j < dimRotationTangent; j++) {
      dirichletDofs[_1][i][j] = not surfaceShellNodes[i][0];
    }
  }

  /////////////////////////////////////////////////////////////
  //                      INITIAL DATA
  /////////////////////////////////////////////////////////////

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

  BlockVector<FieldVector<double,dim> > identity(compositeBasis.size({0}));
  Dune::Functions::interpolate(deformationPowerBasis, identity, [](FieldVector<double,dim> x){
    return x;
  });

  for (std::size_t i=0; i<x[_0].size(); ++i)
    x[_0][i] = identity[i];

  for (std::size_t i=0; i<x[_1].size(); ++i)
    x[_1][i] = Rotation<double,dim>::identity();


  /////////////////////////////////////////////////////////////
  //               STRESS-FREE SURFACE SHELL DATA
  /////////////////////////////////////////////////////////////
  auto stressFreeFEBasis = makeBasis(
    gridView,
    power<dim>(
      lagrange<stressFreeDataOrder>(),
      blockedInterleaved()
      ));

  GlobalIndexSet<GridView> globalVertexIndexSet(gridView,dim);
  BlockVector<FieldVector<double,dim> > stressFreeShellVector(stressFreeFEBasis.size());

  // Read grid deformation from deformation function
  auto gridDeformation = [](FieldVector<double,dim> x){
                           return x;
                         };
  Functions::interpolate(stressFreeFEBasis, stressFreeShellVector, gridDeformation);

  auto stressFreeShellFunction = Dune::Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(stressFreeFEBasis, stressFreeShellVector);

  /////////////////////////////////////////////////////////////
  //                      MAIN HOMOTOPY LOOP
  /////////////////////////////////////////////////////////////
  FieldVector<double,dim> neumannValues {3e6,0,0};
  std::cout << "Neumann values: " << neumannValues << std::endl;

  // Function for the Cosserat material parameters
  auto fThickness = [](const auto& x) {
                      return 0.1;
                    };
  auto fLame = [](const auto& x) -> FieldVector<double, 2> {
                 return {1e7, 1e7};
               };

  //////////////////////////////////////////////////////////////
  //   Create an assembler for the energy functional
  //////////////////////////////////////////////////////////////

  // A constant vector-valued function, for simple Neumann boundary values
  auto neumannFunction = std::function<FieldVector<double,dim>(FieldVector<double,dim>)>([&](FieldVector<double,dim> ) {
    return neumannValues * (-1.0);
  });

  ///////////////////////////////////////////////////
  //   Create the energy functional
  ///////////////////////////////////////////////////

  ParameterTree materialParameters;

  materialParameters["mooneyrivlin_energy"] = "ciarlet";
  materialParameters["mooneyrivlin_a"] = "1e6";
  materialParameters["mooneyrivlin_b"] = "1e6";
  materialParameters["mooneyrivlin_c"] = "1e6";

  materialParameters["mu_c"] = "0.0";
  materialParameters["L_c"] = "1e-4";
  materialParameters["b1"] = "1.0";
  materialParameters["b2"] = "1.0";
  materialParameters["b3"] = "1.0";

  std::shared_ptr<Elasticity::LocalDensity<dim,ValueType> > elasticDensity;
  elasticDensity = std::make_shared<Elasticity::MooneyRivlinDensity<dim,ValueType> >(materialParameters);

  using ActiveRigidBodyMotion = GFE::ProductManifold<RealTuple<adouble,dim>, Rotation<adouble,dim> >;

  // Select which type of geometric interpolation to use
  using LocalDeformationInterpolationRule = LocalGeodesicFEFunction<dim, GridType::ctype, decltype(deformationFEBasis.localView().tree().finiteElement()), RealTuple<adouble,dim> >;
  using LocalOrientationInterpolationRule = LocalGeodesicFEFunction<dim, GridType::ctype, decltype(orientationFEBasis.localView().tree().finiteElement()), Rotation<adouble,dim> >;

  using LocalInterpolationRule = std::tuple<LocalDeformationInterpolationRule,LocalOrientationInterpolationRule>;

  auto elasticEnergy = std::make_shared<GFE::LocalIntegralEnergy<CompositeBasis, LocalInterpolationRule, ActiveRigidBodyMotion> >(elasticDensity);
  auto neumannEnergy = std::make_shared<GFE::NeumannEnergy<CompositeBasis, RealTuple<ValueType,targetDim>, Rotation<ValueType,dim> > >(neumannBoundary,neumannFunction);

  auto surfaceCosseratEnergy = std::make_shared<GFE::SurfaceCosseratEnergy<
      decltype(stressFreeShellFunction), CompositeBasis, RealTuple<ValueType,dim>, Rotation<ValueType,dim> > >(
    materialParameters,
    &surfaceShellBoundary,
    stressFreeShellFunction,
    fThickness,
    fLame);

  using RBM = GFE::ProductManifold<RealTuple<double, dim>,Rotation<double,dim> >;

  GFE::SumEnergy<CompositeBasis, RealTuple<ValueType,targetDim>, Rotation<ValueType,targetDim> > sumEnergy;
  sumEnergy.addLocalEnergy(neumannEnergy);
  sumEnergy.addLocalEnergy(elasticEnergy);
  sumEnergy.addLocalEnergy(surfaceCosseratEnergy);

  LocalGeodesicFEADOLCStiffness<CompositeBasis,RBM> localGFEADOLCStiffness(&sumEnergy);
  MixedGFEAssembler<CompositeBasis,
      RealTuple<double,dim>,
      Rotation<double,dim> > mixedAssembler(compositeBasis, &localGFEADOLCStiffness);

  ////////////////////////////////////////////////////////
  //   Set Dirichlet values
  ////////////////////////////////////////////////////////

  BitSetVector<RealTuple<double,dim>::TangentVector::dimension> deformationDirichletDofs(dirichletDofs[_0].size(), false);
  for (std::size_t i = 0; i < dirichletDofs[_0].size(); i++)
    for (int j = 0; j < RealTuple<double,dim>::TangentVector::dimension; j++)
      deformationDirichletDofs[i][j] = dirichletDofs[_0][i][j];

  BitSetVector<Rotation<double,dim>::TangentVector::dimension> orientationDirichletDofs(dirichletDofs[_1].size(), false);
  for (std::size_t i = 0; i < dirichletDofs[_1].size(); i++)
    for (int j = 0; j < Rotation<double,dim>::TangentVector::dimension; j++)
      orientationDirichletDofs[i][j] = dirichletDofs[_1][i][j];

#if !MIXED_SPACE
  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 < dim; j ++) {   // Displacement part
      xRBM[i][_0].globalCoordinates()[j] = x[_0][i][j];
      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];
  }
  typedef GFE::GeodesicFEAssemblerWrapper<CompositeBasis, DeformationFEBasis, RBM, RealTuple<double, dim>, Rotation<double,dim> > GFEAssemblerWrapper;
  GFEAssemblerWrapper assembler(&mixedAssembler, deformationFEBasis);
#endif

  ///////////////////////////////////////////////////
  //   Create the chosen solver and solve!
  ///////////////////////////////////////////////////

  std::string solverType = "trustRegion";

  std::size_t finalIteration;
  double finalEnergy;

  if (solverType == "trustRegion")
  {
#if MIXED_SPACE
    MixedRiemannianTrustRegionSolver<GridType, CompositeBasis, DeformationFEBasis, RealTuple<double,dim>, OrientationFEBasis, Rotation<double,dim> > solver;
    solver.setup(*grid,
                 &mixedAssembler,
                 deformationFEBasis,
                 orientationFEBasis,
                 x,
                 deformationDirichletDofs,
                 orientationDirichletDofs,
                 tolerance,
                 maxSolverSteps,
                 initialTrustRegionRadius,
                 multigridIterations,
                 mgTolerance,
                 mu, nu1, nu2,
                 baseIterations,
                 baseTolerance,
                 false /* instrumented */);

    solver.setScaling({1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
    solver.setInitialIterate(x);
    solver.solve();
    x = solver.getSol();
#else
    RiemannianTrustRegionSolver<DeformationFEBasis, RBM, GFEAssemblerWrapper> solver;
    solver.setup(*grid,
                 &assembler,
                 xRBM,
                 dirichletDofsRBM,
                 tolerance,
                 maxSolverSteps,
                 initialTrustRegionRadius,
                 multigridIterations,
                 mgTolerance,
                 mu, nu1, nu2,
                 baseIterations,
                 baseTolerance,
                 false /* instrumented */);

    solver.setScaling({1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
    solver.setInitialIterate(xRBM);
    solver.solve();
    xRBM = solver.getSol();
    for (std::size_t i = 0; i < xRBM.size(); i++) {
      x[_0][i] = xRBM[i][_0];
      x[_1][i] = xRBM[i][_1];
    }
#endif
    finalIteration = solver.getStatistics().finalIteration;
    finalEnergy = solver.getStatistics().finalEnergy;
  } else {    // solverType == "proximalNewton"

#if MIXED_SPACE
    DUNE_THROW(Exception, "Error: There is no MixedRiemannianProximalNewtonSolver!");
#else
    RiemannianProximalNewtonSolver<DeformationFEBasis, RBM, GFEAssemblerWrapper> solver;
    solver.setup(*grid,
                 &assembler,
                 xRBM,
                 dirichletDofsRBM,
                 tolerance,
                 maxSolverSteps,
                 1.0 /* initialRegularization */,
                 false /* instrumented */);
    solver.setScaling({1.0, 1.0, 1.0, 1.0, 1.0, 1.0});
    solver.setInitialIterate(xRBM);
    solver.solve();
    xRBM = solver.getSol();
    for (std::size_t i = 0; i < xRBM.size(); i++) {
      x[_0][i] = xRBM[i][_0];
      x[_1][i] = xRBM[i][_1];
    }

    finalIteration = solver.getStatistics().finalIteration;
    finalEnergy = solver.getStatistics().finalEnergy;
#endif
  }

  /////////////////////////////////
  //   Output result
  /////////////////////////////////

  // Compute the displacement, for visualization
  auto displacement = x[_0];
  for (std::size_t i = 0; i < compositeBasis.size({0}); i++)
    displacement[i] -= identity[i];

  auto displacementFunction = Functions::makeDiscreteGlobalBasisFunction<FieldVector<double,dim> >(deformationPowerBasis, displacement);

  // We (used to) need to subsample, because VTK cannot natively display real second-order functions
  // TODO: This is not true anymore, update our code accordingly!
  SubsamplingVTKWriter<GridView> vtkWriter(gridView, Dune::refinementLevels(displacementOrder-1));
  vtkWriter.addVertexData(displacementFunction, VTK::FieldInfo("displacement", VTK::FieldInfo::Type::scalar, dim));
  vtkWriter.write("filmonsubstratetest-result");

#if MIXED_SPACE
  std::size_t expectedFinalIteration = 10;
  double expectedEnergy = -13812728.2;
#else
  std::size_t expectedFinalIteration = 12;
  double expectedEnergy = -13812920.2;
#endif

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

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

  return 0;
}