From a0902b1efffabdc6367e23cddd8a45453bad762a Mon Sep 17 00:00:00 2001
From: Oliver Sander <sander@igpm.rwth-aachen.de>
Date: Mon, 27 Oct 2014 20:27:09 +0000
Subject: [PATCH] New program to experiment with finite strain elasticity
 models

[[Imported from SVN: r9943]]
---
 Makefile.am                 |   9 +
 finite-strain-elasticity.cc | 376 ++++++++++++++++++++++++++++++++++++
 2 files changed, 385 insertions(+)
 create mode 100644 finite-strain-elasticity.cc

diff --git a/Makefile.am b/Makefile.am
index f65db4c4..9d3f74c2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -14,6 +14,7 @@ ADOLC_LDFLAGS = -L/home/sander/adolc-inst/lib64
 ADOLC_LIBS = -ladolc
 
 noinst_PROGRAMS = cosserat-continuum \
+                  finite-strain-elasticity \
                   mixed-cosserat-continuum \
                   rodobstacle rod3d harmonicmaps harmonicmaps-eoc rod-eoc
 
@@ -25,6 +26,14 @@ cosserat_continuum_LDADD    = $(UG_LIBS) $(IPOPT_LIBS) \
 cosserat_continuum_LDFLAGS  = $(UG_LDFLAGS) $(IPOPT_LDFLAGS) \
                               $(ADOLC_LDFLAGS) $(PYTHON_LDFLAGS)
 
+finite_strain_elasticity_SOURCES  = finite-strain-elasticity.cc
+finite_strain_elasticity_CXXFLAGS = $(UG_CPPFLAGS) $(IPOPT_CPPFLAGS) \
+                              $(ADOLC_CPPFLAGS) $(PYTHON_CPPFLAGS) $(UMFPACK_CPPFLAGS)
+finite_strain_elasticity_LDADD    = $(UG_LIBS) $(IPOPT_LIBS) \
+                              $(ADOLC_LIBS) $(PYTHON_LIBS) $(UMFPACK_LIBS)
+finite_strain_elasticity_LDFLAGS  = $(UG_LDFLAGS) $(IPOPT_LDFLAGS) \
+                              $(ADOLC_LDFLAGS) $(PYTHON_LDFLAGS) $(UMFPACK_LDFLAGS)
+
 mixed_cosserat_continuum_SOURCES  = mixed-cosserat-continuum.cc
 mixed_cosserat_continuum_CXXFLAGS = $(UG_CPPFLAGS) $(IPOPT_CPPFLAGS) \
                               $(ADOLC_CPPFLAGS) $(PYTHON_CPPFLAGS)
diff --git a/finite-strain-elasticity.cc b/finite-strain-elasticity.cc
new file mode 100644
index 00000000..6bb463e1
--- /dev/null
+++ b/finite-strain-elasticity.cc
@@ -0,0 +1,376 @@
+#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/gfe/adolcnamespaceinjections.hh>
+#include <dune/common/bitsetvector.hh>
+#include <dune/common/parametertree.hh>
+#include <dune/common/parametertreeparser.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/fufem/boundarypatch.hh>
+#include <dune/fufem/functions/vtkbasisgridfunction.hh>
+#include <dune/fufem/functiontools/boundarydofs.hh>
+#include <dune/fufem/functiontools/basisinterpolator.hh>
+#include <dune/fufem/functionspacebases/p1nodalbasis.hh>
+#include <dune/fufem/functionspacebases/p2nodalbasis.hh>
+#include <dune/fufem/dunepython.hh>
+
+#include <dune/solvers/solvers/iterativesolver.hh>
+#include <dune/solvers/norms/energynorm.hh>
+
+#include <dune/gfe/localadolcstiffness.hh>
+#include <dune/gfe/stvenantkirchhoffenergy.hh>
+#include <dune/gfe/feassembler.hh>
+#include <dune/gfe/trustregionsolver.hh>
+
+// grid dimension
+const int dim = 3;
+
+using namespace Dune;
+
+/** \brief A constant vector-valued function, for simple Neumann boundary values */
+struct NeumannFunction
+    : public Dune::VirtualFunction<FieldVector<double,dim>, FieldVector<double,3> >
+{
+    NeumannFunction(const FieldVector<double,3> values,
+                    double homotopyParameter)
+    : values_(values),
+      homotopyParameter_(homotopyParameter)
+    {}
+
+    void evaluate(const FieldVector<double, dim>& x, FieldVector<double,3>& out) const {
+        out = 0;
+        out.axpy(-homotopyParameter_, values_);
+    }
+
+    FieldVector<double,3> values_;
+    double homotopyParameter_;
+};
+
+
+int main (int argc, char *argv[]) try
+{
+    // initialize MPI, finalize is done automatically on exit
+    Dune::MPIHelper& mpiHelper = MPIHelper::instance(argc, argv);
+
+    // Start Python interpreter
+    Python::start();
+    Python::Reference main = Python::import("__main__");
+    Python::run("import math");
+
+    //feenableexcept(FE_INVALID);
+    Python::runStream()
+        << std::endl << "import sys"
+        << std::endl << "sys.path.append('/home/sander/dune/dune-gfe/')"
+        << std::endl;
+
+    typedef std::vector<FieldVector<double,dim> > SolutionType;
+
+    // parse data file
+    ParameterTree parameterSet;
+//     if (argc != 2)
+//       DUNE_THROW(Exception, "Usage: ./hencky-material <parameter file>");
+
+    ParameterTreeParser::readINITree(argv[1], parameterSet);
+
+    ParameterTreeParser::readOptions(argc, argv, parameterSet);
+
+    // read solver settings
+    const int numLevels                   = parameterSet.get<int>("numLevels");
+    int numHomotopySteps                  = parameterSet.get<int>("numHomotopySteps");
+    const double tolerance                = parameterSet.get<double>("tolerance");
+    const int maxTrustRegionSteps         = parameterSet.get<int>("maxTrustRegionSteps");
+    const double initialTrustRegionRadius = parameterSet.get<double>("initialTrustRegionRadius");
+    const int multigridIterations         = parameterSet.get<int>("numIt");
+    const int nu1                         = parameterSet.get<int>("nu1");
+    const int nu2                         = parameterSet.get<int>("nu2");
+    const int mu                          = parameterSet.get<int>("mu");
+    const int baseIterations              = parameterSet.get<int>("baseIt");
+    const double mgTolerance              = parameterSet.get<double>("mgTolerance");
+    const double baseTolerance            = parameterSet.get<double>("baseTolerance");
+    const bool instrumented               = parameterSet.get<bool>("instrumented");
+    std::string resultPath                = parameterSet.get("resultPath", "");
+
+    // ///////////////////////////////////////
+    //    Create the grid
+    // ///////////////////////////////////////
+    typedef UGGrid<dim> GridType;
+
+    shared_ptr<GridType> grid;
+
+    FieldVector<double,dim> lower(0), upper(1);
+
+    if (parameterSet.get<bool>("structuredGrid")) {
+
+        lower = parameterSet.get<FieldVector<double,dim> >("lower");
+        upper = parameterSet.get<FieldVector<double,dim> >("upper");
+
+        array<unsigned int,dim> elements = parameterSet.get<array<unsigned int,dim> >("elements");
+        grid = StructuredGridFactory<GridType>::createCubeGrid(lower, upper, elements);
+
+    } else {
+        std::string path                = parameterSet.get<std::string>("path");
+        std::string gridFile            = parameterSet.get<std::string>("gridFile");
+        grid = shared_ptr<GridType>(GmshReader<GridType>::read(path + "/" + gridFile));
+    }
+
+    grid->globalRefine(numLevels-1);
+
+    grid->loadBalance();
+
+    if (mpiHelper.rank()==0)
+      std::cout << "There are " << grid->leafGridView().comm().size() << " processes" << std::endl;
+
+    typedef GridType::LeafGridView GridView;
+    GridView gridView = grid->leafGridView();
+
+    typedef P1NodalBasis<GridView,double> FEBasis;
+    FEBasis feBasis(gridView);
+
+    // /////////////////////////////////////////
+    //   Read Dirichlet values
+    // /////////////////////////////////////////
+
+    BitSetVector<1> dirichletVertices(gridView.size(dim), false);
+    BitSetVector<1> neumannVertices(gridView.size(dim), false);
+
+    GridType::Codim<dim>::LeafIterator vIt    = gridView.begin<dim>();
+    GridType::Codim<dim>::LeafIterator vEndIt = gridView.end<dim>();
+
+    const GridView::IndexSet& indexSet = gridView.indexSet();
+
+    // Make Python function that computes which vertices are on the Dirichlet boundary,
+    // based on the vertex positions.
+    std::string lambda = std::string("lambda x: (") + parameterSet.get<std::string>("dirichletVerticesPredicate") + std::string(")");
+    PythonFunction<FieldVector<double,dim>, bool> pythonDirichletVertices(Python::evaluate(lambda));
+
+    // Same for the Neumann boundary
+    lambda = std::string("lambda x: (") + parameterSet.get<std::string>("neumannVerticesPredicate", "0") + std::string(")");
+    PythonFunction<FieldVector<double,dim>, bool> pythonNeumannVertices(Python::evaluate(lambda));
+
+    for (; vIt!=vEndIt; ++vIt) {
+
+        bool isDirichlet;
+        pythonDirichletVertices.evaluate(vIt->geometry().corner(0), isDirichlet);
+        dirichletVertices[indexSet.index(*vIt)] = isDirichlet;
+
+        bool isNeumann;
+        pythonNeumannVertices.evaluate(vIt->geometry().corner(0), isNeumann);
+        neumannVertices[indexSet.index(*vIt)] = isNeumann;
+
+    }
+
+    BoundaryPatch<GridView> dirichletBoundary(gridView, dirichletVertices);
+    BoundaryPatch<GridView> neumannBoundary(gridView, neumannVertices);
+
+    if (mpiHelper.rank()==0)
+      std::cout << "Neumann boundary has " << neumannBoundary.numFaces() << " faces\n";
+
+
+    BitSetVector<1> dirichletNodes(feBasis.size(), false);
+    constructBoundaryDofs(dirichletBoundary,feBasis,dirichletNodes);
+
+    BitSetVector<1> neumannNodes(feBasis.size(), false);
+    constructBoundaryDofs(neumannBoundary,feBasis,neumannNodes);
+
+    BitSetVector<dim> dirichletDofs(feBasis.size(), false);
+    for (size_t i=0; i<feBasis.size(); i++)
+      if (dirichletNodes[i][0])
+        for (int j=0; j<dim; j++)
+          dirichletDofs[i][j] = true;
+
+    // //////////////////////////
+    //   Initial iterate
+    // //////////////////////////
+
+    SolutionType x(feBasis.size());
+
+    lambda = std::string("lambda x: (") + parameterSet.get<std::string>("initialDeformation") + std::string(")");
+    PythonFunction<FieldVector<double,dim>, FieldVector<double,3> > pythonInitialDeformation(Python::evaluate(lambda));
+
+    std::vector<FieldVector<double,3> > v;
+    Functions::interpolate(feBasis, v, pythonInitialDeformation);
+
+    for (size_t i=0; i<x.size(); i++)
+      x[i] = v[i];
+
+    lambda = std::string("lambda x: (") + parameterSet.get<std::string>("identity") + std::string(")");
+    PythonFunction<FieldVector<double,dim>, FieldVector<double,3> > pythonIdentity(Python::evaluate(lambda));
+
+    SolutionType identity;
+    Functions::interpolate(feBasis, identity, pythonIdentity);
+
+    ////////////////////////////////////////////////////////
+    //   Main homotopy loop
+    ////////////////////////////////////////////////////////
+
+    // Output initial iterate (of homotopy loop)
+    VTKWriter<GridType::LeafGridView> vtkWriter(grid->leafGridView());
+    BlockVector<FieldVector<double,3> > displacement(x.size());
+    for (auto it = grid->leafGridView().template begin<dim>(); it != grid->leafGridView().template end<dim>(); ++it)
+    {
+      size_t idx = grid->leafGridView().indexSet().index(*it);
+      displacement[idx] = x[idx] - it->geometry().corner(0);
+
+      //std::cout << "idx: " << idx << "   coordinate: " << it->geometry().corner(0) << std::endl;
+    }
+
+    Dune::shared_ptr<VTKBasisGridFunction<FEBasis,BlockVector<FieldVector<double,3> > > > vtkDisplacement
+               = Dune::make_shared<VTKBasisGridFunction<FEBasis,BlockVector<FieldVector<double,3> > > >
+                                  (feBasis, displacement, "Displacement");
+    vtkWriter.addVertexData(vtkDisplacement);
+    vtkWriter.write(resultPath + "hencky_homotopy_0");
+
+    for (int i=0; i<numHomotopySteps; i++) {
+
+        double homotopyParameter = (i+1)*(1.0/numHomotopySteps);
+        if (mpiHelper.rank()==0)
+            std::cout << "Homotopy step: " << i << ",    parameter: " << homotopyParameter << std::endl;
+
+
+    // ////////////////////////////////////////////////////////////
+    //   Create an assembler for the energy functional
+    // ////////////////////////////////////////////////////////////
+
+    const ParameterTree& materialParameters = parameterSet.sub("materialParameters");
+    shared_ptr<NeumannFunction> neumannFunction;
+    if (parameterSet.hasKey("neumannValues"))
+        neumannFunction = make_shared<NeumannFunction>(parameterSet.get<FieldVector<double,3> >("neumannValues"),
+                                                       homotopyParameter);
+
+        std::cout << "Neumann values: " << parameterSet.get<FieldVector<double,3> >("neumannValues") << std::endl;
+
+        if (mpiHelper.rank() == 0) {
+            std::cout << "Material parameters:" << std::endl;
+            materialParameters.report();
+        }
+
+    // Assembler using ADOL-C
+
+    StVenantKirchhoffEnergy<GridView,
+                 FEBasis::LocalFiniteElement,
+                 adouble> henckyEnergy(materialParameters,
+#if 0
+                                         &neumannBoundary,
+                                         neumannFunction.get());
+#else
+                                       nullptr, nullptr);
+#endif
+    LocalADOLCStiffness<GridView,
+                        FEBasis::LocalFiniteElement,
+                        SolutionType> localADOLCStiffness(&henckyEnergy);
+
+    FEAssembler<FEBasis,SolutionType> assembler(gridView, &localADOLCStiffness);
+
+    std::vector<FieldVector<double,3> > pointLoads(x.size());
+    std::fill(pointLoads.begin(), pointLoads.end(), 0);
+    pointLoads[1372] = parameterSet.get<FieldVector<double,3> >("neumannValues");
+    pointLoads[1372] *= 0.5;
+
+    // /////////////////////////////////////////////////
+    //   Create a Riemannian trust-region solver
+    // /////////////////////////////////////////////////
+
+    TrustRegionSolver<GridType,SolutionType> solver;
+    solver.setup(*grid,
+                 &assembler,
+                 x,
+                 dirichletDofs,
+                 tolerance,
+                 maxTrustRegionSteps,
+                 initialTrustRegionRadius,
+                 multigridIterations,
+                 mgTolerance,
+                 mu, nu1, nu2,
+                 baseIterations,
+                 baseTolerance,
+                 pointLoads
+                );
+
+    solver.identity_ = identity;
+
+        ////////////////////////////////////////////////////////
+        //   Set Dirichlet values
+        ////////////////////////////////////////////////////////
+
+        Python::Reference dirichletValuesClass = Python::import(parameterSet.get<std::string>("problem") + "-dirichlet-values");
+
+        Python::Callable C = dirichletValuesClass.get("DirichletValues");
+
+        // Call a constructor.
+        Python::Reference dirichletValuesPythonObject = C(homotopyParameter);
+
+        // Extract object member functions as Dune functions
+        PythonFunction<FieldVector<double,dim>, FieldVector<double,3> >   dirichletValues(dirichletValuesPythonObject.get("dirichletValues"));
+
+        std::vector<FieldVector<double,3> > ddV;
+        Functions::interpolate(feBasis, ddV, dirichletValues, dirichletDofs);
+
+        for (size_t j=0; j<x.size(); j++)
+          if (dirichletNodes[j][0])
+            x[j] = ddV[j];
+
+        // /////////////////////////////////////////////////////
+        //   Solve!
+        // /////////////////////////////////////////////////////
+
+        solver.setInitialIterate(x);
+        solver.solve();
+
+        x = solver.getSol();
+
+        // Output result of each homotopy step
+        VTKWriter<GridType::LeafGridView> vtkWriter(grid->leafGridView());
+        BlockVector<FieldVector<double,3> > displacement(x.size());
+        for (auto it = grid->leafGridView().template begin<dim>(); it != grid->leafGridView().template end<dim>(); ++it)
+        {
+          size_t idx = grid->leafGridView().indexSet().index(*it);
+          displacement[idx] = x[idx] - it->geometry().corner(0);
+        }
+
+
+        Dune::shared_ptr<VTKBasisGridFunction<FEBasis,BlockVector<FieldVector<double,3> > > > vtkDisplacement
+               = Dune::make_shared<VTKBasisGridFunction<FEBasis,BlockVector<FieldVector<double,3> > > >
+                                  (feBasis, displacement, "Displacement");
+        vtkWriter.addVertexData(vtkDisplacement);
+        vtkWriter.write(resultPath + "hencky_homotopy_" + std::to_string(i+1));
+
+    }
+
+    // //////////////////////////////
+    //   Output result
+    // //////////////////////////////
+
+    // finally: compute the average deformation of the Neumann boundary
+    // That is what we need for the locking tests
+    FieldVector<double,3> averageDef(0);
+    for (size_t i=0; i<x.size(); i++)
+        if (neumannNodes[i][0])
+        {
+            averageDef += x[i];
+            std::cout << "i: " << i << ",  pos: " << x[i] << std::endl;
+        }
+    averageDef /= neumannNodes.count();
+
+    if (mpiHelper.rank()==0)
+    {
+      std::cout << "Neumann value = " << parameterSet.get<std::string>("neumannValues") << std::endl;
+      std::cout << "Neumann value = " << parameterSet.get<FieldVector<double,dim> >("neumannValues") << "  "
+                << ",  average deflection: " << averageDef << std::endl;
+    }
+
+ } catch (Exception e) {
+
+    std::cout << e << std::endl;
+
+ }
-- 
GitLab