From b00c3fe44df1a9a514e2006f164dbff4619d5c40 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20He=C3=9F?= <rene.hess@iwr.uni-heidelberg.de>
Date: Tue, 9 Aug 2016 13:03:33 +0200
Subject: [PATCH] Solve nonlinear problem

---
 dune/perftool/vtkpredicate.hh                 |   4 +-
 python/dune/perftool/pdelab/driver.py         |  56 +++++-
 test/CMakeLists.txt                           |   1 +
 test/nonlinear/CMakeLists.txt                 |  15 ++
 test/nonlinear/nonlinear.ufl                  |   8 +
 test/nonlinear/nonlinear_ref.vtu              |   3 +
 test/nonlinear/nonlinear_symdiff.mini         |  11 ++
 test/nonlinear/reference_driver.hh            | 124 +++++++++++++
 test/nonlinear/reference_main.cc              |  33 ++++
 .../reference_nonlinearpoissonfem.hh          | 172 ++++++++++++++++++
 test/nonlinear/reference_problem.hh           |  56 ++++++
 11 files changed, 474 insertions(+), 9 deletions(-)
 create mode 100644 test/nonlinear/CMakeLists.txt
 create mode 100644 test/nonlinear/nonlinear.ufl
 create mode 100644 test/nonlinear/nonlinear_ref.vtu
 create mode 100644 test/nonlinear/nonlinear_symdiff.mini
 create mode 100644 test/nonlinear/reference_driver.hh
 create mode 100644 test/nonlinear/reference_main.cc
 create mode 100644 test/nonlinear/reference_nonlinearpoissonfem.hh
 create mode 100644 test/nonlinear/reference_problem.hh

diff --git a/dune/perftool/vtkpredicate.hh b/dune/perftool/vtkpredicate.hh
index fa77e2a3..d567e01e 100644
--- a/dune/perftool/vtkpredicate.hh
+++ b/dune/perftool/vtkpredicate.hh
@@ -1,6 +1,8 @@
 #ifndef DUNE_PERFTOOL_VTKPREDICATE_HH
 #define DUNE_PERFTOOL_VTKPREDICATE_HH
 
+#include <dune/typetree/nodeinterface.hh>
+
 /** A predicate for vtk output
  *  that cuts all vector grid function spaces with a length greater than 4.
  *  This is necessary because VTK cannot handle them.
@@ -14,4 +16,4 @@ struct CuttingPredicate
   }
 };
 
-#endif
\ No newline at end of file
+#endif
diff --git a/python/dune/perftool/pdelab/driver.py b/python/dune/perftool/pdelab/driver.py
index 4fe77cdf..3db5c3c7 100644
--- a/python/dune/perftool/pdelab/driver.py
+++ b/python/dune/perftool/pdelab/driver.py
@@ -50,6 +50,18 @@ def set_form(f):
     _form = f
 
 
+def is_linear(form):
+    '''Test if form is linear in test function'''
+    from ufl import derivative
+    from ufl.algorithms import expand_derivatives
+    jacform = expand_derivatives(derivative(form, form.coefficients()[0]))
+    for coeff in jacform.coefficients():
+        if 0 == coeff.count():
+            return False
+
+    return True
+
+
 def isLagrange(fem):
     return fem._short_name is 'CG'
 
@@ -802,12 +814,27 @@ def typedef_stationarylinearproblemsolver(name):
     return "typedef Dune::PDELab::StationaryLinearProblemSolver<{}, {}, {}> {};".format(gotype, lstype, xtype, name)
 
 
+@preamble
+def typedef_stationarynonlinearproblemsolver(name):
+    include_file("dune/pdelab/newton/newton.hh", filetag="driver")
+    gotype = type_gridoperator()
+    lstype = type_linearsolver()
+    xtype = type_vector()
+    return "typedef Dune::PDELab::Newton<{}, {}, {}> {};".format(gotype, lstype, xtype, name)
+
+
 @symbol
 def type_stationarylinearproblemsolver():
     typedef_stationarylinearproblemsolver("SLP")
     return "SLP"
 
 
+@symbol
+def type_stationarynonlinearproblemssolver():
+    typedef_stationarynonlinearproblemsolver("SNP")
+    return "SNP"
+
+
 @preamble
 def define_stationarylinearproblemsolver(name):
     slptype = type_stationarylinearproblemsolver()
@@ -818,22 +845,35 @@ def define_stationarylinearproblemsolver(name):
     return "{} {}({}, {}, {}, {});".format(slptype, name, go, ls, x, red)
 
 
+@preamble
+def define_stationarynonlinearproblemsolver(name):
+    snptype = type_stationarynonlinearproblemssolver()
+    go = name_gridoperator()
+    x = name_vector()
+    ls = name_linearsolver()
+    return "{}  {}({}, {}, {});".format(snptype, name, go, x, ls)
+
 @symbol
 def name_stationarylinearproblemsolver():
     define_stationarylinearproblemsolver("slp")
     return "slp"
 
 
+@symbol
+def name_stationarynonlinearproblemsolver():
+    define_stationarynonlinearproblemsolver("snp")
+    return "snp"
+
+
 @preamble
 def dune_solve():
-    from ufl.algorithms.predicates import is_multilinear
-    # This is crap as it does check for linearity of the rank 1 form,
-    # which is by definition True.
-#     if is_multilinear(_form):
-    slp = name_stationarylinearproblemsolver()
-    return "{}.apply();".format(slp)
-#     else:
-#         raise NotImplementedError
+    # Test if form is linear in ansatzfunction
+    if is_linear(_form):
+        slp = name_stationarylinearproblemsolver()
+        return "{}.apply();".format(slp)
+    else:
+        snp = name_stationarynonlinearproblemsolver()
+        return "{}.apply();".format(snp)
 
 
 @preamble
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 8b9c34a9..76611b31 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -1,3 +1,4 @@
 add_subdirectory(laplace)
 add_subdirectory(poisson)
 add_subdirectory(stokes)
+add_subdirectory(nonlinear)
diff --git a/test/nonlinear/CMakeLists.txt b/test/nonlinear/CMakeLists.txt
new file mode 100644
index 00000000..ff8e1624
--- /dev/null
+++ b/test/nonlinear/CMakeLists.txt
@@ -0,0 +1,15 @@
+add_generated_executable(UFLFILE nonlinear.ufl
+                         TARGET nonlinear_symdiff
+                         FORM_COMPILER_ARGS
+                         )
+
+
+dune_add_system_test(TARGET nonlinear_symdiff
+                     INIFILE nonlinear_symdiff.mini
+                     SCRIPT dune_vtkcompare.py
+                     )
+
+# Add the reference code with the PDELab localoperator that produced
+# the reference vtk file
+add_executable(nonlinear_ref reference_main.cc)
+set_target_properties(nonlinear_ref PROPERTIES EXCLUDE_FROM_ALL 1)
diff --git a/test/nonlinear/nonlinear.ufl b/test/nonlinear/nonlinear.ufl
new file mode 100644
index 00000000..080010cb
--- /dev/null
+++ b/test/nonlinear/nonlinear.ufl
@@ -0,0 +1,8 @@
+f = Expression("return -4;")
+g = Expression("return x.two_norm2();")
+
+V = FiniteElement("CG", "triangle", 1, dirichlet_expression=g)
+u = TrialFunction(V)
+v = TestFunction(V)
+
+forms = [(inner(grad(u), grad(v)) + u*u*v - f*v)*dx]
diff --git a/test/nonlinear/nonlinear_ref.vtu b/test/nonlinear/nonlinear_ref.vtu
new file mode 100644
index 00000000..eb718c0a
--- /dev/null
+++ b/test/nonlinear/nonlinear_ref.vtu
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:58c404919a3b6e9fea3a0609fc9ae56a9c5b98f5f1de0e86d133b8c8fe34895c
+size 225446
diff --git a/test/nonlinear/nonlinear_symdiff.mini b/test/nonlinear/nonlinear_symdiff.mini
new file mode 100644
index 00000000..31e2aba0
--- /dev/null
+++ b/test/nonlinear/nonlinear_symdiff.mini
@@ -0,0 +1,11 @@
+__name = nonlinear_symdiff
+
+lowerleft = 0.0 0.0
+upperright = 1.0 1.0
+elements = 32 32
+elementType = simplical
+
+[wrapper.vtkcompare]
+name = {__name}
+reference = nonlinear_ref
+extension = vtu
diff --git a/test/nonlinear/reference_driver.hh b/test/nonlinear/reference_driver.hh
new file mode 100644
index 00000000..db0b072f
--- /dev/null
+++ b/test/nonlinear/reference_driver.hh
@@ -0,0 +1,124 @@
+#ifndef DUNE_PERFTOOL_TEST_NONLINEAR_REFERENCE_DRIVER_HH
+#define DUNE_PERFTOOL_TEST_NONLINEAR_REFERENCE_DRIVER_HH
+
+#include <dune/common/parametertreeparser.hh>
+#include <dune/pdelab/constraints/conforming.hh>
+#include <dune/pdelab/finiteelementmap/pkfem.hh>
+#include <dune/grid/io/file/vtk/subsamplingvtkwriter.hh>
+#include <dune/alugrid/grid.hh>
+#include <dune/pdelab/backend/istl.hh>
+#include <dune/pdelab/function/callableadapter.hh>
+#include <dune/testtools/gridconstruction.hh>
+#include <dune/common/parametertree.hh>
+#include <dune/pdelab/gridoperator/gridoperator.hh>
+#include <dune/pdelab/gridfunctionspace/vtk.hh>
+#include <dune/pdelab/newton/newton.hh>
+#include <dune/perftool/vtkpredicate.hh>
+#include <dune/pdelab/backend/istl.hh>
+#include <string>
+#include <dune/pdelab/backend/istl.hh>
+#include <dune/pdelab/finiteelementmap/opbfem.hh>
+
+#include "reference_problem.hh"
+#include "reference_nonlinearpoissonfem.hh"
+
+void driver (int argc, char** argv){
+  // Dimension and important types
+  const int dim = 2;
+  typedef double RF;
+
+  // Open ini file
+  Dune::ParameterTree initree;
+  Dune::ParameterTreeParser::readINITree(argv[1], initree);
+
+  // Create grid
+  typedef Dune::ALUGrid<dim, dim, Dune::simplex, Dune::conforming> Grid;
+  IniGridFactory<Grid> factory(initree);
+  std::shared_ptr<Grid> grid = factory.getGrid();
+  typedef Grid::LeafGridView GV;
+  GV gv = grid->leafGridView();
+
+  // Make PDE parameter class
+  RF eta = 1.0;
+  Problem<RF> problem(eta);
+  auto glambda = [&](const auto& e, const auto& x)
+    {return problem.g(e,x);};
+  auto g = Dune::PDELab::
+    makeGridFunctionFromCallable(gv,glambda);
+  auto blambda = [&](const auto& i, const auto& x)
+    {return problem.b(i,x);};
+  auto b = Dune::PDELab::
+    makeBoundaryConditionFromCallable(gv,blambda);
+
+  // Make grid function space
+  typedef Grid::ctype DF;
+
+  typedef Dune::PDELab::PkLocalFiniteElementMap<GV,DF,double,1> FEM;
+  FEM fem(gv);
+  typedef Dune::PDELab::ConformingDirichletConstraints CON;
+  typedef Dune::PDELab::istl::VectorBackend<> VBE;
+  typedef Dune::PDELab::GridFunctionSpace<GV,FEM,CON,VBE> GFS;
+  GFS gfs(gv,fem);
+  gfs.name("Vh");
+
+  // Assemble constraints
+  typedef typename GFS::template
+    ConstraintsContainer<RF>::Type CC;
+  CC cc;
+  Dune::PDELab::constraints(b,gfs,cc); // assemble constraints
+  std::cout << "constrained dofs=" << cc.size() << " of "
+            << gfs.globalSize() << std::endl;
+
+  // A coefficient vector
+  using Z = Dune::PDELab::Backend::Vector<GFS,RF>;
+  Z z(gfs); // initial value
+
+  // Make a grid function out of it
+  typedef Dune::PDELab::DiscreteGridFunction<GFS,Z> ZDGF;
+  ZDGF zdgf(gfs,z);
+
+  // Fill the coefficient vector
+  Dune::PDELab::interpolate(g,gfs,z);
+
+  // Make a local operator
+  typedef NonlinearPoissonFEM<Problem<RF>,FEM> LOP;
+  LOP lop(problem);
+
+  // Make a global operator
+  typedef Dune::PDELab::istl::BCRSMatrixBackend<> MBE;
+  int degree = initree.get("fem.degree",(int)1);
+  MBE mbe((int)pow(1+2*degree,dim));
+  typedef Dune::PDELab::GridOperator<
+    GFS,GFS,  /* ansatz and test space */
+    LOP,      /* local operator */
+    MBE,      /* matrix backend */
+    RF,RF,RF, /* domain, range, jacobian field type*/
+    CC,CC     /* constraints for ansatz and test space */
+    > GO;
+  GO go(gfs,cc,gfs,cc,lop,mbe);
+
+  // Select a linear solver backend
+  typedef Dune::PDELab::ISTLBackend_SEQ_UMFPack LS;
+  LS ls(false);
+
+  // Set up nonlinear solver
+  Dune::PDELab::Newton<GO,LS,Z> newton(go,z,ls);
+  // newton.setReassembleThreshold(0.0); // always reassemble J
+  // newton.setVerbosityLevel(3);        // be verbose
+  // newton.setReduction(1e-10);         // total reduction
+  // newton.setMinLinearReduction(1e-4); // min. red. in lin. solve
+  // newton.setMaxIterations(25);        // limit number of its
+  // newton.setLineSearchMaxIterations(10); // limit line search
+
+  // Solve nonlinear problem
+  newton.apply();
+
+  // Write VTK output file
+  int subsampling = initree.get("vtk.subsamplinglevel",(int)0);
+  Dune::SubsamplingVTKWriter<GV> vtkwriter(gv,subsampling);
+  Dune::PDELab::addSolutionToVTKWriter(vtkwriter, gfs, z);
+  std::string vtkfile = initree.get<std::string>("wrapper.vtkcompare.name", "output");
+  vtkwriter.write(vtkfile, Dune::VTK::ascii);
+}
+
+#endif
diff --git a/test/nonlinear/reference_main.cc b/test/nonlinear/reference_main.cc
new file mode 100644
index 00000000..4304d9b0
--- /dev/null
+++ b/test/nonlinear/reference_main.cc
@@ -0,0 +1,33 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <dune/common/parallel/mpihelper.hh>
+#include <dune/common/exceptions.hh>
+
+#include "reference_driver.hh"
+
+int main(int argc, char** argv)
+{
+  try{
+    //Maybe initialize Mpi
+    Dune::MPIHelper& helper = Dune::MPIHelper::instance(argc, argv);
+    if(Dune::MPIHelper::isFake)
+      std::cout<< "This is a sequential program." << std::endl;
+    else
+      std::cout<<"I am rank "<<helper.rank()<<" of "<<helper.size()
+        <<" processes!"<<std::endl;
+
+    driver(argc, argv);
+
+    return 0;
+  }
+  catch (Dune::Exception &e){
+    std::cerr << "Dune reported error: " << e << std::endl;
+    return 1;
+  }
+  catch (...){
+    std::cerr << "Unknown exception thrown!" << std::endl;
+    return 1;
+  }
+}
diff --git a/test/nonlinear/reference_nonlinearpoissonfem.hh b/test/nonlinear/reference_nonlinearpoissonfem.hh
new file mode 100644
index 00000000..556d72a6
--- /dev/null
+++ b/test/nonlinear/reference_nonlinearpoissonfem.hh
@@ -0,0 +1,172 @@
+#include<vector>
+
+#include<dune/common/exceptions.hh>
+#include<dune/common/fvector.hh>
+#include<dune/geometry/type.hh>
+
+#include<dune/pdelab/common/referenceelements.hh>
+#include<dune/pdelab/common/quadraturerules.hh>
+#include<dune/pdelab/localoperator/pattern.hh>
+#include<dune/pdelab/localoperator/flags.hh>
+#include<dune/pdelab/localoperator/defaultimp.hh>
+#include<dune/pdelab/finiteelement/localbasiscache.hh>
+#include<dune/pdelab/localoperator/variablefactories.hh>
+
+/** a local operator for solving the nonlinear Poisson equation with conforming FEM
+ *
+ * \f{align*}{
+ *   -\Delta u(x) + q(u(x)) &=& f(x) x\in\Omega,  \\
+ *                     u(x) &=& g(x) x\in\partial\Omega_D \\
+ *  -\nabla u(x) \cdot n(x) &=& j(x) x\in\partial\Omega_N \\
+ * \f}
+ *
+ */
+template<typename Param, typename FEM>
+class NonlinearPoissonFEM :
+  public Dune::PDELab::
+    NumericalJacobianVolume<NonlinearPoissonFEM<Param,FEM> >,
+  public Dune::PDELab::
+    NumericalJacobianApplyVolume<NonlinearPoissonFEM<Param,FEM> >,
+  public Dune::PDELab::FullVolumePattern,
+  public Dune::PDELab::LocalOperatorDefaultFlags
+{
+  typedef typename FEM::Traits::FiniteElementType::
+     Traits::LocalBasisType LocalBasis;
+  Dune::PDELab::LocalBasisCache<LocalBasis> cache;
+  Param& param; // parameter functions
+  int incrementorder; // increase of integration order
+
+public:
+  // pattern assembly flags
+  enum { doPatternVolume = true };
+
+  // residual assembly flags
+  enum { doLambdaVolume = true };
+  enum { doLambdaBoundary = true };
+  enum { doAlphaVolume = true };
+
+  //! constructor stores a copy of the parameter object
+  NonlinearPoissonFEM (Param& param_, int incrementorder_=0)
+    : param(param_), incrementorder(incrementorder_)
+  {}
+
+  //! right hand side integral
+  template<typename EG, typename LFSV, typename R>
+  void lambda_volume (const EG& eg, const LFSV& lfsv,
+                      R& r) const
+  {
+    // select quadrature rule
+    auto geo = eg.geometry();
+    const int order = incrementorder+
+      2*lfsv.finiteElement().localBasis().order();
+    auto rule = Dune::PDELab::quadratureRule(geo,order);
+
+    // loop over quadrature points
+    for (const auto& ip : rule)
+      {
+        // evaluate basis functions
+        auto& phihat = cache.evaluateFunction(ip.position(),
+                             lfsv.finiteElement().localBasis());
+
+        // integrate -f*phi_i
+        decltype(ip.weight()) factor = ip.weight()*
+          geo.integrationElement(ip.position());
+        auto f=param.f(eg.entity(),ip.position());
+        for (size_t i=0; i<lfsv.size(); i++)
+          r.accumulate(lfsv,i,-f*phihat[i]*factor);
+      }
+  }
+
+  // Neumann boundary integral
+  template<typename IG, typename LFSV, typename R>
+  void lambda_boundary (const IG& ig, const LFSV& lfsv,
+                        R& r) const
+  {
+    // evaluate boundary condition type
+    auto localgeo = ig.geometryInInside();
+    auto facecenterlocal = Dune::PDELab::
+      referenceElement(localgeo).position(0,0);
+    bool isdirichlet=param.b(ig.intersection(),facecenterlocal);
+
+    // skip rest if we are on Dirichlet boundary
+    if (isdirichlet) return;
+
+    // select quadrature rule
+    auto globalgeo = ig.geometry();
+    const int order = incrementorder+
+      2*lfsv.finiteElement().localBasis().order();
+    auto rule = Dune::PDELab::quadratureRule(globalgeo,order);
+
+    // loop over quadrature points and integrate normal flux
+    for (const auto& ip : rule)
+      {
+        // quadrature point in local coordinates of element
+        auto local = localgeo.global(ip.position());
+
+        // evaluate shape functions (assume Galerkin method)
+        auto& phihat = cache.evaluateFunction(local,
+                             lfsv.finiteElement().localBasis());
+
+        // integrate j
+        decltype(ip.weight()) factor = ip.weight()*
+          globalgeo.integrationElement(ip.position());
+        auto j = param.j(ig.intersection(),ip.position());
+        for (size_t i=0; i<lfsv.size(); i++)
+          r.accumulate(lfsv,i,j*phihat[i]*factor);
+      }
+  }
+
+  //! volume integral depending on test and ansatz functions
+  template<typename EG, typename LFSU, typename X,
+           typename LFSV, typename R>
+  void alpha_volume (const EG& eg, const LFSU& lfsu, const X& x,
+                     const LFSV& lfsv, R& r) const
+  {
+    // types & dimension
+    const int dim = EG::Entity::dimension;
+    typedef decltype(Dune::PDELab::
+                     makeZeroBasisFieldValue(lfsu)) RF;
+
+    // select quadrature rule
+    auto geo = eg.geometry();
+    const int order = incrementorder+
+      2*lfsu.finiteElement().localBasis().order();
+    auto rule = Dune::PDELab::quadratureRule(geo,order);
+
+    // loop over quadrature points
+    for (const auto& ip : rule)
+      {
+        // evaluate basis functions
+        auto& phihat = cache.evaluateFunction(ip.position(),
+                             lfsu.finiteElement().localBasis());
+
+        // evaluate u
+        RF u=0.0;
+        for (size_t i=0; i<lfsu.size(); i++)
+          u += x(lfsu,i)*phihat[i];
+
+        // evaluate gradient of shape functions
+        auto& gradphihat = cache.evaluateJacobian(ip.position(),
+                             lfsu.finiteElement().localBasis());
+
+        // transform gradients of shape functions to real element
+        const auto S = geo.jacobianInverseTransposed(ip.position());
+        auto gradphi = makeJacobianContainer(lfsu);
+        for (size_t i=0; i<lfsu.size(); i++)
+          S.mv(gradphihat[i][0],gradphi[i][0]);
+
+        // compute gradient of u
+        Dune::FieldVector<RF,dim> gradu(0.0);
+        for (size_t i=0; i<lfsu.size(); i++)
+          gradu.axpy(x(lfsu,i),gradphi[i][0]);
+
+        // integrate (grad u)*grad phi_i + q(u)*phi_i
+        auto factor = ip.weight()*
+          geo.integrationElement(ip.position());
+        auto q = param.q(u);
+        for (size_t i=0; i<lfsu.size(); i++)
+          r.accumulate(lfsu,i,(gradu*gradphi[i][0]+
+                               q*phihat[i])*factor);
+      }
+  }
+};
diff --git a/test/nonlinear/reference_problem.hh b/test/nonlinear/reference_problem.hh
new file mode 100644
index 00000000..df35410c
--- /dev/null
+++ b/test/nonlinear/reference_problem.hh
@@ -0,0 +1,56 @@
+template<typename Number>
+class Problem
+{
+  Number eta;
+public:
+  typedef Number value_type;
+
+  //! Constructor without arg sets nonlinear term to zero
+  Problem () : eta(0.0) {}
+
+  //! Constructor takes eta parameter
+  Problem (const Number& eta_) : eta(eta_) {}
+
+  //! nonlinearity
+  Number q (Number u) const
+  {
+    return eta*u*u;
+  }
+
+  //! derivative of nonlinearity
+  Number qprime (Number u) const
+  {
+    return 2*eta*u;
+  }
+
+  //! right hand side
+  template<typename E, typename X>
+  Number f (const E& e, const X& x) const
+  {
+    return -2.0*x.size();
+  }
+
+  //! boundary condition type function (true = Dirichlet)
+  template<typename I, typename X>
+  bool b (const I& i, const X& x) const
+  {
+    return true;
+  }
+
+  //! Dirichlet extension
+  template<typename E, typename X>
+  Number g (const E& e, const X& x) const
+  {
+    auto global = e.geometry().global(x);
+    Number s=0.0;
+    for (std::size_t i=0; i<global.size(); i++) s+=global[i]*global[i];
+    return s;
+  }
+
+  //! Neumann boundary condition
+  template<typename I, typename X>
+  Number j (const I& i, const X& x) const
+  {
+    return 0.0;
+  }
+};
-- 
GitLab