diff --git a/apps/benchmarks/CMakeLists.txt b/apps/benchmarks/CMakeLists.txt
index 01d57440109ea02878e19231b10a36e012d93d8c..aabba8b459bbd6e7dbe05c5e42a34728824bbb4d 100644
--- a/apps/benchmarks/CMakeLists.txt
+++ b/apps/benchmarks/CMakeLists.txt
@@ -7,6 +7,7 @@ add_subdirectory( CouetteFlow )
 add_subdirectory( FluidParticleCoupling )
 add_subdirectory( ForcesOnSphereNearPlaneInShearFlow )
 add_subdirectory( GranularGas )
+add_subdirectory( IntegratorAccuracy )
 add_subdirectory( LennardJones )
 add_subdirectory( NonUniformGrid )
 add_subdirectory( MotionSingleHeavySphere )
diff --git a/apps/benchmarks/IntegratorAccuracy/CMakeLists.txt b/apps/benchmarks/IntegratorAccuracy/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8e95e831cc1fb275731b12bc14460cf64eedc729
--- /dev/null
+++ b/apps/benchmarks/IntegratorAccuracy/CMakeLists.txt
@@ -0,0 +1,4 @@
+waLBerla_link_files_to_builddir( "*.ipynb" )
+
+waLBerla_add_executable ( NAME IntegratorAccuracy 
+                          DEPENDS core mesa_pd )
diff --git a/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.cpp b/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..af2dc8a3aaa54f805822eabcbd4139fb49d3cdf7
--- /dev/null
+++ b/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.cpp
@@ -0,0 +1,274 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file   IntegratorAccuracy.cpp
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include "mesa_pd/data/ParticleAccessor.h"
+
+#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+
+#include "core/Environment.h"
+#include "core/math/all.h"
+
+#include <iostream>
+
+namespace walberla {
+
+using namespace walberla::mesa_pd;
+
+class SingleParticleAccessorWithShape : public data::SingleParticleAccessor
+{
+public:
+   void setInvMass(const size_t /*p_idx*/, const real_t &val)
+   { invMass_ = val; }
+
+   const auto &getInvMass(const size_t /*p_idx*/) const
+   { return invMass_; }
+
+   void setInvInertiaBF(const size_t /*p_idx*/, const Mat3 &val)
+   { invInertiaBF_ = val; }
+
+   const auto &getInvInertiaBF(const size_t /*p_idx*/) const
+   { return invInertiaBF_; }
+
+private:
+   real_t invMass_;
+   Mat3 invInertiaBF_;
+};
+
+struct Oscillator
+{
+   real_t amplitude = 1.5_r;
+   real_t k = 0.1_r;
+   real_t damping = 0_r;
+   real_t mass = 0.9_r;
+   real_t dt = 0.2_r;
+   real_t phaseFraction = 0_r;
+   real_t periods = 10_r;
+   real_t phase = phaseFraction * math::pi;
+   real_t dampingRatio = damping / (2_r * std::sqrt(mass * k));
+   real_t omega = std::sqrt(k / mass) * std::sqrt(1_r - dampingRatio * dampingRatio);
+   real_t decay = std::sqrt(k / mass) * dampingRatio;
+   real_t durationOnePeriod = 2_r * math::pi / omega;
+   uint_t timeSteps = uint_c(periods * durationOnePeriod / dt);
+
+   Oscillator(int argc, char **argv)
+   {
+      for (int i = 1; i < argc; ++i)
+      {
+         if (std::strcmp(argv[i], "--dt") == 0)
+         {
+            dt = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--amplitude") == 0)
+         {
+            amplitude = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--k") == 0)
+         {
+            k = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--damping") == 0)
+         {
+            damping = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--mass") == 0)
+         {
+            mass = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--phaseFraction") == 0)
+         {
+            phaseFraction = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--periods") == 0)
+         {
+            periods = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
+      }
+
+      update();
+   }
+
+   void update()
+   {
+      phase = phaseFraction * math::pi;
+      dampingRatio = damping / (2_r * std::sqrt(mass * k));
+      omega = std::sqrt(k / mass) * std::sqrt(1_r - dampingRatio * dampingRatio);
+      decay = std::sqrt(k / mass) * dampingRatio;
+      durationOnePeriod = 2_r * math::pi / omega;
+      timeSteps = uint_c(periods * durationOnePeriod / dt);
+   }
+
+   Vec3 getForce(const Vec3 &pos, const Vec3 &vel) const
+   {
+      return -k * pos - damping * vel;
+   }
+
+   real_t getEnergy(const real_t &pos, const real_t &vel) const
+   {
+      return 0.5_r * mass * vel * vel + 0.5_r * k * pos * pos;
+   }
+
+   real_t analyticalPos(real_t t) const
+   {
+      return amplitude * std::exp(-decay * t) * std::cos(omega * t + phase);
+   }
+
+   real_t analyticalVel(real_t t) const
+   {
+      return -decay * amplitude * std::exp(-decay * t) * std::cos(omega * t + phase)
+             -amplitude * std::exp(-decay * t) * omega * std::sin(omega * t + phase);
+   }
+};
+
+struct ExplicitEuler
+{
+   ExplicitEuler(real_t dt) : integrator(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
+   {
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      integrator(0, particle);
+   }
+   kernel::ExplicitEuler integrator;
+};
+
+struct SemiImplicitEuler
+{
+   SemiImplicitEuler(real_t dt) : integrator(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
+   {
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      integrator(0, particle);
+   }
+   kernel::SemiImplicitEuler integrator;
+};
+
+struct VelocityVerlet
+{
+   VelocityVerlet(real_t dt) : preVV(dt), postVV(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
+   {
+      preVV(0, particle);
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      postVV(0, particle);
+   }
+   kernel::VelocityVerletPreForceUpdate preVV;
+   kernel::VelocityVerletPostForceUpdate postVV;
+};
+
+struct AccuracyResult
+{
+   real_t maxPosDeviation;
+   real_t maxVelDeviation;
+   real_t maxEneDeviation;
+};
+
+template <typename Integrator>
+AccuracyResult checkIntegrator(const Oscillator& osc)
+{
+   //init data structures
+   SingleParticleAccessorWithShape particle;
+
+   //first dummy argument is needed to fulfill accessor interface
+   particle.setPosition(0, Vec3(0, 0, osc.analyticalPos(0_r)));
+   particle.setLinearVelocity(0, Vec3(0, 0, osc.analyticalVel(0_r)));
+   particle.setInvMass(0, 1_r / osc.mass);
+   particle.setForce(0, osc.getForce(Vec3(0, 0, osc.analyticalPos(real_t(0))),
+                                        Vec3(0, 0, osc.analyticalVel(real_t(0)))));
+   particle.setOldForce(0, osc.getForce(Vec3(0, 0, osc.analyticalPos(-osc.dt)),
+                                           Vec3(0, 0, osc.analyticalVel(-osc.dt))));
+
+   // explicit euler
+   Integrator integrator(osc.dt);
+
+   real_t maxPosDeviation = 0_r;
+   real_t maxVelDeviation = 0_r;
+   real_t maxEneDeviation = 0_r;
+
+   for (auto i = uint_t(0); i <= osc.timeSteps; ++i)
+   {
+      real_t refPos = osc.analyticalPos(real_c(i) * osc.dt);
+      real_t refVel = osc.analyticalVel(real_c(i) * osc.dt);
+      real_t refEne = osc.getEnergy(refPos, refVel);
+
+      maxPosDeviation = std::max(maxPosDeviation, std::abs(particle.getPosition(0)[2] - refPos));
+      maxVelDeviation = std::max(maxVelDeviation, std::abs(particle.getLinearVelocity(0)[2] - refVel));
+      maxEneDeviation = std::max(maxEneDeviation, std::abs(osc.getEnergy(particle.getPosition(0)[2], particle.getLinearVelocity(0)[2]) - refEne));
+
+      std::cout << real_t(i) * osc.dt << " "
+                << refPos << " "
+                << refVel << " "
+                << refEne << " "
+                << particle.getPosition(0)[2] << " "
+                << particle.getLinearVelocity(0)[2] << " "
+                << osc.getEnergy(particle.getPosition(0)[2], particle.getLinearVelocity(0)[2]) << " "
+                << maxPosDeviation << " "
+                << maxVelDeviation << std::endl;
+
+      integrator(particle, osc);
+   }
+
+   return {maxPosDeviation, maxVelDeviation, maxEneDeviation};
+}
+
+int main(int argc, char **argv)
+{
+   mpi::Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+
+   Oscillator osc(argc, argv);
+
+   AccuracyResult res;
+//   res = walberla::checkIntegrator<walberla::ExplicitEuler>(osc);
+//   res = walberla::checkIntegrator<walberla::SemiImplicitEuler>(osc);
+   res = walberla::checkIntegrator<walberla::VelocityVerlet>(osc);
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace walberla
+
+
+/*
+ * Simulates a harmonic oscillator to test the accuracy of the integrators.
+ * Playground for integrator analysis. The corresponding unit test is located at
+ * tests/mesa_pd/kernel/IntegratorAccuracy.cpp
+ */
+int main(int argc, char **argv)
+{
+   return walberla::main(argc, argv);
+}
+
diff --git a/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.ipynb b/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..f0cc8feef715c2af5e8e6192175d4cec00f09b44
--- /dev/null
+++ b/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.ipynb
@@ -0,0 +1,156 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# error analysis of integration kernels"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import matplotlib.pyplot as plt\n",
+    "import numpy as np"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "integrator = 'euler'\n",
+    "data01 = np.loadtxt(f'{integrator}01.txt').transpose()\n",
+    "data02 = np.loadtxt(f'{integrator}02.txt').transpose()\n",
+    "data04 = np.loadtxt(f'{integrator}04.txt').transpose()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot(data01[0], data01[7], label='dt = 0.1')\n",
+    "plt.plot(data02[0], data02[7], label='dt = 0.2')\n",
+    "plt.plot(data04[0], data04[7], label='dt = 0.4')\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('max pos error')\n",
+    "plt.legend()\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot(data01[0], data01[8], label='dt = 0.1')\n",
+    "plt.plot(data02[0], data02[8], label='dt = 0.2')\n",
+    "plt.plot(data04[0], data04[8], label='dt = 0.4')\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('max vel error')\n",
+    "plt.legend()\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "final_errors = np.ones(3)\n",
+    "final_errors[0] = np.abs(data01[7][-1])\n",
+    "final_errors[1] = np.abs(data02[7][-1])\n",
+    "final_errors[2] = np.abs(data04[7][-1])\n",
+    "\n",
+    "plt.bar(np.arange(3), final_errors)\n",
+    "plt.show()\n",
+    "\n",
+    "print(final_errors[2] / final_errors[1])\n",
+    "print(final_errors[1] / final_errors[0])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot(data01[0], data01[1], label='analytic')\n",
+    "plt.plot(data01[0], data01[4], label='dt = 0.1')\n",
+    "plt.plot(data02[0], data02[4], label='dt = 0.2')\n",
+    "plt.plot(data04[0], data04[4], label='dt = 0.4')\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('pos')\n",
+    "plt.legend()\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot(data01[0], data01[2], label='analytic')\n",
+    "plt.plot(data01[0], data01[5], label='dt = 0.1')\n",
+    "plt.plot(data02[0], data02[5], label='dt = 0.2')\n",
+    "plt.plot(data04[0], data04[5], label='dt = 0.4')\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('vel')\n",
+    "plt.legend()\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot(data01[0], data01[6] / data01[3][0], label='dt = 0.1')\n",
+    "plt.plot(data02[0], data02[6] / data01[3][0], label='dt = 0.2')\n",
+    "plt.plot(data04[0], data04[6] / data01[3][0], label='dt = 0.4')\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('relative energy')\n",
+    "plt.legend()\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.4"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/python/mesa_pd.py b/python/mesa_pd.py
index cc8b5309131ae2e90b3cd32a04bd77599fef9256..0d94b5a92b101d927cabcd4e44c1a99302870142 100755
--- a/python/mesa_pd.py
+++ b/python/mesa_pd.py
@@ -103,6 +103,7 @@ if __name__ == '__main__':
     mpd.add(kernel.InsertParticleIntoSparseLinkedCells())
     mpd.add(kernel.LinearSpringDashpot())
     mpd.add(kernel.NonLinearSpringDashpot())
+    mpd.add(kernel.SemiImplicitEuler())
     mpd.add(kernel.SingleCast(ps))
     mpd.add(kernel.SpringDashpot())
     mpd.add(kernel.SpringDashpotSpring())
diff --git a/python/mesa_pd/kernel/SemiImplicitEuler.py b/python/mesa_pd/kernel/SemiImplicitEuler.py
new file mode 100644
index 0000000000000000000000000000000000000000..f5b6632a1f57d7dba5631896a3bd26b5a5cce177
--- /dev/null
+++ b/python/mesa_pd/kernel/SemiImplicitEuler.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
+
+
+class SemiImplicitEuler:
+    def __init__(self, integrate_rotation=True):
+        self.context = {'bIntegrateRotation': integrate_rotation,
+                        'interface': [
+                            create_access("position", "walberla::mesa_pd::Vec3", access="gs"),
+                            create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="gs"),
+                            create_access("invMass", "walberla::real_t", access="g"),
+                            create_access("force", "walberla::mesa_pd::Vec3", access="gs"),
+                            create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g")
+                        ]
+                        }
+
+        if integrate_rotation:
+            self.context['interface'].append(create_access("rotation", "walberla::mesa_pd::Rot3", access="gs"))
+            self.context['interface'].append(create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="gs"))
+            self.context['interface'].append(create_access("invInertiaBF", "walberla::mesa_pd::Mat3", access="g"))
+            self.context['interface'].append(create_access("torque", "walberla::mesa_pd::Vec3", access="gs"))
+
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/SemiImplicitEuler.templ.h', ctx)
+
+        ctx["InterfaceTestName"] = "SemiImplicitEulerInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/SemiImplicitEuler.h"
+        ctx["SemiImplicitEulerInstantiation"] = \
+            "template void kernel::SemiImplicitEuler::operator()(const size_t p_idx1, Accessor& ac) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/SemiImplicitEulerInterfaceCheck.cpp')
diff --git a/python/mesa_pd/kernel/__init__.py b/python/mesa_pd/kernel/__init__.py
index 317646d1e961b70c81c12e877161b353d9840bd8..4a639693bde73fbcb50cff624750a058e1519271 100644
--- a/python/mesa_pd/kernel/__init__.py
+++ b/python/mesa_pd/kernel/__init__.py
@@ -12,6 +12,7 @@ from .InsertParticleIntoLinkedCells import InsertParticleIntoLinkedCells
 from .InsertParticleIntoSparseLinkedCells import InsertParticleIntoSparseLinkedCells
 from .LinearSpringDashpot import LinearSpringDashpot
 from .NonLinearSpringDashpot import NonLinearSpringDashpot
+from .SemiImplicitEuler import SemiImplicitEuler
 from .SingleCast import SingleCast
 from .SpringDashpot import SpringDashpot
 from .SpringDashpotSpring import SpringDashpotSpring
@@ -31,6 +32,7 @@ __all__ = ['DoubleCast',
            'InsertParticleIntoSparseLinkedCells',
            'LinearSpringDashpot',
            'NonLinearSpringDashpot',
+           'SemiImplicitEuler',
            'SingleCast',
            'SpringDashpot',
            'SpringDashpotSpring',
diff --git a/python/mesa_pd/templates/kernel/ExplicitEuler.templ.h b/python/mesa_pd/templates/kernel/ExplicitEuler.templ.h
index e3916291b679d64f6559e0b35d4b651690f75dc7..79b1518b3f32e6557b49463edf4a8f8f07ffe46e 100644
--- a/python/mesa_pd/templates/kernel/ExplicitEuler.templ.h
+++ b/python/mesa_pd/templates/kernel/ExplicitEuler.templ.h
@@ -34,7 +34,12 @@ namespace mesa_pd {
 namespace kernel {
 
 /**
- * Kernel which explicitly integrates all particles in time.
+ * Explicit Euler integration.
+ * Uses the 0.5at^2 extension for position integration.
+ * Boils down to using v_{t+0.5dt} for position integration.
+ * This version exhibits increased stability compared to standard explicit euler.
+ *
+ * Wachs, A. Particle-scale computational approaches to model dry and saturated granular flows of non-Brownian, non-cohesive, and non-spherical rigid bodies. Acta Mech 230, 1919–1980 (2019). https://doi.org/10.1007/s00707-019-02389-9
  *
  * This kernel requires the following particle accessor interface
  * \code
@@ -75,15 +80,19 @@ inline void ExplicitEuler::operator()(const size_t idx,
 
    if (!data::particle_flags::isSet( ac.getFlags(idx), data::particle_flags::FIXED))
    {
-      ac.setPosition      (idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ * dt_ + ac.getLinearVelocity(idx) * dt_ + ac.getPosition(idx));
-      ac.setLinearVelocity(idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ + ac.getLinearVelocity(idx));
+      ac.setPosition      (idx, 0.5_r * ac.getInvMass(idx) * ac.getForce(idx) * dt_ * dt_ +
+                                ac.getLinearVelocity(idx) * dt_ +
+                                ac.getPosition(idx));
+      ac.setLinearVelocity(idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ +
+                                ac.getLinearVelocity(idx));
 
       {%- if bIntegrateRotation %}
       const Vec3 wdot = math::transformMatrixRART(ac.getRotation(idx).getMatrix(),
                                                   ac.getInvInertiaBF(idx)) * ac.getTorque(idx);
 
       // Calculating the rotation angle
-      const Vec3 phi( ac.getAngularVelocity(idx) * dt_ + wdot * dt_ * dt_);
+      const Vec3 phi( 0.5_r * wdot * dt_ * dt_ +
+                      ac.getAngularVelocity(idx) * dt_ );
 
       // Calculating the new orientation
       auto rotation = ac.getRotation(idx);
diff --git a/python/mesa_pd/templates/kernel/SemiImplicitEuler.templ.h b/python/mesa_pd/templates/kernel/SemiImplicitEuler.templ.h
new file mode 100644
index 0000000000000000000000000000000000000000..7045cbfe1ec8cc74f72d9554d16f10503745f01a
--- /dev/null
+++ b/python/mesa_pd/templates/kernel/SemiImplicitEuler.templ.h
@@ -0,0 +1,109 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file SemiImplicitEuler.h
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+
+/**
+ * Semi-implicit Euler integration for position and velocity.
+ *
+ * This kernel requires the following particle accessor interface
+ * \code
+   {%- for prop in interface %}
+   {%- if 'g' in prop.access %}
+ * const {{prop.type}}& get{{prop.name | capFirst}}(const size_t p_idx) const;
+   {%- endif %}
+   {%- if 's' in prop.access %}
+ * void set{{prop.name | capFirst}}(const size_t p_idx, const {{prop.type}}& v);
+   {%- endif %}
+   {%- if 'r' in prop.access %}
+ * {{prop.type}}& get{{prop.name | capFirst}}Ref(const size_t p_idx);
+   {%- endif %}
+ *
+   {%- endfor %}
+ * \endcode
+ *
+ * \pre  All forces and torques acting on the particles have to be set.
+ * \post All forces and torques are reset to 0.
+ * \ingroup mesa_pd_kernel
+ */
+class SemiImplicitEuler
+{
+public:
+   explicit SemiImplicitEuler(const real_t dt) : dt_(dt) {}
+
+   template <typename Accessor>
+   void operator()(const size_t i, Accessor& ac) const;
+private:
+   real_t dt_ = real_t(0.0);
+};
+
+template <typename Accessor>
+inline void SemiImplicitEuler::operator()(const size_t idx,
+                                          Accessor& ac) const
+{
+   static_assert(std::is_base_of<data::IAccessor, Accessor>::value, "please provide a valid accessor");
+
+   if (!data::particle_flags::isSet( ac.getFlags(idx), data::particle_flags::FIXED))
+   {
+      ac.setLinearVelocity( idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ +
+                                 ac.getLinearVelocity(idx));
+      ac.setPosition      ( idx, ac.getLinearVelocity(idx) * dt_ +
+                                 ac.getPosition(idx));
+
+      {%- if bIntegrateRotation %}
+      const Vec3 wdot = math::transformMatrixRART(ac.getRotation(idx).getMatrix(),
+                                                  ac.getInvInertiaBF(idx)) * ac.getTorque(idx);
+
+      ac.setAngularVelocity(idx, wdot * dt_ +
+                                 ac.getAngularVelocity(idx));
+
+      // Calculating the rotation angle
+      const Vec3 phi( ac.getAngularVelocity(idx) * dt_ );
+
+      // Calculating the new orientation
+      auto rotation = ac.getRotation(idx);
+      rotation.rotate( phi );
+      ac.setRotation(idx, rotation);
+
+      {%- endif %}
+   }
+
+   ac.setForce (idx, Vec3(real_t(0), real_t(0), real_t(0)));
+   {%- if bIntegrateRotation %}
+   ac.setTorque(idx, Vec3(real_t(0), real_t(0), real_t(0)));
+   {%- endif %}
+}
+
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
diff --git a/python/mesa_pd/templates/kernel/VelocityVerlet.templ.h b/python/mesa_pd/templates/kernel/VelocityVerlet.templ.h
index 148ac8c8d96d71d4cb7a0af97805689236d46a35..98d97b628174fc04f082517336bc709766b62a19 100644
--- a/python/mesa_pd/templates/kernel/VelocityVerlet.templ.h
+++ b/python/mesa_pd/templates/kernel/VelocityVerlet.templ.h
@@ -41,6 +41,8 @@ namespace kernel {
  * integration is only complete when both functions are called. The integration
  * is symplectic.
  *
+ * Wachs, A. Particle-scale computational approaches to model dry and saturated granular flows of non-Brownian, non-cohesive, and non-spherical rigid bodies. Acta Mech 230, 1919–1980 (2019). https://doi.org/10.1007/s00707-019-02389-9
+ *
  * This kernel requires the following particle accessor interface
  * \code
    {%- for prop in interface %}
diff --git a/src/mesa_pd/kernel/ExplicitEuler.h b/src/mesa_pd/kernel/ExplicitEuler.h
index 8887219b19e206bf4e5b193b7090e3ae989886ce..639e3671a8712875796060e4cace0701ae6f74ff 100644
--- a/src/mesa_pd/kernel/ExplicitEuler.h
+++ b/src/mesa_pd/kernel/ExplicitEuler.h
@@ -34,7 +34,12 @@ namespace mesa_pd {
 namespace kernel {
 
 /**
- * Kernel which explicitly integrates all particles in time.
+ * Explicit Euler integration.
+ * Uses the 0.5at^2 extension for position integration.
+ * Boils down to using v_{t+0.5dt} for position integration.
+ * This version exhibits increased stability compared to standard explicit euler.
+ *
+ * Wachs, A. Particle-scale computational approaches to model dry and saturated granular flows of non-Brownian, non-cohesive, and non-spherical rigid bodies. Acta Mech 230, 1919–1980 (2019). https://doi.org/10.1007/s00707-019-02389-9
  *
  * This kernel requires the following particle accessor interface
  * \code
@@ -87,13 +92,17 @@ inline void ExplicitEuler::operator()(const size_t idx,
 
    if (!data::particle_flags::isSet( ac.getFlags(idx), data::particle_flags::FIXED))
    {
-      ac.setPosition      (idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ * dt_ + ac.getLinearVelocity(idx) * dt_ + ac.getPosition(idx));
-      ac.setLinearVelocity(idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ + ac.getLinearVelocity(idx));
+      ac.setPosition      (idx, 0.5_r * ac.getInvMass(idx) * ac.getForce(idx) * dt_ * dt_ +
+                                ac.getLinearVelocity(idx) * dt_ +
+                                ac.getPosition(idx));
+      ac.setLinearVelocity(idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ +
+                                ac.getLinearVelocity(idx));
       const Vec3 wdot = math::transformMatrixRART(ac.getRotation(idx).getMatrix(),
                                                   ac.getInvInertiaBF(idx)) * ac.getTorque(idx);
 
       // Calculating the rotation angle
-      const Vec3 phi( ac.getAngularVelocity(idx) * dt_ + wdot * dt_ * dt_);
+      const Vec3 phi( 0.5_r * wdot * dt_ * dt_ +
+                      ac.getAngularVelocity(idx) * dt_ );
 
       // Calculating the new orientation
       auto rotation = ac.getRotation(idx);
diff --git a/src/mesa_pd/kernel/SemiImplicitEuler.h b/src/mesa_pd/kernel/SemiImplicitEuler.h
new file mode 100644
index 0000000000000000000000000000000000000000..cf0df0734ea6bc7e5120809ef044c0e96f1e708b
--- /dev/null
+++ b/src/mesa_pd/kernel/SemiImplicitEuler.h
@@ -0,0 +1,115 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file SemiImplicitEuler.h
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+
+/**
+ * Semi-implicit Euler integration for position and velocity.
+ *
+ * This kernel requires the following particle accessor interface
+ * \code
+ * const walberla::mesa_pd::Vec3& getPosition(const size_t p_idx) const;
+ * void setPosition(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::mesa_pd::Vec3& getLinearVelocity(const size_t p_idx) const;
+ * void setLinearVelocity(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::real_t& getInvMass(const size_t p_idx) const;
+ *
+ * const walberla::mesa_pd::Vec3& getForce(const size_t p_idx) const;
+ * void setForce(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::mesa_pd::data::particle_flags::FlagT& getFlags(const size_t p_idx) const;
+ *
+ * const walberla::mesa_pd::Rot3& getRotation(const size_t p_idx) const;
+ * void setRotation(const size_t p_idx, const walberla::mesa_pd::Rot3& v);
+ *
+ * const walberla::mesa_pd::Vec3& getAngularVelocity(const size_t p_idx) const;
+ * void setAngularVelocity(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::mesa_pd::Mat3& getInvInertiaBF(const size_t p_idx) const;
+ *
+ * const walberla::mesa_pd::Vec3& getTorque(const size_t p_idx) const;
+ * void setTorque(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * \endcode
+ *
+ * \pre  All forces and torques acting on the particles have to be set.
+ * \post All forces and torques are reset to 0.
+ * \ingroup mesa_pd_kernel
+ */
+class SemiImplicitEuler
+{
+public:
+   explicit SemiImplicitEuler(const real_t dt) : dt_(dt) {}
+
+   template <typename Accessor>
+   void operator()(const size_t i, Accessor& ac) const;
+private:
+   real_t dt_ = real_t(0.0);
+};
+
+template <typename Accessor>
+inline void SemiImplicitEuler::operator()(const size_t idx,
+                                          Accessor& ac) const
+{
+   static_assert(std::is_base_of<data::IAccessor, Accessor>::value, "please provide a valid accessor");
+
+   if (!data::particle_flags::isSet( ac.getFlags(idx), data::particle_flags::FIXED))
+   {
+      ac.setLinearVelocity( idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ +
+                                 ac.getLinearVelocity(idx));
+      ac.setPosition      ( idx, ac.getLinearVelocity(idx) * dt_ +
+                                 ac.getPosition(idx));
+      const Vec3 wdot = math::transformMatrixRART(ac.getRotation(idx).getMatrix(),
+                                                  ac.getInvInertiaBF(idx)) * ac.getTorque(idx);
+
+      ac.setAngularVelocity(idx, wdot * dt_ +
+                                 ac.getAngularVelocity(idx));
+
+      // Calculating the rotation angle
+      const Vec3 phi( ac.getAngularVelocity(idx) * dt_ );
+
+      // Calculating the new orientation
+      auto rotation = ac.getRotation(idx);
+      rotation.rotate( phi );
+      ac.setRotation(idx, rotation);
+   }
+
+   ac.setForce (idx, Vec3(real_t(0), real_t(0), real_t(0)));
+   ac.setTorque(idx, Vec3(real_t(0), real_t(0), real_t(0)));
+}
+
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/kernel/VelocityVerlet.h b/src/mesa_pd/kernel/VelocityVerlet.h
index f029652c27e4c1e5e4ac6e81d27b083e59e3a7b6..69ed50dcb468111399c11a70534087af9255a69f 100644
--- a/src/mesa_pd/kernel/VelocityVerlet.h
+++ b/src/mesa_pd/kernel/VelocityVerlet.h
@@ -41,6 +41,8 @@ namespace kernel {
  * integration is only complete when both functions are called. The integration
  * is symplectic.
  *
+ * Wachs, A. Particle-scale computational approaches to model dry and saturated granular flows of non-Brownian, non-cohesive, and non-spherical rigid bodies. Acta Mech 230, 1919–1980 (2019). https://doi.org/10.1007/s00707-019-02389-9
+ *
  * This kernel requires the following particle accessor interface
  * \code
  * const walberla::mesa_pd::Vec3& getPosition(const size_t p_idx) const;
diff --git a/tests/mesa_pd/CMakeLists.txt b/tests/mesa_pd/CMakeLists.txt
index 3742a2ef2dcabdd68d50fe0c436fd0b21d360435..caf3ba2752b2630203a42de911480bb4969eda00 100644
--- a/tests/mesa_pd/CMakeLists.txt
+++ b/tests/mesa_pd/CMakeLists.txt
@@ -128,6 +128,9 @@ waLBerla_execute_test( NAME   MESA_PD_Kernel_LinearSpringDashpot )
 waLBerla_compile_test( NAME   MESA_PD_Kernel_LinkedCellsVsBruteForce FILES kernel/LinkedCellsVsBruteForce.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_Kernel_LinkedCellsVsBruteForce PROCESSES 27 )
 
+waLBerla_compile_test( NAME   MESA_PD_Kernel_SemiImplicitEuler FILES kernel/SemiImplicitEuler.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Kernel_SemiImplicitEuler )
+
 waLBerla_compile_test( NAME   MESA_PD_Kernel_SingleCast FILES kernel/SingleCast.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_Kernel_SingleCast )
 
@@ -197,4 +200,4 @@ if (WALBERLA_MESAPD_CONVEX_POLYHEDRON_AVAILABLE)
 
     waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_ConvexPolyhedron_GJK_EPA FILES collision_detection/ConvexPolyhedron_GJK_EPA.cpp DEPENDS core mesa_pd mesh_common )
     waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_ConvexPolyhedron_GJK_EPA )
-endif()
\ No newline at end of file
+endif()
diff --git a/tests/mesa_pd/DropTestAnalytic.cpp b/tests/mesa_pd/DropTestAnalytic.cpp
index 7c3c89ed0b3caec83e9bb699a1062a2e26ef44d9..3b257e23c0a3cac077300db2a1fe3bc79087b038 100644
--- a/tests/mesa_pd/DropTestAnalytic.cpp
+++ b/tests/mesa_pd/DropTestAnalytic.cpp
@@ -25,7 +25,7 @@
 #include <mesa_pd/data/ShapeStorage.h>
 
 #include <mesa_pd/kernel/DoubleCast.h>
-#include <mesa_pd/kernel/ExplicitEuler.h>
+#include <mesa_pd/kernel/SemiImplicitEuler.h>
 #include <mesa_pd/kernel/ParticleSelector.h>
 #include <mesa_pd/kernel/SpringDashpot.h>
 
@@ -102,7 +102,7 @@ int main( int argc, char ** argv )
    real_t dt = real_t(0.00001);
 
    // Init kernels
-   kernel::ExplicitEuler                 explicitEuler( dt );
+   kernel::SemiImplicitEuler             implEuler( dt );
    kernel::SpringDashpot                 dem(1);
    auto meff = real_t(1.0) / ss->shapes[sp->getShapeID()]->getInvMass();
    dem.setParametersFromCOR(0,0,real_t(0.9), dt * real_t(20), meff);
@@ -152,7 +152,7 @@ int main( int argc, char ** argv )
       ps->forEachParticle(false,
                           kernel::SelectLocal(),
                           accessor,
-                          explicitEuler,
+                          implEuler,
                           accessor);
 
 //      if(i%1 == 0)
diff --git a/tests/mesa_pd/DropTestGeneral.cpp b/tests/mesa_pd/DropTestGeneral.cpp
index 3d668694c7f3429e12354e561a24389ca4e87443..0ab23c4fa42b9265200c11045c6b79d4773dd18d 100644
--- a/tests/mesa_pd/DropTestGeneral.cpp
+++ b/tests/mesa_pd/DropTestGeneral.cpp
@@ -25,16 +25,14 @@
 #include <mesa_pd/data/ShapeStorage.h>
 
 #include <mesa_pd/kernel/DoubleCast.h>
-#include <mesa_pd/kernel/ExplicitEuler.h>
+#include <mesa_pd/kernel/SemiImplicitEuler.h>
 #include <mesa_pd/kernel/ParticleSelector.h>
 #include <mesa_pd/kernel/SpringDashpot.h>
 
 #include <core/Abort.h>
 #include <core/Environment.h>
 #include <core/logging/Logging.h>
-#include <core/waLBerlaBuildInfo.h>
 
-#include <functional>
 #include <memory>
 #include <string>
 #include <type_traits>
@@ -122,7 +120,7 @@ int main( int argc, char ** argv )
    real_t dt = real_t(0.00001);
 
    // Init kernels
-   kernel::ExplicitEuler                 explicitEuler( dt );
+   kernel::SemiImplicitEuler             implEuler( dt );
    kernel::SpringDashpot                 dem(1);
    auto meff = real_t(1.0) / ss->shapes[sp->getShapeID()]->getInvMass();
    dem.setParametersFromCOR(0,0,real_t(0.9), dt * real_t(20), meff);
@@ -172,7 +170,7 @@ int main( int argc, char ** argv )
       ps->forEachParticle(false,
                           kernel::SelectLocal(),
                           accessor,
-                          explicitEuler,
+                          implEuler,
                           accessor);
 
 //      if(i%1 == 0)
diff --git a/tests/mesa_pd/kernel/CoefficientOfRestitutionLSD.cpp b/tests/mesa_pd/kernel/CoefficientOfRestitutionLSD.cpp
index 7e316264f934e5088e33028a4dc9fe96714f440b..ac16d145e73e75ccfa02cfc151aa09b046766f32 100644
--- a/tests/mesa_pd/kernel/CoefficientOfRestitutionLSD.cpp
+++ b/tests/mesa_pd/kernel/CoefficientOfRestitutionLSD.cpp
@@ -13,7 +13,7 @@
 //  You should have received a copy of the GNU General Public License along
 //  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
 //
-//! \file DEMIntegratorAccuracy.cpp
+//! \file CoefficientOfRestitutionLSD.cpp
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
@@ -26,7 +26,7 @@
 
 #include "mesa_pd/kernel/DoubleCast.h"
 #include "mesa_pd/kernel/VelocityVerlet.h"
-#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
 #include "mesa_pd/kernel/LinearSpringDashpot.h"
 
 #include "core/Environment.h"
@@ -136,7 +136,7 @@ int main( int argc, char** argv )
    collision_detection::AnalyticContactDetection acd;
    kernel::DoubleCast       double_cast;
 
-   kernel::ExplicitEuler explEuler(dt);
+   kernel::SemiImplicitEuler implEuler(dt);
    kernel::VelocityVerletPreForceUpdate  vvPreForce( dt );
    kernel::VelocityVerletPostForceUpdate vvPostForce( dt );
 
@@ -160,7 +160,7 @@ int main( int argc, char** argv )
       auto force = accessor->getForce(0);
 
       if(useVelocityVerlet) vvPostForce(0,*accessor);
-      else explEuler(0, *accessor);
+      else implEuler(0, *accessor);
 
       WALBERLA_LOG_INFO(steps << ": penetration = " << acd.getPenetrationDepth() << " || vel = " << accessor->getLinearVelocity(0)[2] << " || force = " << force[2]);
 
diff --git a/tests/mesa_pd/kernel/CoefficientOfRestitutionNLSD.cpp b/tests/mesa_pd/kernel/CoefficientOfRestitutionNLSD.cpp
index c422a70b497527a3ff6ef5aaf4181168a7ec9ddb..c2bfbcfdb7160555bc8945a8400b7a06fdf248cb 100644
--- a/tests/mesa_pd/kernel/CoefficientOfRestitutionNLSD.cpp
+++ b/tests/mesa_pd/kernel/CoefficientOfRestitutionNLSD.cpp
@@ -13,7 +13,7 @@
 //  You should have received a copy of the GNU General Public License along
 //  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
 //
-//! \file DEMIntegratorAccuracy.cpp
+//! \file CoefficientOfRestitutionNLSD.cpp
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
@@ -26,7 +26,7 @@
 
 #include "mesa_pd/kernel/DoubleCast.h"
 #include "mesa_pd/kernel/VelocityVerlet.h"
-#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
 #include "mesa_pd/kernel/NonLinearSpringDashpot.h"
 #include "mesa_pd/mpi/ReduceContactHistory.h"
 
@@ -137,7 +137,7 @@ int main( int argc, char** argv )
    collision_detection::AnalyticContactDetection acd;
    kernel::DoubleCast       double_cast;
 
-   kernel::ExplicitEuler explEuler(dt);
+   kernel::SemiImplicitEuler implEuler(dt);
    kernel::VelocityVerletPreForceUpdate  vvPreForce( dt );
    kernel::VelocityVerletPostForceUpdate vvPostForce( dt );
 
@@ -162,7 +162,7 @@ int main( int argc, char** argv )
       auto force = accessor->getForce(0);
 
       if(useVelocityVerlet) vvPostForce(0,*accessor);
-      else explEuler(0, *accessor);
+      else implEuler(0, *accessor);
 
       WALBERLA_LOG_INFO(steps << ": penetration = " << acd.getPenetrationDepth() << " || vel = " << accessor->getLinearVelocity(0)[2] << " || force = " << force[2]);
 
diff --git a/tests/mesa_pd/kernel/CoefficientOfRestitutionSD.cpp b/tests/mesa_pd/kernel/CoefficientOfRestitutionSD.cpp
index 300e0eb916d6f19330ccc873733387fbd47981e3..910f5da3d3d68029778879db3c5bd8b641c8dbcb 100644
--- a/tests/mesa_pd/kernel/CoefficientOfRestitutionSD.cpp
+++ b/tests/mesa_pd/kernel/CoefficientOfRestitutionSD.cpp
@@ -13,7 +13,7 @@
 //  You should have received a copy of the GNU General Public License along
 //  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
 //
-//! \file DEMIntegratorAccuracy.cpp
+//! \file CoefficientOfRestitutionSD.cpp
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
@@ -26,7 +26,7 @@
 
 #include "mesa_pd/kernel/DoubleCast.h"
 #include "mesa_pd/kernel/VelocityVerlet.h"
-#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
 #include "mesa_pd/kernel/SpringDashpot.h"
 
 #include "core/Environment.h"
@@ -136,7 +136,7 @@ int main( int argc, char** argv )
    collision_detection::AnalyticContactDetection acd;
    kernel::DoubleCast       double_cast;
 
-   kernel::ExplicitEuler explEuler(dt);
+   kernel::SemiImplicitEuler implEuler(dt);
    kernel::VelocityVerletPreForceUpdate  vvPreForce( dt );
    kernel::VelocityVerletPostForceUpdate vvPostForce( dt );
 
@@ -160,7 +160,7 @@ int main( int argc, char** argv )
       auto force = accessor->getForce(0);
 
       if(useVelocityVerlet) vvPostForce(0,*accessor);
-      else explEuler(0, *accessor);
+      else implEuler(0, *accessor);
 
       WALBERLA_LOG_INFO(steps << ": penetration = " << acd.getPenetrationDepth() << " || vel = " << accessor->getLinearVelocity(0)[2] << " || force = " << force[2]);
 
diff --git a/tests/mesa_pd/kernel/ExplicitEuler.cpp b/tests/mesa_pd/kernel/ExplicitEuler.cpp
index 1685ff1aed29b315bfd36be07ef45b0465b44908..17d55505b02fe16895825eca3fa2c1588eb98c71 100644
--- a/tests/mesa_pd/kernel/ExplicitEuler.cpp
+++ b/tests/mesa_pd/kernel/ExplicitEuler.cpp
@@ -66,8 +66,10 @@ int main( int argc, char ** argv )
    accessor.setAngularVelocity( 0, angVel);
    accessor.setForce(           0, force);
    accessor.setTorque(          0, torque);
-   accessor.setInvMass(         0, real_t(1.23456));
-   accessor.setInvInertiaBF(    0, Mat3(real_t(1.23456), real_t(0), real_t(0), real_t(0), real_t(1.23456), real_t(0), real_t(0), real_t(0), real_t(1.23456)));
+   accessor.setInvMass(         0, 1.23456_r);
+   accessor.setInvInertiaBF(    0, Mat3(1.23456_r, 0_r, 0_r,
+                                        0_r, 1.23456_r, 0_r,
+                                        0_r, 0_r, 1.23456_r ));
 
    //init kernels
    const real_t dt = real_t(1);
@@ -83,12 +85,16 @@ int main( int argc, char ** argv )
    WALBERLA_CHECK_FLOAT_EQUAL(accessor.getTorque(0), Vec3(0));
 
    //check velocity
-   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getLinearVelocity(0), force * accessor.getInvMass(0) * dt + linVel);
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getLinearVelocity(0), force * accessor.getInvMass(0) * dt +
+                                                             linVel);
    WALBERLA_CHECK_FLOAT_EQUAL(accessor.getAngularVelocity(0), wdot * dt + angVel);
 
    //check position
-   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getPosition(0), linVel * dt + force * accessor.getInvMass(0) * dt * dt);
-   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getRotation(0).getQuaternion(), Quat( (wdot * dt + angVel).getNormalized(), (wdot * dt + angVel).length() * dt ));
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getPosition(0), linVel * dt +
+                                                       0.5_r * force * accessor.getInvMass(0) * dt * dt);
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getRotation(0).getQuaternion(),
+                              Quat( (wdot * dt + angVel).getNormalized(),
+                                    (0.5_r * wdot * dt + angVel).length() * dt ));
 
    accessor.setPosition(        0, Vec3(0,0,0));
    accessor.setRotation(        0, Rot3(Quat()));
@@ -96,8 +102,10 @@ int main( int argc, char ** argv )
    accessor.setAngularVelocity( 0, angVel);
    accessor.setForce(           0, force);
    accessor.setTorque(          0, torque);
-   accessor.setInvMass(         0, real_t(1.23456));
-   accessor.setInvInertiaBF(    0, Mat3(real_t(1.23456), real_t(0), real_t(0), real_t(0), real_t(1.23456), real_t(0), real_t(0), real_t(0), real_t(1.23456)));
+   accessor.setInvMass(         0, 1.23456_r);
+   accessor.setInvInertiaBF(    0, Mat3(1.23456_r, 0_r, 0_r,
+                                           0_r, 1.23456_r, 0_r,
+                                           0_r, 0_r, 1.23456_r ));
    data::particle_flags::set( accessor.getFlagsRef(0), data::particle_flags::FIXED );
 
    integrator(0, accessor);
diff --git a/tests/mesa_pd/kernel/GenerateLinkedCells.cpp b/tests/mesa_pd/kernel/GenerateLinkedCells.cpp
index e7a60f36ed57cfc8a05fbda3c14d884b291f73c6..d4c2f30c678c7bb03ed4bc565c3a163fee6a531a 100644
--- a/tests/mesa_pd/kernel/GenerateLinkedCells.cpp
+++ b/tests/mesa_pd/kernel/GenerateLinkedCells.cpp
@@ -18,7 +18,7 @@
 //
 //======================================================================================================================
 
-#include <mesa_pd/kernel/ExplicitEuler.h>
+#include <mesa_pd/kernel/SemiImplicitEuler.h>
 #include <mesa_pd/kernel/ForceLJ.h>
 #include <mesa_pd/kernel/InsertParticleIntoLinkedCells.h>
 #include <mesa_pd/kernel/ParticleSelector.h>
@@ -72,7 +72,7 @@ int main( int argc, char ** argv )
    //init kernels
    kernel::InsertParticleIntoLinkedCells ipilc;
    kernel::ForceLJ lj(1);
-   kernel::ExplicitEuler integrator( real_t(0.01) );
+   kernel::SemiImplicitEuler integrator( real_t(0.01) );
 
    //timeloop
    for (auto timestep = 0; timestep < 100; ++timestep)
diff --git a/tests/mesa_pd/kernel/IntegratorAccuracy.cpp b/tests/mesa_pd/kernel/IntegratorAccuracy.cpp
index 695bae5b9662299ceb77583d726fab3e012e4b62..e97c47106b1cd741e0652654601dd986f3d0cb1b 100644
--- a/tests/mesa_pd/kernel/IntegratorAccuracy.cpp
+++ b/tests/mesa_pd/kernel/IntegratorAccuracy.cpp
@@ -15,170 +15,252 @@
 //
 //! \file   IntegratorAccuracy.cpp
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
 
-#include "mesa_pd/data/ParticleAccessorWithShape.h"
-#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ParticleAccessor.h"
 
-#include "mesa_pd/kernel/ParticleSelector.h"
-#include "mesa_pd/kernel/VelocityVerlet.h"
 #include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
 
 #include "core/Environment.h"
-#include "core/logging/Logging.h"
 #include "core/math/all.h"
 
 #include <iostream>
 
-namespace integrator_accuracy
-{
-
-using namespace walberla;
+namespace walberla {
 
 using namespace walberla::mesa_pd;
 
-
-Vec3 getForce(const Vec3& pos, real_t k)
+class SingleParticleAccessorWithShape : public data::SingleParticleAccessor
 {
-   return -k * pos;
-}
+public:
+   void setInvMass(const size_t /*p_idx*/, const real_t &val)
+   { invMass_ = val; }
 
-real_t analyticalTrajectory(real_t amplitude, real_t timeStep, real_t omega, real_t phase)
-{
-   return amplitude * std::cos(omega * timeStep + phase);
-}
+   const auto &getInvMass(const size_t /*p_idx*/) const
+   { return invMass_; }
 
-real_t analyticalVelocity(real_t amplitude, real_t timeStep, real_t omega, real_t phase)
-{
-   return -amplitude * omega * std::sin(omega * timeStep + phase);
-}
+   void setInvInertiaBF(const size_t /*p_idx*/, const Mat3 &val)
+   { invInertiaBF_ = val; }
 
+   const auto &getInvInertiaBF(const size_t /*p_idx*/) const
+   { return invInertiaBF_; }
 
-/*
- * Simulates a harmonic oscillator to test the accuracy of the integrators.
- * The error of the maximum position and the maximum velocity is compared against the analytical values.
- * Via command line arguments, the simulation can be adapted.
- * Currently tested integrators:
- *  - explicit Euler (default)
- *  - velocity verlet (-useVV)
- */
-int main( int argc, char ** argv )
-{
-   mpi::Environment env(argc, argv);
-   WALBERLA_UNUSED(env);
-   mpi::MPIManager::instance()->useWorldComm();
+private:
+   real_t invMass_;
+   Mat3 invInertiaBF_;
+};
 
-   real_t amplitude       = real_t(1.5);
-   real_t k               = real_t(0.1);
-   real_t mass            = real_t(0.9);
-   real_t dt              = real_t(0.2);
-   bool useVelocityVerlet = false;
-   real_t phaseFraction   = real_t(0);
-   real_t periods         = real_t(1.1);
+struct Oscillator
+{
+   real_t amplitude = 1.5_r;
+   real_t k = 0.1_r;
+   real_t damping = 0_r;
+   real_t mass = 0.9_r;
+   real_t dt = 0.2_r;
+   real_t phaseFraction = 0_r;
+   real_t periods = 10_r;
+   real_t phase = phaseFraction * math::pi;
+   real_t dampingRatio = damping / (2_r * std::sqrt(mass * k));
+   real_t omega = std::sqrt(k / mass) * std::sqrt(1_r - dampingRatio * dampingRatio);
+   real_t decay = std::sqrt(k / mass) * dampingRatio;
+   real_t durationOnePeriod = 2_r * math::pi / omega;
+   uint_t timeSteps = uint_c(periods * durationOnePeriod / dt);
 
-   for( int i = 1; i < argc; ++i )
+   void update()
    {
-      if( std::strcmp( argv[i], "--useVV" )         == 0 ) { useVelocityVerlet = true; continue; }
-      if( std::strcmp( argv[i], "--dt" )            == 0 ) { dt = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--amplitude" )     == 0 ) { amplitude = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--k" )             == 0 ) { k = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--mass" )          == 0 ) { mass = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--phaseFraction" ) == 0 ) { phaseFraction = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--periods" )       == 0 ) { periods = real_c(std::atof( argv[++i] )); continue; }
-      WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
+      phase = phaseFraction * math::pi;
+      dampingRatio = damping / (2_r * std::sqrt(mass * k));
+      omega = std::sqrt(k / mass) * std::sqrt(1_r - dampingRatio * dampingRatio);
+      decay = std::sqrt(k / mass) * dampingRatio;
+      durationOnePeriod = 2_r * math::pi / omega;
+      timeSteps = uint_c(periods * durationOnePeriod / dt);
    }
 
+   Vec3 getForce(const Vec3 &pos, const Vec3 &vel) const
+   {
+      return -k * pos - damping * vel;
+   }
 
-   real_t phase = phaseFraction * math::pi;
-   real_t omega = std::sqrt(k / mass);
-   real_t durationOnePeriod = real_t(2) * math::pi / omega;
-   uint_t timeSteps = uint_c(periods * durationOnePeriod / dt);
+   real_t getEnergy(const real_t &pos, const real_t &vel) const
+   {
+      return 0.5_r * mass * vel * vel + 0.5_r * k * pos * pos;
+   }
 
-   WALBERLA_LOG_INFO("omega = " << omega << ", T = " << real_t(2) * math::pi / omega << ", time steps = " << timeSteps << ", phase = " << phase << ", periods = " << periods);
+   real_t analyticalPos(real_t t) const
+   {
+      return amplitude * std::exp(-decay * t) * std::cos(omega * t + phase);
+   }
 
-   //initialize particle
-   const auto pos = Vec3(0,0,analyticalTrajectory(amplitude, real_t(0), omega, phase));
-   const auto linVel = Vec3(0,0,analyticalVelocity(amplitude, real_t(0), omega, phase));
+   real_t analyticalVel(real_t t) const
+   {
+      return -decay * amplitude * std::exp(-decay * t) * std::cos(omega * t + phase)
+             -amplitude * std::exp(-decay * t) * omega * std::sin(omega * t + phase);
+   }
+};
 
-   WALBERLA_LOG_INFO("Initial pos = " << pos << ", vel = " << linVel);
+struct ExplicitEuler
+{
+   ExplicitEuler(real_t dt) : integrator(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
+   {
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      integrator(0, particle);
+   }
+   kernel::ExplicitEuler integrator;
+};
 
-   //init data structures
-   auto ps = std::make_shared<data::ParticleStorage>(1);
-   auto ss = std::make_shared<data::ShapeStorage>();
-   auto  smallSphere = ss->create<data::Sphere>( real_t(1) );
-   ss->shapes[smallSphere]->updateMassAndInertia(real_t(2707));
-   data::ParticleAccessorWithShape accessor(ps, ss);
-   
-   data::Particle&& p = *ps->create();
-   p.setPosition(pos);
-   p.setLinearVelocity(linVel);
-   p.setForce(getForce(pos, k));
-   p.setOldForce(getForce(pos, k));
-   p.setInvMass(real_t(1) / mass);
-   p.setShapeID(smallSphere);
-
-   // velocity verlet
-   kernel::VelocityVerletPreForceUpdate  preForce( dt );
-   kernel::VelocityVerletPostForceUpdate postForce( dt );
+struct SemiImplicitEuler
+{
+   SemiImplicitEuler(real_t dt) : integrator(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
+   {
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      integrator(0, particle);
+   }
+   kernel::SemiImplicitEuler integrator;
+};
 
-   // explicit euler
-   kernel::ExplicitEuler explEuler( dt );
-
-   real_t maxVel = 0.;
-   real_t maxRefVel = 0.;
-   real_t maxPos = 0.;
-   real_t maxRefPos = 0.;
-   
-   for (auto i = uint_t(1); i <= timeSteps; ++i)
+struct VelocityVerlet
+{
+   VelocityVerlet(real_t dt) : preVV(dt), postVV(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
    {
+      preVV(0, particle);
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      postVV(0, particle);
+   }
+   kernel::VelocityVerletPreForceUpdate preVV;
+   kernel::VelocityVerletPostForceUpdate postVV;
+};
 
-      if( useVelocityVerlet ) ps->forEachParticle(false, kernel::SelectAll(), accessor, preForce, accessor);
-      p.setForce( getForce(p.getPosition(), k) );
-      auto force = p.getForce();
+struct AccuracyResult
+{
+   real_t maxPosDeviation;
+   real_t maxVelDeviation;
+   real_t maxEneDeviation;
+};
 
-      if( useVelocityVerlet ) ps->forEachParticle(false, kernel::SelectAll(), accessor, postForce, accessor);
-      else  ps->forEachParticle(false, kernel::SelectAll(), accessor, explEuler, accessor);
+template <typename Integrator>
+AccuracyResult checkIntegrator(const Oscillator& osc)
+{
+   //init data structures
+   SingleParticleAccessorWithShape particle;
 
-      real_t refPos = analyticalTrajectory(amplitude, real_c(i) * dt, omega, phase);
-      real_t refVel = analyticalVelocity(amplitude, real_c(i) * dt, omega, phase);
+   //first dummy argument is needed to fulfill accessor interface
+   particle.setPosition(0, Vec3(0, 0, osc.analyticalPos(0_r)));
+   particle.setLinearVelocity(0, Vec3(0, 0, osc.analyticalVel(0_r)));
+   particle.setInvMass(0, 1_r / osc.mass);
+   particle.setForce(0, osc.getForce(Vec3(0, 0, osc.analyticalPos(real_t(0))),
+                                        Vec3(0, 0, osc.analyticalVel(real_t(0)))));
+   particle.setOldForce(0, osc.getForce(Vec3(0, 0, osc.analyticalPos(-osc.dt)),
+                                           Vec3(0, 0, osc.analyticalVel(-osc.dt))));
 
-      WALBERLA_LOG_INFO(i << ": pos = " << p.getPosition()[2] << " " << refPos
-                          << " || vel = " << p.getLinearVelocity()[2] << " " << refVel
-                          << " || force = " << force[2] );
+   // explicit euler
+   Integrator integrator(osc.dt);
 
-      maxPos = std::max(maxPos, std::abs(p.getPosition()[2]));
-      maxRefPos = std::max(maxRefPos, std::abs(refPos));
+   real_t maxPosDeviation = 0_r;
+   real_t maxVelDeviation = 0_r;
+   real_t maxEneDeviation = 0_r;
 
-      maxVel = std::max(maxVel, std::abs(p.getLinearVelocity()[2]));
-      maxRefVel = std::max(maxRefVel, std::abs(refVel));
+   for (auto i = uint_t(0); i <= osc.timeSteps; ++i)
+   {
+      real_t refPos = osc.analyticalPos(real_c(i) * osc.dt);
+      real_t refVel = osc.analyticalVel(real_c(i) * osc.dt);
+      real_t refEne = osc.getEnergy(refPos, refVel);
+
+      maxPosDeviation = std::max(maxPosDeviation, std::abs(particle.getPosition(0)[2] - refPos));
+      maxVelDeviation = std::max(maxVelDeviation, std::abs(particle.getLinearVelocity(0)[2] - refVel));
+      maxEneDeviation = std::max(maxEneDeviation, std::abs(osc.getEnergy(particle.getPosition(0)[2], particle.getLinearVelocity(0)[2]) - refEne));
+
+      integrator(particle, osc);
    }
 
-   real_t relativePositionError = ( maxPos - maxRefPos ) / maxRefPos;
-   WALBERLA_LOG_INFO("error in position = " << relativePositionError * 100. << "%");
+   return {maxPosDeviation, maxVelDeviation, maxEneDeviation};
+}
 
-   real_t relativeVelocityError = ( maxVel - maxRefVel ) / maxRefVel;
-   WALBERLA_LOG_INFO("error in velocity = " << relativeVelocityError * 100. << "%");
+int main(int argc, char **argv)
+{
+   mpi::Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
 
-   if( useVelocityVerlet )
-   {
-      WALBERLA_CHECK_LESS(relativePositionError, real_t(0.01), "Error in position too large!");
-      WALBERLA_CHECK_LESS(relativeVelocityError, real_t(0.01), "Error in velocity too large!");
-   }
-   else
+   if (std::is_same<real_t, float>::value)
    {
-      WALBERLA_CHECK_LESS(relativePositionError, real_t(0.11), "Error in position too large!");
-      WALBERLA_CHECK_LESS(relativeVelocityError, real_t(0.10), "Error in velocity too large!");
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
    }
 
+   Oscillator osc;
+
+   AccuracyResult res;
+   osc.dt = 0.1_r;
+   osc.update();
+   res = walberla::checkIntegrator<walberla::ExplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 1.03068993874562120e+00_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 3.33225581358688350e-01_r);
+   res = walberla::checkIntegrator<walberla::SemiImplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 2.92576360173544339e-02_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 1.45120298258364505e-03_r);
+   res = walberla::checkIntegrator<walberla::VelocityVerlet>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 4.24116598691555435e-03_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 1.45059589844465098e-03_r);
+
+   osc.dt = 0.2_r;
+   osc.update();
+   res = walberla::checkIntegrator<walberla::ExplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 2.76387000209972467e+00_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 8.88433661185269896e-01_r);
+   res = walberla::checkIntegrator<walberla::SemiImplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 6.70464626869100577e-02_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 5.81009940233766925e-03_r);
+   res = walberla::checkIntegrator<walberla::VelocityVerlet>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 1.69147419522671719e-02_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 5.78432113979018836e-03_r);
+
+   osc.dt = 0.4_r;
+   osc.update();
+   res = walberla::checkIntegrator<walberla::ExplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 1.04680753378045406e+01_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 3.34470580215144420e+00_r);
+   res = walberla::checkIntegrator<walberla::SemiImplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 1.68291142727994780e-01_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 2.33193930919295134e-02_r);
+   res = walberla::checkIntegrator<walberla::VelocityVerlet>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 6.71909796751584687e-02_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 2.29906986019860066e-02_r);
+
+   //check energy conservation
+   osc.dt = 0.4_r;
+   osc.periods = 1000;
+   osc.update();
+   //res = walberla::checkIntegrator<walberla::ExplicitEuler>(osc);
+   //explicit euler is not symplectic
+   res = walberla::checkIntegrator<walberla::SemiImplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxEneDeviation, 8.03571427989904774e-03_r);
+   res = walberla::checkIntegrator<walberla::VelocityVerlet>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxEneDeviation, 4.99960610503419334e-04_r);
 
    return EXIT_SUCCESS;
 }
 
-} //namespace integrator_accuracy
+} //namespace walberla
+
 
-int main( int argc, char ** argv )
+/*
+ * Simulates a harmonic oscillator to test the accuracy of the integrators.
+ */
+int main(int argc, char **argv)
 {
-   return integrator_accuracy::main(argc, argv);
+   return walberla::main(argc, argv);
 }
 
diff --git a/tests/mesa_pd/kernel/LinearSpringDashpot.cpp b/tests/mesa_pd/kernel/LinearSpringDashpot.cpp
index 18beb0732157060883c716146297fa803e3b82cb..64f099e2679602570afa4f3b6c6922751038d0d2 100644
--- a/tests/mesa_pd/kernel/LinearSpringDashpot.cpp
+++ b/tests/mesa_pd/kernel/LinearSpringDashpot.cpp
@@ -26,7 +26,7 @@
 #include "mesa_pd/data/ShapeStorage.h"
 
 #include "mesa_pd/kernel/DoubleCast.h"
-#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
 #include "mesa_pd/kernel/VelocityVerlet.h"
 #include "mesa_pd/kernel/LinearSpringDashpot.h"
 #include "mesa_pd/mpi/ReduceContactHistory.h"
@@ -152,7 +152,7 @@ int main( int argc, char ** argv )
    kernel::VelocityVerletPostForceUpdate vvPostForce( dt );
 
    // explicit euler
-   kernel::ExplicitEuler explEuler( dt );
+   kernel::SemiImplicitEuler implEuler( dt );
 
    // collision response
    collision_detection::AnalyticContactDetection     acd;
@@ -188,7 +188,7 @@ int main( int argc, char ** argv )
       rch(*ps);
 
       if(useVelocityVerlet) vvPostForce(0,*accessor);
-      else explEuler(0, *accessor);
+      else implEuler(0, *accessor);
 
       ++steps;
    } while (double_cast(0, 1, *accessor, acd, *accessor ) || p.getLinearVelocity()[2] < 0);
diff --git a/tests/mesa_pd/kernel/SemiImplicitEuler.cpp b/tests/mesa_pd/kernel/SemiImplicitEuler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2d3a22f84fac0c4ecf720221dfb70c95466c3350
--- /dev/null
+++ b/tests/mesa_pd/kernel/SemiImplicitEuler.cpp
@@ -0,0 +1,133 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file   SemiImplicitEuler.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/ParticleAccessor.h>
+
+#include <mesa_pd/kernel/SemiImplicitEuler.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <iostream>
+
+namespace walberla {
+
+using namespace walberla::mesa_pd;
+
+class SingleParticleAccessor : public data::SingleParticleAccessor
+{
+public:
+   const walberla::real_t& getInvMass(const size_t /*p_idx*/) const {return invMass_;}
+   void setInvMass(const size_t /*p_idx*/, const walberla::real_t& v) { invMass_ = v;}
+   const walberla::mesa_pd::Mat3& getInvInertiaBF(const size_t /*p_idx*/) const {return invInertiaBF_;}
+   void setInvInertiaBF(const size_t /*p_idx*/, const walberla::mesa_pd::Mat3& v) { invInertiaBF_ = v;}
+
+   walberla::real_t        invMass_;
+   walberla::mesa_pd::Mat3 invInertiaBF_;
+};
+
+int main( int argc, char ** argv )
+{
+   Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+
+   //init data structures
+   SingleParticleAccessor accessor;
+
+   //initialize particle
+   const auto linVel = Vec3(1,2,3);
+   const auto angVel = Vec3(1,2,3);
+
+   const auto force  = Vec3(1,2,3);
+   const auto torque = Vec3(1,2,3);
+
+   accessor.setPosition(        0, Vec3(0,0,0));
+   accessor.setRotation(        0, Rot3(Quat()));
+   accessor.setLinearVelocity(  0, linVel);
+   accessor.setAngularVelocity( 0, angVel);
+   accessor.setForce(           0, force);
+   accessor.setTorque(          0, torque);
+   accessor.setInvMass(         0, 1.23456_r);
+   accessor.setInvInertiaBF(    0, Mat3(1.23456_r, 0_r, 0_r,
+                                        0_r, 1.23456_r, 0_r,
+                                        0_r, 0_r, 1.23456_r ));
+
+   //init kernels
+   const real_t dt = real_t(1);
+   kernel::SemiImplicitEuler integrator( dt );
+
+   integrator(0, accessor);
+
+   const auto& R = accessor.getRotation(0).getMatrix();
+   const auto wdot = R * accessor.getInvInertiaBF(0) * R.getTranspose() * torque;
+
+   //check force
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getForce(0), Vec3(0));
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getTorque(0), Vec3(0));
+
+   //check velocity
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getLinearVelocity(0), force * accessor.getInvMass(0) * dt +
+                                                             linVel);
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getAngularVelocity(0), wdot * dt + angVel);
+
+   //check position
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getPosition(0), linVel * dt +
+                                                       force * accessor.getInvMass(0) * dt * dt);
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getRotation(0).getQuaternion(),
+                              Quat( (wdot * dt + angVel).getNormalized(),
+                                    (wdot * dt + angVel).length() * dt ));
+
+   accessor.setPosition(        0, Vec3(0,0,0));
+   accessor.setRotation(        0, Rot3(Quat()));
+   accessor.setLinearVelocity(  0, linVel);
+   accessor.setAngularVelocity( 0, angVel);
+   accessor.setForce(           0, force);
+   accessor.setTorque(          0, torque);
+   accessor.setInvMass(         0, 1.23456_r);
+   accessor.setInvInertiaBF(    0, Mat3(1.23456_r, 0_r, 0_r,
+                                           0_r, 1.23456_r, 0_r,
+                                           0_r, 0_r, 1.23456_r ));
+   data::particle_flags::set( accessor.getFlagsRef(0), data::particle_flags::FIXED );
+
+   integrator(0, accessor);
+
+   //check force
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getForce(0), Vec3(0));
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getTorque(0), Vec3(0));
+
+   //check velocity
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getLinearVelocity(0), linVel);
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getAngularVelocity(0), angVel);
+
+   //check position
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getPosition(0), Vec3(0,0,0));
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getRotation(0).getQuaternion(), Quat());
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   return walberla::main(argc, argv);
+}
diff --git a/tests/mesa_pd/kernel/SpherePile.cpp b/tests/mesa_pd/kernel/SpherePile.cpp
index 67e1087a749cb50e0c29f77b40ac197a5598f0a4..3f71e929724fb98a3cdc6cb73095a45f6d79711a 100644
--- a/tests/mesa_pd/kernel/SpherePile.cpp
+++ b/tests/mesa_pd/kernel/SpherePile.cpp
@@ -26,7 +26,7 @@
 #include "mesa_pd/data/ShapeStorage.h"
 
 #include "mesa_pd/kernel/DoubleCast.h"
-#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
 #include "mesa_pd/kernel/SpringDashpot.h"
 #include "mesa_pd/kernel/SpringDashpotSpring.h"
 #include "mesa_pd/mpi/ReduceContactHistory.h"
@@ -98,7 +98,7 @@ int main(int argc, char **argv)
          false);
 
    // explicit euler
-   kernel::ExplicitEuler explEuler(dt);
+   kernel::SemiImplicitEuler implEuler(dt);
    collision_detection::AnalyticContactDetection acd;
    kernel::DoubleCast double_cast;
    kernel::SpringDashpot sd(1);
@@ -147,7 +147,7 @@ int main(int argc, char **argv)
       ps->forEachParticle(false,
                     kernel::SelectLocal(),
                     *ac,
-                    explEuler,
+                    implEuler,
                     *ac);
    }
    WALBERLA_LOG_DEVEL_VAR(sp->getPosition());
@@ -193,7 +193,7 @@ int main(int argc, char **argv)
       ps->forEachParticle(false,
                     kernel::SelectLocal(),
                     *ac,
-                    explEuler,
+                    implEuler,
                     *ac);
    }
    WALBERLA_LOG_DEVEL_VAR(sp->getPosition());
diff --git a/tests/mesa_pd/kernel/interfaces/SemiImplicitEulerInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/SemiImplicitEulerInterfaceCheck.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ad498f1d909ff1cf404d41ad92808603b26669b6
--- /dev/null
+++ b/tests/mesa_pd/kernel/interfaces/SemiImplicitEulerInterfaceCheck.cpp
@@ -0,0 +1,90 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of
+//  the License, or (at your option) any later version.
+//
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+//  for more details.
+//
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file SemiImplicitEulerInterfaceCheck.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/IAccessor.h>
+#include <mesa_pd/kernel/SemiImplicitEuler.h>
+
+#include <core/UniqueID.h>
+
+#include <map>
+
+namespace walberla {
+namespace mesa_pd {
+
+class Accessor : public data::IAccessor
+{
+public:
+   virtual ~Accessor() = default;
+   const walberla::mesa_pd::Vec3& getPosition(const size_t /*p_idx*/) const {return position_;}
+   void setPosition(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { position_ = v;}
+   
+   const walberla::mesa_pd::Vec3& getLinearVelocity(const size_t /*p_idx*/) const {return linearVelocity_;}
+   void setLinearVelocity(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { linearVelocity_ = v;}
+   
+   const walberla::real_t& getInvMass(const size_t /*p_idx*/) const {return invMass_;}
+   
+   const walberla::mesa_pd::Vec3& getForce(const size_t /*p_idx*/) const {return force_;}
+   void setForce(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { force_ = v;}
+   
+   const walberla::mesa_pd::data::particle_flags::FlagT& getFlags(const size_t /*p_idx*/) const {return flags_;}
+   
+   const walberla::mesa_pd::Rot3& getRotation(const size_t /*p_idx*/) const {return rotation_;}
+   void setRotation(const size_t /*p_idx*/, const walberla::mesa_pd::Rot3& v) { rotation_ = v;}
+   
+   const walberla::mesa_pd::Vec3& getAngularVelocity(const size_t /*p_idx*/) const {return angularVelocity_;}
+   void setAngularVelocity(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { angularVelocity_ = v;}
+   
+   const walberla::mesa_pd::Mat3& getInvInertiaBF(const size_t /*p_idx*/) const {return invInertiaBF_;}
+   
+   const walberla::mesa_pd::Vec3& getTorque(const size_t /*p_idx*/) const {return torque_;}
+   void setTorque(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { torque_ = v;}
+   
+
+   id_t getInvalidUid() const {return UniqueID<int>::invalidID();}
+   size_t getInvalidIdx() const {return std::numeric_limits<size_t>::max();}
+   /**
+   * @brief Returns the index of particle specified by uid.
+   * @param uid unique id of the particle to be looked up
+   * @return the index of the particle or std::numeric_limits<size_t>::max() if the particle is not found
+   */
+   size_t uidToIdx(const id_t& /*uid*/) const {return 0;}
+   size_t size() const { return 1; }
+private:
+   walberla::mesa_pd::Vec3 position_;
+   walberla::mesa_pd::Vec3 linearVelocity_;
+   walberla::real_t invMass_;
+   walberla::mesa_pd::Vec3 force_;
+   walberla::mesa_pd::data::particle_flags::FlagT flags_;
+   walberla::mesa_pd::Rot3 rotation_;
+   walberla::mesa_pd::Vec3 angularVelocity_;
+   walberla::mesa_pd::Mat3 invInertiaBF_;
+   walberla::mesa_pd::Vec3 torque_;
+};
+
+
+
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file