From 0e84ee3b75029b67c3b26da44713adf83ed05375 Mon Sep 17 00:00:00 2001
From: Elias Pipping <elias.pipping@fu-berlin.de>
Date: Tue, 11 Sep 2012 16:01:59 +0200
Subject: [PATCH] Implement Newmark scheme

---
 src/enum_scheme.cc     |  3 +++
 src/enums.hh           |  3 ++-
 src/one-body-sample.cc | 55 ++++++++++++++++++++++++++++++++++++++
 src/timestepping.cc    | 60 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 120 insertions(+), 1 deletion(-)

diff --git a/src/enum_scheme.cc b/src/enum_scheme.cc
index 188a8645..9866d4d5 100644
--- a/src/enum_scheme.cc
+++ b/src/enum_scheme.cc
@@ -8,6 +8,9 @@ template <> struct StringToEnum<Config::scheme> {
     if (s == "implicitEuler")
       return Config::ImplicitEuler;
 
+    if (s == "newmark")
+      return Config::Newmark;
+
     DUNE_THROW(Dune::Exception, "failed to parse enum");
   }
 };
diff --git a/src/enums.hh b/src/enums.hh
index 8e59946e..21fb6cdf 100644
--- a/src/enums.hh
+++ b/src/enums.hh
@@ -12,7 +12,8 @@ struct Config {
   };
   enum scheme {
     ImplicitTwoStep,
-    ImplicitEuler
+    ImplicitEuler,
+    Newmark
   };
 };
 #endif
diff --git a/src/one-body-sample.cc b/src/one-body-sample.cc
index 7c15b244..55b2c72e 100644
--- a/src/one-body-sample.cc
+++ b/src/one-body-sample.cc
@@ -47,6 +47,7 @@
 #include <dune/istl/bvector.hh>
 
 #include <dune/fufem/assemblers/functionalassembler.hh>
+#include <dune/fufem/assemblers/localassemblers/massassembler.hh>
 #include <dune/fufem/assemblers/localassemblers/stvenantkirchhoffassembler.hh>
 #include <dune/fufem/assemblers/localassemblers/vonmisesstressassembler.hh>
 #include <dune/fufem/assemblers/operatorassembler.hh>
@@ -165,6 +166,40 @@ int main(int argc, char *argv[]) {
     P0Basis const p0Basis(leafView);
     P1Basis const p1Basis(leafView);
 
+    MassAssembler<GridType, P1Basis::LocalFiniteElement,
+                  P1Basis::LocalFiniteElement> const localMass;
+    MatrixType massMatrix;
+    {
+      timer.reset();
+      OperatorAssembler<P1Basis, P1Basis>(p1Basis, p1Basis)
+          .assemble(localMass, massMatrix);
+
+      // We have volume*gravity*density/area = normalstress (V*g*rho/A =
+      // sigma_n)
+      double volume = 1.0;
+      for (int i = 0; i < dim; ++i)
+        volume *= (upperRight[i] - lowerLeft[i]);
+
+      double area = 1.0;
+      for (int i = 0; i < dim; ++i)
+        if (i != 1)
+          area *= (upperRight[i] - lowerLeft[i]);
+
+      double const gravity = 9.81;
+
+      double const normalStress =
+          parset.get<double>("boundary.friction.normalstress");
+
+      // rho    = sigma     * A       / V   / g
+      // kg/m^d = N/m^(d-1) * m^(d-1) / m^d / (N/kg)
+      double const density = normalStress * area / volume / gravity;
+
+      massMatrix *= density;
+      if (parset.get<bool>("enable_timer"))
+        std::cout << "Assembled mass matrix in " << timer.elapsed() << "s"
+                  << std::endl;
+    }
+
     // Assemble elastic force on the body
     StVenantKirchhoffAssembler<GridType, P1Basis::LocalFiniteElement,
                                P1Basis::LocalFiniteElement> const
@@ -200,6 +235,13 @@ int main(int argc, char *argv[]) {
 
     VectorType ud(finestSize);
     ud = 0.0;
+    VectorType ud_old(finestSize);
+    ud_old = 0.0;
+
+    VectorType udd(finestSize);
+    udd = 0.0;
+    VectorType udd_old(finestSize);
+    udd_old = 0.0;
 
     SingletonVectorType alpha_old(finestSize);
     alpha_old = parset.get<double>("boundary.friction.state.initial");
@@ -280,6 +322,12 @@ int main(int argc, char *argv[]) {
                   ell, stiffnessMatrix, u_old, u_old_old_ptr, ignoreNodes,
                   dirichletFunction, time, tau);
               break;
+            case Config::Newmark:
+              timeSteppingScheme = Dune::make_shared<Newmark<
+                  VectorType, MatrixType, decltype(dirichletFunction), dim>>(
+                  ell, stiffnessMatrix, massMatrix, u_old, ud_old, udd_old,
+                  ignoreNodes, dirichletFunction, time, tau);
+              break;
           }
         }
         timeSteppingScheme->setup(problem_rhs, problem_iterate, problem_A);
@@ -307,6 +355,11 @@ int main(int argc, char *argv[]) {
           timeSteppingScheme->extractSolution(problem_iterate, u);
           timeSteppingScheme->extractVelocity(problem_iterate, ud);
 
+          udd = 0;
+          Arithmetic::addProduct(udd, 2.0 / tau, ud);
+          Arithmetic::addProduct(udd, -2.0 / tau, ud_old);
+          Arithmetic::addProduct(udd, -1.0, udd_old);
+
           // Update the state
           for (size_t i = 0; i < frictionalNodes.size(); ++i) {
             if (frictionalNodes[i][0]) {
@@ -400,6 +453,8 @@ int main(int argc, char *argv[]) {
       }
       u_old_old = u_old;
       u_old = u;
+      ud_old = ud;
+      udd_old = udd;
       alpha_old = alpha;
 
       // Compute von Mises stress and write everything to a file
diff --git a/src/timestepping.cc b/src/timestepping.cc
index dd9ad0e0..0a3e3dd7 100644
--- a/src/timestepping.cc
+++ b/src/timestepping.cc
@@ -1,3 +1,5 @@
+#include <dune/fufem/arithmetic.hh>
+
 template <class VectorType, class MatrixType, class FunctionType, int dim>
 class TimeSteppingScheme {
 public:
@@ -149,3 +151,61 @@ class ImplicitTwoStep
 private:
   VectorType const *u_old_old_ptr;
 };
+
+template <class VectorType, class MatrixType, class FunctionType, int dim>
+class Newmark
+    : public TimeSteppingScheme<VectorType, MatrixType, FunctionType, dim> {
+public:
+  Newmark(VectorType const &_ell, MatrixType const &_A, MatrixType const &_B,
+          VectorType const &_u_old, VectorType const &_ud_old,
+          VectorType const &_udd_old,
+          Dune::BitSetVector<dim> const &_dirichletNodes,
+          FunctionType const &_dirichletFunction, double _time, double _tau)
+      : TimeSteppingScheme<VectorType, MatrixType, FunctionType, dim>(
+            _ell, _A, _u_old, _dirichletNodes, _dirichletFunction, _time, _tau),
+        B(_B),
+        ud_old(_ud_old),
+        udd_old(_udd_old) {}
+
+  void virtual setup(VectorType &problem_rhs, VectorType &problem_iterate,
+                     MatrixType &problem_A) const {
+    problem_rhs = this->ell;
+    /* */ B.usmv(2.0 / this->tau, ud_old, problem_rhs);
+    /* */ B.usmv(1.0, udd_old, problem_rhs);
+    this->A.usmv(-1.0, this->u_old, problem_rhs);
+    this->A.usmv(-this->tau / 2.0, ud_old, problem_rhs);
+
+    // For fixed tau, we'd only really have to do this once
+    problem_A = this->A;
+    problem_A *= this->tau / 2.0;
+    Arithmetic::addProduct(problem_A, 2.0 / this->tau, B);
+
+    // ud_old makes a good initial iterate; we could use anything, though
+    problem_iterate = ud_old;
+
+    for (size_t i = 0; i < this->dirichletNodes.size(); ++i)
+      if (this->dirichletNodes[i].count() == dim) {
+        problem_iterate[i] = 0;
+        this->dirichletFunction.evaluate(this->time, problem_iterate[i][0]);
+      } else if (this->dirichletNodes[i][1])
+        problem_iterate[i][1] = 0; // Y direction prescribed
+  }
+
+  // u1 = u0 + tau/2 (du1 + du0)
+  void virtual extractSolution(VectorType const &problem_iterate,
+                               VectorType &solution) const {
+    solution = this->u_old;
+    Arithmetic::addProduct(solution, this->tau / 2.0, problem_iterate); // ud
+    Arithmetic::addProduct(solution, this->tau / 2.0, ud_old);
+  }
+
+  void virtual extractVelocity(VectorType const &problem_iterate,
+                               VectorType &velocity) const {
+    velocity = problem_iterate;
+  }
+
+private:
+  MatrixType const &B;
+  VectorType const &ud_old;
+  VectorType const &udd_old;
+};
-- 
GitLab