diff --git a/apps/benchmarks/GranularGas/Accessor.h b/apps/benchmarks/GranularGas/Accessor.h
index 13667332c7553c48142c776ef6fc00be7e4ac065..1d34ce30eb3530257c916014fb46edc915737a1f 100644
--- a/apps/benchmarks/GranularGas/Accessor.h
+++ b/apps/benchmarks/GranularGas/Accessor.h
@@ -33,13 +33,8 @@ public:
       , ss_(ss)
    {}
 
-   const walberla::real_t& getInvMass(const size_t p_idx) const {return ss_->shapes[ps_->getShapeIDRef(p_idx)]->getInvMass();}
-   walberla::real_t& getInvMassRef(const size_t p_idx) {return ss_->shapes[ps_->getShapeIDRef(p_idx)]->getInvMass();}
-   void setInvMass(const size_t p_idx, const walberla::real_t& v) { ss_->shapes[ps_->getShapeIDRef(p_idx)]->getInvMass() = v;}
-
+   const auto& getInvMass(const size_t p_idx) const {return ss_->shapes[ps_->getShapeIDRef(p_idx)]->getInvMass();}
    const auto& getInvInertiaBF(const size_t p_idx) const {return ss_->shapes[ps_->getShapeIDRef(p_idx)]->getInvInertiaBF();}
-   auto& getInvInertiaBFRef(const size_t p_idx) {return ss_->shapes[ps_->getShapeIDRef(p_idx)]->getInvInertiaBF();}
-   void setInvInertiaBF(const size_t p_idx, const Mat3& v) { ss_->shapes[ps_->getShapeIDRef(p_idx)]->getInvInertiaBF() = v;}
 
    data::BaseShape* getShape(const size_t p_idx) const {return ss_->shapes[ps_->getShapeIDRef(p_idx)].get();}
 private:
diff --git a/python/mesa_pd.py b/python/mesa_pd.py
index d356d40abd4a2b125008f67fbd1b4e248ae97a4d..401fc3bc7b00f0870367758a68db435e5afa727c 100755
--- a/python/mesa_pd.py
+++ b/python/mesa_pd.py
@@ -27,7 +27,7 @@ if __name__ == '__main__':
    os.makedirs(args.path + "/src/mesa_pd/mpi/notifications", exist_ok = True)
    os.makedirs(args.path + "/src/mesa_pd/vtk", exist_ok = True)
 
-   shapes = ["Sphere", "HalfSpace", "CylindricalBoundary", "Box"]
+   shapes = ["Sphere", "HalfSpace", "CylindricalBoundary", "Box", "Ellipsoid"]
 
    ps    = data.ParticleStorage()
    ch    = data.ContactHistory()
diff --git a/src/core/debug/CheckFunctions.impl.h b/src/core/debug/CheckFunctions.impl.h
index d8f7e384cf0539a2533fcb563c7ab049afa2a705..023d5f7d3fa48d2920d7ba26444453931502572c 100644
--- a/src/core/debug/CheckFunctions.impl.h
+++ b/src/core/debug/CheckFunctions.impl.h
@@ -351,7 +351,7 @@ void check_float_equal( const T & lhs, const U & rhs,
       << "File:       " << filename << ":" << line << '\n'
       << "Expression: " << lhsExpression << " == " << rhsExpression << '\n'
       //<< "ULP:        " << distance << '\n'
-      << "Values:     " << std::setw(length) << std::setfill(' ') << lhsExpression << " = ";
+      << "Values:     "  << std::setw(length) << std::setfill(' ') << lhsExpression << " = ";
 
    printValue( ss, lhs ) << '\n';
 
diff --git a/src/core/math/Random.h b/src/core/math/Random.h
index d63d2e7ede6338606219e743d7fc44444ca23010..3ae4dc747b5f01f810e7a44cb6e7c5e9b79e37de 100644
--- a/src/core/math/Random.h
+++ b/src/core/math/Random.h
@@ -158,7 +158,7 @@ private:
 *   \brief Returns a random floating point number of type REAL in the range [min,max) (max excluded!)
 */
 //**********************************************************************************************************************
-template< typename REAL >
+template< typename REAL = real_t >
 REAL realRandom( const REAL min = REAL(0), const REAL max = REAL(1), std::mt19937 & generator = internal::getGenerator() )
 {
    static_assert( std::numeric_limits<REAL>::is_specialized && !std::numeric_limits<REAL>::is_integer, "Floating point type required/expected!" );
diff --git a/src/core/math/Utility.h b/src/core/math/Utility.h
index de5f00f75c5caf9e0d975926db3584625f712124..b71ea90e07522396b798cef116fc6af8e76d5efb 100644
--- a/src/core/math/Utility.h
+++ b/src/core/math/Utility.h
@@ -84,6 +84,8 @@ inline real_t round( real_t a );
 //
 // The sign function only works for signed built-in data types. The attempt to use unsigned data
 // types or user-defined class types will result in a compile time error.
+//
+// http://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c
  */
 template< typename T >
 inline const T sign( T a )
diff --git a/src/mesa_pd/collision_detection/AnalyticContactDetection.h b/src/mesa_pd/collision_detection/AnalyticContactDetection.h
index cbc5b34d4168d4368b5fb232cb6b2dccef4251ba..bb3f8f1f21ba9356d0a4385c8de871efe0ec5dc6 100644
--- a/src/mesa_pd/collision_detection/AnalyticContactDetection.h
+++ b/src/mesa_pd/collision_detection/AnalyticContactDetection.h
@@ -46,6 +46,8 @@ namespace collision_detection {
 class AnalyticContactDetection
 {
 public:
+   virtual ~AnalyticContactDetection() {}
+
    size_t& getIdx1() {return idx1_;}
    size_t& getIdx2() {return idx2_;}
    Vec3&   getContactPoint() {return contactPoint_;}
@@ -116,7 +118,35 @@ public:
                     const data::Sphere& s,
                     Accessor& ac);
 
-private:
+   template <typename Accessor>
+   bool operator()( const size_t idx1,
+                    const size_t idx2,
+                    const data::HalfSpace& p0,
+                    const data::HalfSpace& p1,
+                    Accessor& ac);
+
+   template <typename Accessor>
+   bool operator()( const size_t idx1,
+                    const size_t idx2,
+                    const data::CylindricalBoundary& p0,
+                    const data::HalfSpace& p1,
+                    Accessor& ac);
+
+   template <typename Accessor>
+   bool operator()( const size_t idx1,
+                    const size_t idx2,
+                    const data::HalfSpace& p0,
+                    const data::CylindricalBoundary& p1,
+                    Accessor& ac);
+
+   template <typename Accessor>
+   bool operator()( const size_t idx1,
+                    const size_t idx2,
+                    const data::CylindricalBoundary& p0,
+                    const data::CylindricalBoundary& p1,
+                    Accessor& ac);
+
+protected:
    size_t idx1_;
    size_t idx2_;
    Vec3   contactPoint_;
@@ -254,6 +284,46 @@ inline bool AnalyticContactDetection::operator()( const size_t idx1,
    return operator()(idx2, idx1, s, p, ac);
 }
 
+template <typename Accessor>
+inline bool AnalyticContactDetection::operator()( const size_t /*idx1*/,
+                                                  const size_t /*idx2*/,
+                                                  const data::HalfSpace& /*geo1*/,
+                                                  const data::HalfSpace& /*geo2*/,
+                                                  Accessor& /*ac*/)
+{
+   WALBERLA_ABORT("Collision between two half spaces is not defined!")
+}
+
+template <typename Accessor>
+inline bool AnalyticContactDetection::operator()( const size_t /*idx1*/,
+                                                  const size_t /*idx2*/,
+                                                  const data::HalfSpace& /*geo1*/,
+                                                  const data::CylindricalBoundary& /*geo2*/,
+                                                  Accessor& /*ac*/)
+{
+   WALBERLA_ABORT("Collision between half spaces and cylindrical boundary is not defined!")
+}
+
+template <typename Accessor>
+inline bool AnalyticContactDetection::operator()( const size_t /*idx1*/,
+                                                  const size_t /*idx2*/,
+                                                  const data::CylindricalBoundary& /*geo1*/,
+                                                  const data::HalfSpace& /*geo2*/,
+                                                  Accessor& /*ac*/)
+{
+   WALBERLA_ABORT("Collision between half spaces and cylindrical boundary is not defined!")
+}
+
+template <typename Accessor>
+inline bool AnalyticContactDetection::operator()( const size_t /*idx1*/,
+                                                  const size_t /*idx2*/,
+                                                  const data::CylindricalBoundary& /*geo1*/,
+                                                  const data::CylindricalBoundary& /*geo2*/,
+                                                  Accessor& /*ac*/)
+{
+   WALBERLA_ABORT("Collision between two cylindrical boundaries is not defined!")
+}
+
 inline
 std::ostream& operator<<( std::ostream& os, const AnalyticContactDetection& ac )
 {
diff --git a/src/mesa_pd/collision_detection/EPA.cpp b/src/mesa_pd/collision_detection/EPA.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2076f680629d6095f21c248cc01dc05fde62851c
--- /dev/null
+++ b/src/mesa_pd/collision_detection/EPA.cpp
@@ -0,0 +1,923 @@
+//======================================================================================================================
+//
+//  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 EPA.cpp
+//! \author Tobias Scharpff
+//! \author Tobias Leemann
+//
+//  DISCLAIMER: The following source file contains modified code from the SOLID-3.5 library for
+//  interference detection as it is published in the book "Collision Detection in Interactive
+//  3D Environments" by Gino van den Bergen <info@dtecta.com>. Even though the original source
+//  was published under the GPL version 2 not allowing later versions, the original author of the
+//  source code permitted the relicensing of the SOLID-3.5 library under the GPL version 3 license.
+//
+//=================================================================================================
+
+#include "EPA.h"
+#include <core/math/Constants.h>
+
+#include <vector>
+
+namespace walberla {
+namespace mesa_pd {
+namespace collision_detection {
+
+//=================================================================================================
+//
+//  EPA::EPA_TRIANGLE CONSTRUCTOR
+//
+//=================================================================================================
+
+//*************************************************************************************************
+/*! \brief Construct a new EPA_Triangle.
+ *  \param a First point index
+ *  \param b Second point index
+ *  \param c Third point index
+ *  \param points Vector with all points
+ */
+EPA::EPA_Triangle::EPA_Triangle( size_t a,
+                                 size_t b,
+                                 size_t c,
+                                 const std::vector<Vec3>& points )
+{
+   const Vec3& A = points[a];
+   const Vec3& B = points[b];
+   const Vec3& C = points[c];
+
+   indices_[0] = a;
+   indices_[1] = b;
+   indices_[2] = c;
+
+   //calculate the closest point to the origin
+   //Real-Time Collsion Buch Seite 137
+   Vec3 ab = B-A;
+   Vec3 ac = C-A;
+   //Vec3 bc = C-B;
+
+   normal_ = ab % ac;
+   Vec3 nT = normal_;
+
+   //
+   real_t vc = nT * (A % B);
+   real_t va = nT * (B % C);
+   real_t vb = nT * (C % A);
+   real_t denom = real_t(1.0) / (va + vb + vc);
+
+   bar_[0] = va * denom;
+   bar_[1] = vb * denom;
+   bar_[2] = real_t(1.0) - bar_[0] - bar_[1];
+
+   closest_ = bar_[0] * A + bar_[1] * B + bar_[2] * C;
+
+   //sqrDist=key is square distance of v to origin
+   sqrDist_ = closest_.sqrLength();
+
+   //adjoined triangles not set yet
+   adjTriangle_[0] = adjTriangle_[1] = adjTriangle_[2] = nullptr;
+   adjEdges_[0]    = adjEdges_[1]    = adjEdges_[2] = 4;
+
+   obsolete_ = false;
+}
+
+//=================================================================================================
+//
+//  EPA::EPA_TRIANGLE UTILITY FUNCTIONS
+//
+//=================================================================================================
+
+//*************************************************************************************************
+/*! \brief Sets the link of this triangles edge0 neighbor to tria and vice versa.
+ */
+inline bool EPA::EPA_Triangle::link( size_t edge0, EPA_Triangle* tria, size_t edge1 )
+{
+   WALBERLA_ASSERT_LESS(edge0, 3, "link: invalid edge index");
+   WALBERLA_ASSERT_LESS(edge1, 3, "link: invalid edge index");
+
+   adjTriangle_[edge0] = tria;
+   adjEdges_[edge0] = edge1;
+   tria->adjTriangle_[edge1] = this;
+   tria->adjEdges_[edge1] = edge0;
+
+   bool b = indices_[edge0]       == tria->indices_[(edge1+1)%3] &&
+            indices_[(edge0+1)%3] == tria->indices_[edge1];
+   return b;
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Fills edgeBuffer with the CCW contour of triangles not seen from point w which is in normal direction of the triangle.
+ */
+inline void EPA::EPA_Triangle::silhouette( const Vec3& w, EPA_EdgeBuffer& edgeBuffer )
+{
+   //std::cerr << "Starting Silhoutette search on Triangle {" << indices_[0] << "," << indices_[1] << "," << indices_[2] << "}" << std::endl;
+   edgeBuffer.clear();
+   obsolete_ = true;
+
+   adjTriangle_[0]->silhouette(adjEdges_[0], w, edgeBuffer);
+   adjTriangle_[1]->silhouette(adjEdges_[1], w, edgeBuffer);
+   adjTriangle_[2]->silhouette(adjEdges_[2], w, edgeBuffer);
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Recursive silhuette finding method.
+ */
+void EPA::EPA_Triangle::silhouette( size_t index, const Vec3& w,
+                                    EPA_EdgeBuffer& edgeBuffer )
+{
+   if (!obsolete_) {
+      real_t test = (closest_ * w);
+      if (test < sqrDist_) {
+         edgeBuffer.push_back(EPA_Edge(this, index));
+      }
+      else {
+         obsolete_ = true; // Facet is visible
+         size_t next = (index+1) % 3;
+         adjTriangle_[next]->silhouette(adjEdges_[next], w, edgeBuffer);
+         next = (next+1) % 3;
+         adjTriangle_[next]->silhouette(adjEdges_[next], w, edgeBuffer);
+      }
+   }
+}
+//*************************************************************************************************
+
+//=================================================================================================
+//
+//  EPA QUERY FUNCTIONS
+//
+//=================================================================================================
+
+//*************************************************************************************************
+//EPA Precision default values for different data types
+template< class T > struct EpsilonRelEPA;
+template<> struct EpsilonRelEPA<       float > { static const       float value; };
+template<> struct EpsilonRelEPA<      double > { static const      double value; };
+template<> struct EpsilonRelEPA< long double > { static const long double value; };
+
+const       float EpsilonRelEPA<       float >::value = static_cast<       float >(1e-4);
+const      double EpsilonRelEPA<      double >::value = static_cast<      double >(1e-6);
+const long double EpsilonRelEPA< long double >::value = static_cast< long double >(1e-6);
+
+//*************************************************************************************************
+/*! \brief Does an EPA computation with contactthreshold added. Use Default relative Error.
+ */
+bool EPA::doEPAcontactThreshold( Support &geom1,
+                                 Support &geom2,
+                                 const GJK& gjk,
+                                 Vec3& retNormal,
+                                 Vec3& contactPoint,
+                                 real_t& penetrationDepth){
+
+   //Default relative epsilon
+   return doEPA(geom1, geom2, gjk, retNormal, contactPoint, penetrationDepth, contactThreshold, EpsilonRelEPA<real_t>::value);
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Does an EPA computation with contactThreshold added. Relative Error can be specified.
+ */
+bool EPA::doEPAcontactThreshold( Support &geom1,
+                                 Support &geom2,
+                                 const GJK& gjk,
+                                 Vec3& retNormal,
+                                 Vec3& contactPoint,
+                                 real_t& penetrationDepth,
+                                 real_t eps_rel){
+
+   return doEPA(geom1, geom2, gjk, retNormal, contactPoint, penetrationDepth, contactThreshold, eps_rel);
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Does an EPA computation with margin added. Use Default relative Error.
+ */
+bool EPA::doEPAmargin( Support &geom1,
+                       Support &geom2,
+                       const GJK& gjk,
+                       Vec3& retNormal,
+                       Vec3& contactPoint,
+                       real_t& penetrationDepth,
+                       real_t margin){
+   //Default relative epsilon
+   return doEPA(geom1, geom2, gjk, retNormal, contactPoint, penetrationDepth, margin, EpsilonRelEPA<real_t>::value);
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Does an epa computation with contact margin added and specified realtive error.
+ */
+bool EPA::doEPA( Support &geom1,
+                 Support &geom2,
+                 const GJK& gjk,
+                 Vec3& retNormal,
+                 Vec3& contactPoint,
+                 real_t& penetrationDepth,
+                 real_t margin,
+                 real_t eps_rel )
+{
+   //have in mind that we use a support mapping which blows up the objects a wee bit so
+   //zero penetraion aka toching contact means that the original bodies have a distance of 2*margin between them
+
+   //Set references to the results of GJK
+   size_t     numPoints( static_cast<size_t>( gjk.getSimplexSize() ) );
+   std::vector<Vec3> epaVolume( gjk.getSimplex() );
+   std::vector<Vec3> supportA ( gjk.getSupportA() );
+   std::vector<Vec3> supportB ( gjk.getSupportB() );
+
+   Vec3 support;
+
+   epaVolume.reserve( maxSupportPoints_ );
+   supportA.reserve ( maxSupportPoints_ );
+   supportB.reserve ( maxSupportPoints_ );
+
+   EPA_EntryBuffer entryBuffer;
+   entryBuffer.reserve(maxTriangles_);
+
+   EPA_EntryHeap entryHeap;
+   entryHeap.reserve(maxTriangles_);
+
+   EPA_EdgeBuffer edgeBuffer;
+   edgeBuffer.reserve(20);
+
+   real_t lowerBoundSqr = math::Limits<real_t>::inf();
+   real_t upperBoundSqr = math::Limits<real_t>::inf();
+
+   //create an Initial simplex
+   if(numPoints == 1) {
+      //If the GJK-Simplex contains only one point, it must be the origin and it must be on the boundary of the CSO.
+      //This means the enlarged bodies are in touching contact and the original bodies do not intersect.
+      return false;
+   }
+   else {
+      createInitialSimplex(numPoints, geom1, geom2, supportA, supportB, epaVolume, entryBuffer, margin);
+   }
+
+   for(EPA_EntryBuffer::iterator it=entryBuffer.begin(); it != entryBuffer.end(); ++it) {
+      if(it->isClosestInternal()) {
+         entryHeap.push_back(&(*it));
+      }
+   }
+
+   if(entryHeap.size() == 0) {
+      //unrecoverable error.
+      return false;
+   }
+
+   std::make_heap(entryHeap.begin(), entryHeap.end(), EPA::EPA_TriangleComp());
+   EPA_Triangle* current = nullptr;
+
+   numIterations_ = 0;
+   //EPA Main-Loop
+   do {
+      ++numIterations_;
+      std::pop_heap(entryHeap.begin(), entryHeap.end(), EPA::EPA_TriangleComp());
+      current = entryHeap.back();
+      entryHeap.pop_back();
+      if(!current->isObsolete()) {
+         lowerBoundSqr = current->getSqrDist();
+
+         if(epaVolume.size() == maxSupportPoints_) {
+            WALBERLA_ASSERT(false, "Support point limit reached.");
+            break;
+         }
+
+         // Compute new support direction
+         // if origin is contained in plane, use out-facing normal.
+         Vec3 normal;
+         if(current->getSqrDist() < real_comparison::Epsilon<real_t>::value*real_comparison::Epsilon<real_t>::value){
+            if(current->getNormal().sqrLength() < real_comparison::Epsilon<real_t>::value*real_comparison::Epsilon<real_t>::value){
+               break;
+            }
+
+            normal = current->getNormal().getNormalizedOrZero();
+         }else{
+            normal = current->getClosest().getNormalizedOrZero();
+         }
+         //std::cerr << "Current Closest: " << current->getClosest();
+         //std::cerr << "New support direction: " <<  normal << std::endl;
+
+         pushSupportMargin(geom1, geom2, normal, margin, epaVolume, supportA, supportB);
+         support = epaVolume.back();
+
+         numPoints++;
+
+         real_t farDist = support * normal; //not yet squared
+
+         WALBERLA_ASSERT_GREATER(farDist, real_t(0.0), "EPA support mapping gave invalid point in expansion direction");
+         //std::cerr << "New upper bound: " <<  farDist*farDist << std::endl;
+         upperBoundSqr = std::min(upperBoundSqr, farDist*farDist);
+
+
+
+         //Try to approximate the new surface with a sphere
+         if (bUseSphereOptimization_)
+         {
+            Vec3 ctr;
+            real_t radius2 = calculateCircle(support,
+                                             epaVolume[(*current)[0]],
+                  epaVolume[(*current)[1]],
+                  epaVolume[(*current)[2]],
+                  ctr);
+            if(radius2 > real_t(0.0)){ //if a Sphere exists
+               // std::cerr << "Circle created with center at " << ctr << ". r2=" << radius2 << std::endl;
+               real_t center_len = ctr.length();
+               real_t circle_dist = (std::sqrt(radius2) - center_len); //Distance from center to the spheres surface
+
+               // Check if the circle matches the bounds given by EPA and limit max error to ca. 5%
+               if (circle_dist*circle_dist <= upperBoundSqr &&
+                   circle_dist*circle_dist >= lowerBoundSqr &&
+                   (circle_dist*circle_dist) < real_t(1.10) * lowerBoundSqr &&
+                   !floatIsEqual(center_len, real_t(0.0)) &&
+                   circle_dist > real_t(0.0)) // In case of numerical errors, this can be the case
+               {
+                  const auto ilen = real_t(1.0) / center_len;
+                  ctr *= -ilen;
+                  pushSupportMargin(geom1, geom2, ctr, margin, epaVolume, supportA, supportB);
+                  support = epaVolume.back();
+                  // Check if support is in expected direction
+                  real_t supp_dist = support.length();
+                  if(floatIsEqual((support % ctr).sqrLength()/support.sqrLength(), real_t(0.0)) &&
+                     supp_dist*supp_dist <= upperBoundSqr &&
+                     supp_dist*supp_dist >= lowerBoundSqr)
+                  {
+                     //Accept sphere
+                     contactPoint = real_t(0.5) * (supportA.back() + supportB.back());
+                     penetrationDepth = -supp_dist + real_t(2.0) * margin;
+                     retNormal = -ctr;
+                     
+                     if(penetrationDepth < contactThreshold){
+                        return true;
+                     }else{
+                        return false;
+                     }
+                  } else {
+                     //Reject sphere
+                     removeSupportMargin(epaVolume, supportA, supportB);
+                     support = epaVolume.back();
+                  }
+               }
+            }
+         }
+
+         //terminating criteria's
+         //- we found that the two bounds are close enough
+         //- the added support point was already in the epaVolume
+         if(upperBoundSqr <= (real_t(1.0)+eps_rel)*(real_t(1.0)+eps_rel)*lowerBoundSqr
+               || support == epaVolume[(*current)[0]]
+               || support == epaVolume[(*current)[1]]
+               || support == epaVolume[(*current)[2]])
+         {
+            //std::cerr << "Tolerance reached." << std::endl;
+            break;
+         }
+
+         // Compute the silhouette cast by the new vertex
+         // Note that the new vertex is on the positive side
+         // of the current triangle, so the current triangle
+         // will not be in the convex hull. Start local search
+         // from this facet.
+
+         current->silhouette(support, edgeBuffer);
+         if(edgeBuffer.size() < 3 ) {
+            return false;
+         }
+
+         if(entryBuffer.size() == maxSupportPoints_) {
+            //"out of memory" so stop here
+            //std::cerr << "Memory Limit reached." << std::endl;
+            break;
+         }
+
+         EPA_EdgeBuffer::const_iterator it = edgeBuffer.begin();
+         entryBuffer.push_back(EPA_Triangle(it->getEnd(), it->getStart(), epaVolume.size()-1, epaVolume));
+
+         EPA_Triangle* firstTriangle = &(entryBuffer.back());
+         //if it is expanding candidate add to heap
+         //std::cerr << "Considering Triangle (" << firstTriangle->getSqrDist() << ") {"  << (*firstTriangle)[0] <<  "," << (*firstTriangle)[1] <<  ","<< (*firstTriangle)[2] << "} ("<< epaVolume[(*firstTriangle)[0]] * firstTriangle->getNormal()<< ")" << std::endl;
+         if(epaVolume[(*firstTriangle)[0]] * firstTriangle->getNormal() < real_t(0.0)){
+            //the whole triangle is on the wrong side of the origin.
+            //This is a numerical error and will produce wrong results, if the search is continued. Stop here.
+            break;
+         }
+         if(firstTriangle->isClosestInternal()
+               && firstTriangle->getSqrDist() > lowerBoundSqr
+               && firstTriangle->getSqrDist() < upperBoundSqr)
+         {
+            entryHeap.push_back(firstTriangle);
+            std::push_heap(entryHeap.begin(), entryHeap.end(), EPA::EPA_TriangleComp());
+         }
+
+         firstTriangle->link(0, it->getTriangle(), it->getIndex());
+
+         EPA_Triangle* lastTriangle = firstTriangle;
+
+         ++it;
+         for(; it != edgeBuffer.end(); ++it){
+            if(entryBuffer.size() == maxSupportPoints_) {
+               //"out of memory" so stop here
+               break;
+            }
+
+            entryBuffer.push_back(EPA_Triangle(it->getEnd(), it->getStart(), epaVolume.size()-1, epaVolume));
+            EPA_Triangle* newTriangle = &(entryBuffer.back());
+
+            //std::cerr << "Considering Triangle (" << newTriangle->getSqrDist() << ") {"  << (*newTriangle)[0] <<  "," << (*newTriangle)[1] <<  ","<< (*newTriangle)[2] << "} ("<< epaVolume[(*newTriangle)[0]] * newTriangle->getNormal() << ")" << std::endl;
+
+            if(epaVolume[(*newTriangle)[0]] * newTriangle->getNormal() < real_t(0.0)){
+               //the whole triangle is on the wrong side of the origin.
+               //This is an error.
+               break;
+            }
+            //if it is expanding candidate add to heap
+            if(newTriangle->isClosestInternal()
+                  &&  newTriangle->getSqrDist() > lowerBoundSqr
+                  &&  newTriangle->getSqrDist() < upperBoundSqr)
+            {
+               entryHeap.push_back(newTriangle);
+               std::push_heap(entryHeap.begin(), entryHeap.end(), EPA::EPA_TriangleComp());
+            }
+
+            if(!newTriangle->link(0, it->getTriangle(), it->getIndex())) {
+               break;
+            }
+
+            if(!newTriangle->link(2, lastTriangle, 1)) {
+               break;
+            }
+
+            lastTriangle = newTriangle;
+         }
+
+         if(it != edgeBuffer.end()) {
+            //For some reason the silhouette couldn't be processed completely
+            //so we stop here and take the last result
+            break;
+         }
+
+         firstTriangle->link(2, lastTriangle, 1);
+      }
+   } while (entryHeap.size() > 0 && entryHeap[0]->getSqrDist() <= upperBoundSqr);
+
+   //Normal must be inverted
+   retNormal   = -current->getClosest().getNormalizedOrZero();
+
+   //Calculate Witness points
+   const Vec3 wittnessA = current->getClosestPoint(supportA);
+   const Vec3 wittnessB = current->getClosestPoint(supportB);
+   contactPoint = real_t(0.5) * (wittnessA + wittnessB);
+
+   //Penetration Depth
+   penetrationDepth = -(current->getClosest().length() - real_t(2.0) * margin);
+
+   /*std::cerr << "normal=" << retNormal <<std::endl;
+   std::cerr << "close =" << current->getClosest() << std::endl;
+   std::cerr << "diff  =" << wittnesA - wittnesB  <<std::endl;
+   std::cerr << "wittnesA    =" << wittnesA <<std::endl;
+   std::cerr << "wittnesB    =" << wittnesB <<std::endl;
+   std::cerr << "contactPoint=" << contactPoint << std::endl;
+   std::cerr << "penDepth=" << penetrationDepth  <<std::endl;
+   std::cerr << "lowerBound=" << sqrt(lowerBoundSqr) <<std::endl;
+   std::cerr << "curreBound=" << current->getClosest().length() << std::endl;
+   std::cerr << "upperBound=" << sqrt(upperBoundSqr) <<std::endl;
+   std::cerr << "Heap Size=" << entryHeap.size() << std::endl;
+   std::cerr << "entryHeap[0]->getSqrDist()=" << entryHeap[0]->getSqrDist() << std::endl;*/
+   //std::cout << "EPA penetration depth: " << penetrationDepth <<  std::endl;
+
+   if(penetrationDepth < contactThreshold) {
+      return true;
+   }
+
+   //no intersection found!
+   return false;
+}
+//*************************************************************************************************
+
+//=================================================================================================
+//
+//  EPA UTILITY FUNCTIONS
+//
+//=================================================================================================
+
+//*************************************************************************************************
+/*! \brief Create a starting tetrahedron for EPA, from the GJK Simplex.
+ */
+inline void EPA::createInitialSimplex( size_t numPoints,
+                                       Support &geom1,
+                                       Support &geom2,
+                                       std::vector<Vec3>& supportA,
+                                       std::vector<Vec3>& supportB,
+                                       std::vector<Vec3>& epaVolume,
+                                       EPA_EntryBuffer& entryBuffer,
+                                       real_t margin )
+{
+   switch(numPoints) {
+   case 2:
+   {
+      //simplex is a line segement
+      //add 3 points around the this segment
+      //the COS is konvex so the resulting hexaheadron should be konvex too
+
+      Vec3 d = epaVolume[1] - epaVolume[0];
+      //find coordinate axis e_i which is furthest from paralell to d
+      //and therefore d has the smallest abs(d[i])
+      real_t abs0 = std::abs(d[0]);
+      real_t abs1 = std::abs(d[1]);
+      real_t abs2 = std::abs(d[2]);
+
+      Vec3 axis;
+      if( abs0 < abs1 && abs0 < abs2) {
+         axis = Vec3(real_t(1.0), real_t(0.0), real_t(0.0));
+      }
+      else if( abs1 < abs0 && abs1 < abs2) {
+         axis = Vec3(real_t(0.0), real_t(1.0), real_t(0.0));
+      }
+      else {
+         axis = Vec3(real_t(0.0), real_t(0.0), real_t(1.0));
+      }
+
+      Vec3 direction1 = (d % axis).getNormalizedOrZero();
+      Quat q(d, (real_t(2.0)/real_t(3.0)) * real_t(walberla::math::M_PI));
+      Mat3 rot = q.toRotationMatrix();
+      Vec3 direction2 = (rot*direction1).getNormalizedOrZero();
+      Vec3 direction3 = (rot*direction2).getNormalizedOrZero();
+
+      //add point in positive normal direction1
+      pushSupportMargin(geom1, geom2, direction1, margin, epaVolume, supportA, supportB);
+      //std::cerr << "S1: " << support1 << std::endl;
+
+      //add point in negative normal direction2
+      pushSupportMargin(geom1, geom2, direction2, margin, epaVolume, supportA, supportB);
+      //std::cerr << "S2: " << support2 << std::endl;
+
+      //add point in negative normal direction
+      pushSupportMargin(geom1, geom2, direction3, margin, epaVolume, supportA, supportB);
+      //std::cerr << "S3: " << support3 << std::endl;
+
+      //Build the hexahedron as it is convex
+      //epaVolume[1] = up
+      //epaVolume[0] = down
+      //epaVolume[2] = ccw1
+      //epaVolume[3] = ccw2
+      //epaVolume[4] = ccw3
+
+
+      //check for containment inside
+      if(originInTetrahedron(epaVolume[0], epaVolume[2], epaVolume[3], epaVolume[4]) || originInTetrahedron(epaVolume[1], epaVolume[2], epaVolume[3], epaVolume[4]) ){
+         //insert triangle 1
+         entryBuffer.push_back(EPA_Triangle(1, 2, 3, epaVolume)); //[0] up->ccw1->ccw2
+         //insert triangle 2
+         entryBuffer.push_back(EPA_Triangle(1, 3, 4, epaVolume)); //[1] up->ccw2->ccw3
+         //insert triangle 3
+         entryBuffer.push_back(EPA_Triangle(1, 4, 2, epaVolume)); //[2] up->ccw3->ccw1
+
+         //link these 3 triangles
+         entryBuffer[0].link(2, &(entryBuffer[1]), 0); //edge up->ccw1
+         entryBuffer[1].link(2, &(entryBuffer[2]), 0); //edge up->ccw2
+         entryBuffer[2].link(2, &(entryBuffer[0]), 0); //edge up->ccw3
+
+
+         //insert triangle 4
+         entryBuffer.push_back(EPA_Triangle(0, 2, 4, epaVolume)); //[3] down->ccw1->ccw3
+         //insert triangle 5
+         entryBuffer.push_back(EPA_Triangle(0, 4, 3, epaVolume)); //[4] down->ccw3->ccw2
+         //insert triangle 6
+         entryBuffer.push_back(EPA_Triangle(0, 3, 2, epaVolume)); //[5] down->ccw2->ccw1
+
+         //link these 3 triangles
+         entryBuffer[3].link(2, &(entryBuffer[4]), 0); //edge down->ccw3
+         entryBuffer[4].link(2, &(entryBuffer[5]), 0); //edge down->ccw1
+         entryBuffer[5].link(2, &(entryBuffer[3]), 0); //edge down->ccw1
+
+         //link the two pyramids
+         entryBuffer[0].link(1, &(entryBuffer[5]), 1); //edge ccw1->ccw2
+         entryBuffer[1].link(1, &(entryBuffer[4]), 1); //edge ccw2->ccw3
+         entryBuffer[2].link(1, &(entryBuffer[3]), 1); //edge ccw3->ccw1
+      }else{
+         //Apply iterative search
+         removeSupportMargin(epaVolume, supportA, supportB); //remove 5th point.
+         //Search starts from the remaining 4 points
+         searchTetrahedron(geom1, geom2, epaVolume, supportA, supportB, entryBuffer, margin);
+      }
+
+      break;
+   }
+   case 3:
+   {
+      //simplex is a triangle, add tow points in positive and negative normal direction
+
+      const Vec3& A = epaVolume[2];  //The Point last added to the simplex
+      const Vec3& B = epaVolume[1];  //One Point that was already in the simplex
+      const Vec3& C = epaVolume[0];  //One Point that was already in the simplex
+      //ABC is a conterclockwise triangle
+
+      const Vec3  AB  = B-A;       //The vector A->B
+      const Vec3  AC  = C-A;       //The vector A->C
+      const Vec3  ABC = (AB%AC).getNormalizedOrZero();     //The the normal pointing towards the viewer if he sees a CCW triangle ABC
+
+      //add point in positive normal direction
+      pushSupportMargin(geom1, geom2, ABC, margin, epaVolume, supportA, supportB);
+
+      //add point in negative normal direction
+      pushSupportMargin(geom1, geom2, -ABC, margin, epaVolume, supportA, supportB);
+      //Vec3 support2 = epaVolume.back();
+
+      //check if the hexahedron is convex aka check if a partial tetrahedron contains the last point
+      if(pointInTetrahedron(epaVolume[3], epaVolume[4], epaVolume[0], epaVolume[2], epaVolume[1])) {
+         //epaVolumne[1] is whithin the tetraheadron 3-4-0-2 so this is the epaVolume to take
+         createInitialTetrahedron(3,4,0,2, epaVolume, entryBuffer);
+      }
+      else if(pointInTetrahedron(epaVolume[3], epaVolume[4], epaVolume[1], epaVolume[0], epaVolume[2])) {
+         createInitialTetrahedron(3,4,1,0, epaVolume, entryBuffer);
+      }
+      else if(pointInTetrahedron(epaVolume[3], epaVolume[4], epaVolume[2], epaVolume[1], epaVolume[0])) {
+         createInitialTetrahedron(3,4,2,1, epaVolume, entryBuffer);
+      }
+      else {
+         //Build the hexahedron as it is convex
+         //insert triangle 1
+         entryBuffer.push_back(EPA_Triangle(3, 2, 1, epaVolume)); //[0] support1->A->B
+         //insert triangle 2
+         entryBuffer.push_back(EPA_Triangle(3, 1, 0, epaVolume)); //[1] support1->B->C
+         //insert triangle 3
+         entryBuffer.push_back(EPA_Triangle(3, 0, 2, epaVolume)); //[2] support1->C->A
+
+         //link these 3 triangles
+         entryBuffer[0].link(2, &(entryBuffer[1]), 0); //edge support1->A
+         entryBuffer[1].link(2, &(entryBuffer[2]), 0); //edge support1->B
+         entryBuffer[2].link(2, &(entryBuffer[0]), 0); //edge support1->C
+
+
+         //insert triangle 4
+         entryBuffer.push_back(EPA_Triangle(4, 2, 0, epaVolume)); //[3] support2->A->C
+         //insert triangle 5
+         entryBuffer.push_back(EPA_Triangle(4, 0, 1, epaVolume)); //[4] support2->C->B
+         //insert triangle 6
+         entryBuffer.push_back(EPA_Triangle(4, 1, 2, epaVolume)); //[5] support2->B->A
+
+         //link these 3 triangles
+         entryBuffer[3].link(2, &(entryBuffer[4]), 0); //edge support2->C
+         entryBuffer[4].link(2, &(entryBuffer[5]), 0); //edge support2->B
+         entryBuffer[5].link(2, &(entryBuffer[3]), 0); //edge support2->A
+
+         //link the two pyramids
+         entryBuffer[0].link(1, &(entryBuffer[5]), 1); //edge A->B
+         entryBuffer[1].link(1, &(entryBuffer[4]), 1); //edge B->C
+         entryBuffer[2].link(1, &(entryBuffer[3]), 1); //edge C->A
+
+      }
+
+      break;
+   }
+   case 4:
+   {
+      createInitialTetrahedron(3,2,1,0, epaVolume, entryBuffer);
+      break;
+   }
+   default:
+   {
+      WALBERLA_ASSERT( false, "invalid number of simplex points in EPA" );
+      break;
+   }
+   }
+}
+
+//*************************************************************************************************
+
+//*************************************************************************************************
+/*! \brief TODO
+ *
+ * see Book "collision detection in interactive 3D environments" page161
+ * ATTENTION seems to have no consistent behavior on the surface and vertices
+ */
+inline bool EPA::originInTetrahedron( const Vec3& p0, const Vec3& p1, const Vec3& p2,
+                                      const Vec3& p3 )
+{
+   Vec3 normal0T = (p1 -p0) % (p2-p0);
+   if( (normal0T*p0 > real_t(0.0)) == (normal0T*p3 > real_t(0.0)) ) {
+      return false;
+   }
+   Vec3 normal1T = (p2 -p1) % (p3-p1);
+   if( (normal1T*p1 > real_t(0.0)) == (normal1T*p0 > real_t(0.0)) ) {
+      return false;
+   }
+   Vec3 normal2T = (p3 -p2) % (p0-p2);
+   if( (normal2T*p2 > real_t(0.0)) == (normal2T*p1 > real_t(0.0)) ) {
+      return false;
+   }
+   Vec3 normal3T = (p0 -p3) % (p1-p3);
+   if( (normal3T*p3 > real_t(0.0)) == (normal3T*p2 > real_t(0.0)) ) {
+      return false;
+   }
+
+   return true;
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Retrurns true, if the origin lies in the tetrahedron ABCD.
+ */
+inline bool EPA::originInTetrahedronVolumeMethod( const Vec3& A, const Vec3& B, const Vec3& C,
+                                                  const Vec3& D )
+{
+   const Vec3& aoT = A;
+   if((aoT * (B % C)) <= real_t(0.0)) {
+      //if volume of ABC and Origin <0.0 than the origin is on the wrong side of ABC
+      //http://mathworld.wolfram.com/Tetrahedron.html volume formula
+      return false;
+   }
+   if((aoT * (C % D)) <= real_t(0.0)) {
+      return false;
+   }
+   if((aoT * (D % B)) <= real_t(0.0)) {
+      return false;
+   }
+   if((B * (D % C)) <= real_t(0.0)) {
+      return false;
+   }
+   return true;
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Retrurns true, if a point lies in the tetrahedron ABCD.
+ *  \param point The point to be checked for containment.
+ */
+inline bool EPA::pointInTetrahedron( const Vec3& A, const Vec3& B, const Vec3& C, const Vec3& D,
+                                     const Vec3& point )
+{
+   return originInTetrahedronVolumeMethod( A-point, B-point, C-point, D-point );
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*!\brief TODO
+ * top, frontLeft ... are indices
+ */
+inline void EPA::createInitialTetrahedron( size_t top,
+                                           size_t frontLeft,
+                                           size_t frontRight,
+                                           size_t back,
+                                           std::vector<Vec3>& epaVolume,
+                                           EPA_EntryBuffer& entryBuffer )
+{
+   //insert triangle 1
+   entryBuffer.push_back(EPA_Triangle(top, frontLeft, frontRight, epaVolume)); //[0] vorne
+   //insert triangle 2
+   entryBuffer.push_back(EPA_Triangle(top, frontRight, back, epaVolume)); //[1] rechts hinten
+   //insert triangle 3
+   entryBuffer.push_back(EPA_Triangle(top, back, frontLeft, epaVolume)); //[2] links hinten
+   //insert triangle 4
+   entryBuffer.push_back(EPA_Triangle(back, frontRight, frontLeft, epaVolume)); //[3] unten
+
+   //make links between the triangles
+   entryBuffer[0].link(0, &(entryBuffer[2]), 2); //Kante vorne links
+   entryBuffer[0].link(2, &(entryBuffer[1]), 0); //Kante vorne rechts
+   entryBuffer[0].link(1, &(entryBuffer[3]), 1); //kante vorne unten
+
+   entryBuffer[1].link(2, &(entryBuffer[2]), 0); //Kante hinten
+   entryBuffer[1].link(1, &(entryBuffer[3]), 0); //kante rechts unten
+
+   entryBuffer[2].link(1, &(entryBuffer[3]), 2); //kante links unten
+
+}
+//*************************************************************************************************
+
+/*! \brief Search a tetrahedron that contains the origin.
+ * Start with four arbitrary support points in epaVolume that form a
+ * tetrahedron. (This needn't contain the origin.)
+ * This algorithm will search and return an altered tetrahedron
+ * containing the origin. Do only use this function if the object/body
+ * certainly contains the origin!
+ * \return True, if a tetrahedron was found. False if search has been aborted.
+ */
+inline bool EPA::searchTetrahedron(Support &geom1,
+                                   Support &geom2,
+                                   std::vector<Vec3>& epaVolume,
+                                   std::vector<Vec3>& supportA,
+                                   std::vector<Vec3>& supportB,
+                                   EPA_EntryBuffer& entryBuffer,
+                                   real_t margin )
+{
+   //Store the point no longer needed (0 if all points are needed, and origin is contained.)
+   int loopCount = 0;
+   int pointIndexToRemove = -1;
+   Vec3 newSearchDirection;
+   do{
+      loopCount++;
+      pointIndexToRemove = -1;
+      //Check if opposite tetrahedron point and orign are on the same side
+      //of the face. (for all faces)
+      Vec3 normal0T = (epaVolume[1] -epaVolume[0]) % (epaVolume[2]-epaVolume[0]);
+      real_t dot_val = normal0T*epaVolume[0];
+      if( (normal0T*epaVolume[3] < dot_val) == (dot_val < real_t(0.0)) ) {
+         pointIndexToRemove = 3;
+         newSearchDirection = (normal0T*epaVolume[3] < dot_val) ? normal0T : -normal0T;
+      }
+
+      Vec3 normal1T = (epaVolume[2] -epaVolume[1]) % (epaVolume[3]-epaVolume[1]);
+      dot_val = normal1T*epaVolume[1];
+      if( (normal1T*epaVolume[0] < dot_val) == (dot_val < real_t(0.0)) ) {
+         pointIndexToRemove = 0;
+         newSearchDirection = (normal1T*epaVolume[0] < dot_val) ? normal1T : -normal1T;
+      }
+
+      Vec3 normal2T = (epaVolume[3] -epaVolume[2]) % (epaVolume[0]-epaVolume[2]);
+      dot_val = normal2T*epaVolume[2];
+      if( (normal2T*epaVolume[1] < dot_val) == (dot_val < real_t(0.0)) ) {
+         pointIndexToRemove = 1;
+         newSearchDirection = (normal2T*epaVolume[1] < dot_val) ? normal2T : -normal2T;
+      }
+
+      Vec3 normal3T = (epaVolume[0] -epaVolume[3]) % (epaVolume[1]-epaVolume[3]);
+      dot_val = normal3T*epaVolume[3];
+      if( (normal3T*epaVolume[2] < dot_val) == (dot_val < real_t(0.0)) ) {
+         pointIndexToRemove = 2;
+         newSearchDirection = (normal3T*epaVolume[2] < dot_val) ? normal3T : -normal3T;
+      }
+      //Origin not contained in tetrahedron.
+      if(pointIndexToRemove != -1){
+         if(loopCount > 50){
+            return false;
+         }
+         //Get new support point and replace old.
+         /*std::cerr << "Search Direction is: "<< newSearchDirection << std::endl;
+                   std::cerr << "Projection of unnecc. point " << pointIndexToRemove << ": " << epaVolume[pointIndexToRemove] * newSearchDirection << std::endl;
+                   std::cerr << "Projection of other points: " << epaVolume[(pointIndexToRemove+1)%4] * newSearchDirection << std::endl;*/
+         newSearchDirection = newSearchDirection.getNormalizedOrZero();
+         /*supportA[pointIndexToRemove] = geom1.supportContactThreshold(newSearchDirection);
+                   supportB[pointIndexToRemove] = geom2.supportContactThreshold(-newSearchDirection);
+                   epaVolume[pointIndexToRemove] = supportA[pointIndexToRemove] - supportB[pointIndexToRemove];*/
+         replaceSupportMargin(geom1, geom2, newSearchDirection, margin, epaVolume, supportA, supportB, (size_t)pointIndexToRemove);
+         //std::cerr << "Projection of new support point " << epaVolume[pointIndexToRemove] << ": " << epaVolume[pointIndexToRemove] * newSearchDirection << std::endl;
+
+      }
+   }
+   while(pointIndexToRemove != -1);
+   //std::cerr << "Found Tet after " << loopCount << " searches." << std::endl;
+
+   //Build final tetrahedron
+   Vec3 check_normal = (epaVolume[1] -epaVolume[0]) % (epaVolume[2]-epaVolume[0]);
+   if(check_normal*epaVolume[3] > check_normal*epaVolume[0]){
+      //p3 is behind.
+      createInitialTetrahedron(1, 0, 2, 3, epaVolume, entryBuffer);
+   }else{
+      //p3 is in front
+      createInitialTetrahedron(1, 3, 2, 0, epaVolume, entryBuffer);
+   }
+   return true;
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Calculate a Circle through the for Points A, B, C, D.
+ * \param center Contains the center point of the circle after the call
+ * \return The squared radius of the circle or a negative value if no such circle exists.
+ */
+inline real_t EPA::calculateCircle(const Vec3& A, const Vec3& B, const Vec3& C,
+                                   const Vec3& D, Vec3& center ){
+   const Vec3 n1(A-B);
+   const Vec3 n2(A-C);
+   const Vec3 n3(A-D);
+
+   // Here we already see if such circle exists.
+   const real_t det = n1 * (n2 % n3);
+   if(floatIsEqual(det, real_t(0.0))){
+      //no circle exists. Leave center untouched, and return -1.0
+      return real_t(-1.0);
+   }
+   const real_t Alen = A.sqrLength();
+   const real_t d1 = (Alen - B.sqrLength())*real_t(0.5);
+   const real_t d2 = (Alen - C.sqrLength())*real_t(0.5);
+   const real_t d3 = (Alen - D.sqrLength())*real_t(0.5);
+
+   //Apply solution formula
+   center = (real_t(1.0)/det)*(d1 * (n2 % n3) + d2 * (n3 % n1) + d3 * (n1 % n2));
+
+   return (A - center).sqrLength();
+}
+//*************************************************************************************************
+
+} //collision_detection
+} //mesa_pd
+} //walberla
diff --git a/src/mesa_pd/collision_detection/EPA.h b/src/mesa_pd/collision_detection/EPA.h
new file mode 100644
index 0000000000000000000000000000000000000000..46d59a08ad592056d3e93246a0ba435bf57c3ca0
--- /dev/null
+++ b/src/mesa_pd/collision_detection/EPA.h
@@ -0,0 +1,476 @@
+//======================================================================================================================
+//
+//  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 EPA.h
+//! \author Tobias Scharpff
+//! \author Tobias Leemann
+//
+//  DISCLAIMER: The following source file contains modified code from the SOLID-3.5 library for
+//  interference detection as it is published in the book "Collision Detection in Interactive
+//  3D Environments" by Gino van den Bergen <info@dtecta.com>. Even though the original source
+//  was published under the GPL version 2 not allowing later versions, the original author of the
+//  source code permitted the relicensing of the SOLID-3.5 library under the GPL version 3 license.
+//
+//=================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/collision_detection/GJK.h>
+#include <mesa_pd/collision_detection/Support.h>
+
+#include <vector>
+
+namespace walberla {
+namespace mesa_pd {
+namespace collision_detection {
+
+/*!
+ * \brief The Expanding-Polytope Algorithm.
+ */
+class EPA
+{
+private :
+   //**Type definitions****************************************************************************
+   class EPA_Edge;
+   class EPA_Triangle;
+   class EPA_TriangleComp;
+
+   typedef std::vector<EPA_Triangle>  EPA_EntryBuffer;
+   typedef std::vector<EPA_Triangle*> EPA_EntryHeap;
+   typedef std::vector<EPA_Edge>      EPA_EdgeBuffer;
+   //**********************************************************************************************
+
+public:
+   static constexpr real_t contactThreshold = real_t(1e-8);
+
+   //**Query functions*****************************************************************************
+   /*!\name Query functions */
+   //@{
+   bool doEPAcontactThreshold( Support &geom1,
+                               Support &geom2,
+                               const GJK& gjk,
+                               Vec3& normal,
+                               Vec3& contactPoint,
+                               real_t& penetrationDepth);
+
+
+   bool doEPAcontactThreshold( Support &geom1,
+                               Support &geom2,
+                               const GJK& gjk,
+                               Vec3& normal,
+                               Vec3& contactPoint,
+                               real_t& penetrationDepth,
+                               real_t eps_rel);
+   
+   bool doEPAmargin( Support &geom1,
+                     Support &geom2,
+                     const GJK& gjk,
+                     Vec3& normal,
+                     Vec3& contactPoint,
+                     real_t& penetrationDepth,
+                     real_t margin);
+
+   bool doEPA( Support &geom1,
+               Support &geom2,
+               const GJK& gjk,
+               Vec3& normal,
+               Vec3& contactPoint,
+               real_t& penetrationDepth,
+               real_t margin,
+               real_t eps_rel );
+
+
+
+   //@}
+   //**********************************************************************************************
+
+   //**Getter/Setter functions*****************************************************************************
+   /*!\name Getter and Setter functions */
+   //@{
+   inline void setMaxSupportPoints( size_t maxSupportPoints) {maxSupportPoints_ = maxSupportPoints;}
+
+   inline size_t getMaxSupportPoints() {return maxSupportPoints_;}
+
+   inline void setMaxTriangles( size_t maxTriangles) {maxTriangles_ = maxTriangles;}
+
+   inline size_t getMaxTriangles() {return maxTriangles_;}
+
+   inline int getNumIterations() const {return numIterations_; }
+
+   inline bool useSphereOptimization() const {return bUseSphereOptimization_; }
+   inline void useSphereOptimization(const bool useIt) {bUseSphereOptimization_ = useIt;}
+
+   //@}
+   //**********************************************************************************************
+
+private:
+   //**Utility functions***************************************************************************
+   /*!\name Utility functions */
+   //@{
+   inline void pushSupportMargin(const Support &geom1,
+                                 const Support &geom2,
+                                 const Vec3& dir,
+                                 const real_t margin,
+                                 std::vector<Vec3>& epaVolume,
+                                 std::vector<Vec3>& supportA,
+                                 std::vector<Vec3>& supportB);
+
+   inline void replaceSupportMargin(const Support &geom1,
+                                    const Support &geom2,
+                                    const Vec3& dir,
+                                    const real_t margin,
+                                    std::vector<Vec3>& epaVolume,
+                                    std::vector<Vec3>& supportA,
+                                    std::vector<Vec3>& supportB,
+                                    size_t indexToReplace);
+
+   inline void removeSupportMargin(std::vector<Vec3>& epaVolume, std::vector<Vec3>& supportA, std::vector<Vec3>& supportB);
+
+   inline bool originInTetrahedron            ( const Vec3& A, const Vec3& B, const Vec3& C,
+                                                const Vec3& D );
+   inline bool originInTetrahedronVolumeMethod( const Vec3& A, const Vec3& B, const Vec3& C,
+                                                const Vec3& D );
+   inline bool pointInTetrahedron             ( const Vec3& A, const Vec3& B, const Vec3& C,
+                                                const Vec3& D, const Vec3& point );
+   bool searchTetrahedron              (Support &geom1,
+                                        Support &geom2,
+                                        std::vector<Vec3>& epaVolume,
+                                        std::vector<Vec3>& supportA,
+                                        std::vector<Vec3>& supportB,
+                                        EPA_EntryBuffer& entryBuffer,
+                                        real_t margin );
+
+   void createInitialTetrahedron       ( size_t top,
+                                         size_t frontLeft,
+                                         size_t frontRight,
+                                         size_t back,
+                                         std::vector<Vec3>& epaVolume,
+                                         EPA_EntryBuffer& entryBuffer );
+
+   void createInitialSimplex           ( size_t numPoints,
+                                         Support &geom1,
+                                         Support &geom2,
+                                         std::vector<Vec3>& supportA,
+                                         std::vector<Vec3>& supportB,
+                                         std::vector<Vec3>& epaVolume,
+                                         EPA_EntryBuffer& entryBuffer,
+                                         real_t margin );
+   inline real_t calculateCircle              ( const Vec3& A,
+                                                const Vec3& B,
+                                                const Vec3& C,
+                                                const Vec3& D,
+                                                Vec3& center );
+   //@}
+   //**********************************************************************************************
+
+
+private:
+   //EPA constants
+   size_t maxSupportPoints_ = 100;
+   size_t maxTriangles_     = 200;
+
+   int numIterations_ = 0;
+   bool bUseSphereOptimization_ = false;
+};
+//*************************************************************************************************
+
+
+
+
+//=================================================================================================
+//
+//  EPA::EPA_TRIANGLE CLASS DEFINITION
+//
+//=================================================================================================
+
+//*************************************************************************************************
+/*!\brief Class storing Information about a triangular facette (Triangle) of the EPA-Polytope
+ *
+ * see Collision detction in interactiv 3D environments; Gino van den bergen page 155
+ */
+class EPA::EPA_Triangle {
+public:
+   //**Constructor*********************************************************************************
+   /*!\name Constructor */
+   //@{
+   explicit inline EPA_Triangle( size_t a, size_t b, size_t c, const std::vector<Vec3>& points );
+   //@}
+   //**********************************************************************************************
+
+   //**Get functions*******************************************************************************
+   /*!\name Get functions */
+   //@{
+   ///Returns the index of the internal vertex i(=0,1,2) within the EPA scope.
+   inline size_t      operator[]( size_t i )                           const {return indices_[i];}
+   ///Returns the point closest to the origin of the affine hull of the triangle, which is also the normal.
+   inline const Vec3& getClosest()                                     const {return closest_;}
+   ///Returns the normal of the triangle. Normal is not normalized!
+   inline const Vec3& getNormal()                                      const {return normal_;}
+   inline Vec3        getClosestPoint(const std::vector<Vec3>& points) const;
+   ///Returns the squared distance to the closest to the origin of the affine hull of the triangle.
+   inline real_t      getSqrDist()                                     const {return sqrDist_;}
+   ///Returns true if the triangle is no longer part of the EPA polygon.
+   inline bool        isObsolete()                                     const {return obsolete_;}
+   inline bool        isClosestInternal()                              const;
+   //@}
+   //**********************************************************************************************
+
+   //**Utility functions***************************************************************************
+   /*!\name Utility functions */
+   //@{
+   inline bool        link( size_t edge0, EPA_Triangle* tria, size_t edge1 );
+   inline void        silhouette( const Vec3& w, EPA_EdgeBuffer& edgeBuffer );
+   //@}
+   //**********************************************************************************************
+
+private:
+   //**Utility functions***************************************************************************
+   /*!\name Utility functions */
+   //@{
+   void silhouette( size_t index, const Vec3& w, EPA_EdgeBuffer& edgeBuffer );
+   //@}
+   //**********************************************************************************************
+
+   //**Member variables****************************************************************************
+   /*!\name Member variables */
+   //@{
+   size_t         indices_[3];     //!< indices of the vertices of the triangle
+   bool           obsolete_;       //!< flag to denote whether die triangle is visible from the new support point
+
+   Vec3           closest_;        //!< the point closest to the origin of the affine hull of the triangle
+   Vec3           normal_;         //!< normal pointing away from the origin
+   real_t         bar_[3];         //!< the barycentric coordinate of closest_
+   real_t         sqrDist_;        //!< =key; square distance of closest_ to the origin
+
+   EPA_Triangle*  adjTriangle_[3]; //!< pointer to the triangle adjacent to edge i(=0,1,2)
+   size_t         adjEdges_[3];    //!< for each adjoining triangle adjTriangle_[i], the index of the adjoining edge
+   //@}
+   //**********************************************************************************************
+};
+//*************************************************************************************************
+
+
+//=================================================================================================
+//
+//  EPA::EPA_EDGE CLASS DEFINITION
+//
+//=================================================================================================
+
+//*************************************************************************************************
+/*!\brief Class storing Information about an Edge of the EPA-Polytope
+ */
+class EPA::EPA_Edge {
+public:
+   //**Constructor*********************************************************************************
+   /*!\name Constructor */
+   //@{
+   EPA_Edge( EPA_Triangle* triangle, size_t index );
+   //@}
+   //**********************************************************************************************
+
+   //**Get functions*******************************************************************************
+   /*!\name Get functions */
+   //@{
+   ///Return the triangle this edge belongs to.
+   EPA_Triangle* getTriangle() const {return triangle_;}
+   ///Get the Index of this edge in its triangle.
+   size_t        getIndex()    const {return startIdx_;}
+   ///Return the start point index  of an edge.
+   size_t        getStart()    const {return (*triangle_)[startIdx_];}
+   ///Return the end point index of an edge.
+   size_t        getEnd()      const {return (*triangle_)[(startIdx_+1) % 3];}
+   //@}
+   //**********************************************************************************************
+
+private:
+   //**Member variables****************************************************************************
+   /*!\name Member variables */
+   //@{
+   EPA_Triangle* triangle_; //!< the EPA triangle the edge is contained in
+   size_t startIdx_; //!< the index of the point the edge starts at (0, 1, 2)
+   //@}
+   //**********************************************************************************************
+};
+//*************************************************************************************************
+
+
+
+//=================================================================================================
+//
+//  EPA::EPA_TRIANGLECOMP CLASS DEFINITION
+//
+//=================================================================================================
+
+//*************************************************************************************************
+/*!\brief
+ * Compare Triangles by their closest points to sort the triangle heap.
+ */
+class EPA::EPA_TriangleComp {
+public:
+   //**Binary function call operator***************************************************************
+   /*!\name Binary function call operator */
+   //@{
+   ///Compare two triangles by their distance.
+   inline bool operator()( const EPA_Triangle *tria1,
+                           const EPA_Triangle *tria2 )
+   { return tria1->getSqrDist() > tria2->getSqrDist(); }
+   //@}
+   //**********************************************************************************************
+};
+//*************************************************************************************************
+
+
+//=================================================================================================
+//
+//  EPA_EDGE CONSTRUCTOR
+//
+//=================================================================================================
+
+//*************************************************************************************************
+/*!\brief
+ * Construct a new Triangle Edge.
+ */
+inline EPA::EPA_Edge::EPA_Edge( EPA_Triangle* triangle,
+                                size_t index )
+   : triangle_(triangle)
+   , startIdx_(index)
+{
+}
+//*************************************************************************************************
+
+
+//=================================================================================================
+//
+//  EPA::EPA_TRIANGLE GET FUNCTIONS
+//
+//=================================================================================================
+
+
+//*************************************************************************************************
+/*! \brief Calculates the corresponding closest point from the given points, using barycentric coordinates.
+ */
+inline Vec3 EPA::EPA_Triangle::getClosestPoint(const std::vector<Vec3>& points) const
+{
+   return   bar_[0] * points[indices_[0]]
+         + bar_[1] * points[indices_[1]]
+         + bar_[2] * points[indices_[2]];
+
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! Returns true if the point closest to the origin of the affine hull of the triangle, lies inside the triangle.
+ */
+inline bool EPA::EPA_Triangle::isClosestInternal() const
+{
+   real_t tol = real_t(0.0);
+   return bar_[0] >= tol
+         && bar_[1] >= tol
+         && bar_[2] >= tol;
+}
+//*************************************************************************************************
+
+
+//=================================================================================================
+//
+//  EPA UTILITY FUNCTIONS
+//
+//=================================================================================================
+
+//*************************************************************************************************
+/*! \brief Calucates a support point of a body extended by threshold.
+ * Adds this support and the base points at bodies a and b to the vector.
+ * \param geom The body.
+ * \param dir The support point direction.
+ * \param margin Extension of the Body.
+ */
+inline void EPA::pushSupportMargin(const Support &geom1,
+                                   const Support &geom2,
+                                   const Vec3& dir,
+                                   const real_t margin,
+                                   std::vector<Vec3>& epaVolume,
+                                   std::vector<Vec3>& supportA,
+                                   std::vector<Vec3>& supportB)
+{
+   Vec3 ndir;
+   if(floatIsEqual(dir.sqrLength(), real_t(1.0))){
+      ndir = dir.getNormalizedOrZero();
+   }else{
+      ndir = dir;
+   }
+   Vec3 sA = geom1.support(ndir);
+   Vec3 sB = geom2.support(-ndir);
+   supportA.push_back(sA);
+   supportB.push_back(sB);
+
+   Vec3 support = sA -sB + real_t(2.0) * ndir * margin;
+   epaVolume.push_back(support);
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Calucates a support point of a body extended by threshold.
+ * Replaces the old value in the vectors at "IndexToReplace" with this support and the base points at bodies a and b .
+ * \param geom The body.
+ * \param dir The support point direction.
+ * \param margin Extension of the Body.
+ */
+inline void EPA::replaceSupportMargin(const Support &geom1,
+                                      const Support &geom2,
+                                      const Vec3& dir,
+                                      const real_t margin,
+                                      std::vector<Vec3>& epaVolume,
+                                      std::vector<Vec3>& supportA,
+                                      std::vector<Vec3>& supportB,
+                                      size_t indexToReplace)
+{
+   Vec3 ndir;
+   if(floatIsEqual(dir.sqrLength(), real_t(1.0))){
+      ndir = dir.getNormalizedOrZero();
+   }else{
+      ndir = dir;
+   }
+   Vec3 sA = geom1.support(ndir);
+   Vec3 sB = geom2.support(-ndir);
+   Vec3 support = sA -sB + real_t(2.0) * ndir * margin;
+
+   supportA[indexToReplace] = sA;
+   supportB[indexToReplace] = sB;
+   epaVolume[indexToReplace] = support;
+}
+//*************************************************************************************************
+
+//*************************************************************************************************
+/*! \brief Removes a support point from the volume.
+ */
+inline void EPA::removeSupportMargin(std::vector<Vec3>& epaVolume,
+                                     std::vector<Vec3>& supportA,
+                                     std::vector<Vec3>& supportB)
+{
+   supportA.pop_back();
+   supportB.pop_back();
+   epaVolume.pop_back();
+}
+//*************************************************************************************************
+
+
+//@}
+
+} // namespace fcd
+} // namespace pe
+} // namespace walberla
diff --git a/src/mesa_pd/collision_detection/GJK.cpp b/src/mesa_pd/collision_detection/GJK.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..edd2a963ff1cf5678e2363af7aa28e3e202c23e7
--- /dev/null
+++ b/src/mesa_pd/collision_detection/GJK.cpp
@@ -0,0 +1,1072 @@
+//======================================================================================================================
+//
+//  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 GJK.cpp
+//! \author Tobias Scharpff
+//! \author Tobias Leemann
+//
+//======================================================================================================================
+
+#include "GJK.h"
+
+#include <vector>
+
+#include <core/Abort.h>
+#include <core/math/Limits.h>
+#include <core/math/Vector3.h>
+
+
+namespace walberla {
+namespace mesa_pd {
+namespace collision_detection {
+
+GJK::GJK()
+   : simplex_(4)
+   , supportA_(4)
+   , supportB_(4)
+   , numPoints_(0)
+{
+   d_ = Vec3(real_t(0.0),real_t(0.6),real_t(0.8)); // just start with any vector of length 1
+}
+
+/**
+ * \brief Calucate a support point of a particle extended by threshold.
+ * \param geom support functions for particle 1 and 2.
+ * \param dir The support point direction.
+ * \param threshold Extension of the particle.
+ */
+const Vec3 GJK::putSupport(const Support &geom1,
+                           const Support &geom2,
+                           const Vec3& dir,
+                           const real_t margin,
+                           std::vector<Vec3> &simplex,
+                           std::vector<Vec3> &supportA,
+                           std::vector<Vec3> &supportB,
+                           size_t index)
+{
+   supportA[index] = geom1.support(dir);
+   supportB[index] = geom2.support(-dir);
+   Vec3 supp = supportA[index]- supportB[index] + (real_t(2.0) * dir * margin);
+   simplex[index] = supp;
+   return supp;
+}
+//*************************************************************************************************
+
+//=================================================================================================
+//
+//  QUERY FUNCTIONS
+//
+//=================================================================================================
+
+//*************************************************************************************************
+/*! \brief Calculate an upper bound for the distance of two Geometries.
+ * \return Distance between geom1 and geom2 or 0.0 if they are intersecting.
+ */
+real_t GJK::doGJK(const Support &geom1, const Support &geom2, Vec3& normal, Vec3& contactPoint)
+{
+   //Variables
+   Vec3 support;     //the current support point
+   real_t ret;         //return value aka distance between geom1 and geom2
+
+
+   ////////////////////////////////////////////////////////////////////////
+   //Initial initialisation step
+   ret = 0.0;
+   supportA_.resize(4);
+   supportB_.resize(4);
+   simplex_.resize(4);
+
+   //get any first support point
+
+   supportA_[0] = geom1.support(d_);
+   supportB_[0] = geom2.support(-d_);
+   support = supportA_[0] - supportB_[0];
+
+   //add this point to the simplex_
+   simplex_[0] = support;
+   numPoints_ = 1;
+
+   if(support * d_ < real_t(0.0)){
+      //we went as far as we could in direction 'd' but not passed the origin
+      //this means the bodies don't overlap
+      ret = calcDistance(normal, contactPoint);
+      return ret;
+   }
+
+   //first real search direction is in the opposite direction of the first support po
+   d_ = -support;
+
+   ////////////////////////////////////////////////////////////////////////
+   //GJK main loop
+   while (true) {
+      //get the support point in the current search direction
+      normalize(d_);
+      supportA_[numPoints_] = geom1.support(d_);
+      supportB_[numPoints_] = geom2.support(-d_);
+      support = supportA_[numPoints_] - supportB_[numPoints_];
+      //std::cerr << "[GJK] Support Direction: " << d_ << std::endl;
+      //std::cerr << "[GJK] Got Support: " << support << std::endl;
+
+      //check if "support" is passed the origin in search direction
+      if(support * d_ < real_t(0.0)){
+         //we went as far as we could in direction 'd' but not passed the origin
+         //this means the bodies don't overlap
+         //calc distance simplex to Origin
+         ret = calcDistance(normal, contactPoint);
+
+         return ret;
+      }
+
+      //add the new support point into the simplex
+      simplex_[numPoints_] = support;
+      numPoints_++;
+
+      ////////////////////////////////////////////////////////////////
+      //check if the origin is in the simplex
+      //if it is the triangle mashes are overlapping
+      switch(numPoints_)
+      {
+      case 2:
+      {
+         if(simplex2(d_)) {
+            simplex_.pop_back();
+            simplex_.pop_back();
+            supportA_.pop_back();
+            supportA_.pop_back();
+            supportB_.pop_back();
+            supportB_.pop_back();
+            return ret;
+         }
+      }
+         break;
+
+      case 3:
+      {
+         if(simplex3(d_)) {
+            simplex_.pop_back();
+            supportA_.pop_back();
+            supportB_.pop_back();
+            return ret;
+         }
+      }
+         break;
+
+      case 4:
+      {
+         if(simplex4(d_)) {
+
+            return ret;
+         }
+      }
+         break;
+      default:
+      {
+         WALBERLA_ABORT( "Number of points in the simplex is not 1<=n<=4" );
+      }
+         break;
+      }
+   }
+
+   return ret; //never reach this point
+}
+//*************************************************************************************************
+
+//*************************************************************************************************
+/**
+ * \brief Compute if two geometries intersect. Both can be enlarged by a specified margin.
+ * \param geom1 support function for the first particle
+ * \param geom2 support function for the second particle
+ * \param margin The margin by which the objects will be enlarged.
+ * \return true, if an itersection is found.
+ */
+bool GJK::doGJKmargin(const Support &geom1, const Support &geom2, real_t margin)
+{
+   //Variables
+   Vec3 support;     //the current support point
+
+   ////////////////////////////////////////////////////////////////////////
+   //Initial initialisation step
+   supportA_.resize(4);
+   supportB_.resize(4);
+   simplex_.resize(4);
+
+   //get any first support point
+   if(numPoints_ != 0) {
+      normalize(d_);
+   }
+   support = putSupport(geom1, geom2, d_, margin, simplex_, supportA_, supportB_, 0);
+
+   //std::cerr << "Support 1: " << support << std::endl;
+   //add this point to the simplex_
+   numPoints_ = 1;
+
+   //first real_t search direction is in the opposite direction of the first support point
+   d_ = -support;
+
+   /*
+   if(support * d_ < 0.0){
+         //we went as far as we could in direction 'd' but not passed the origin
+         //this means the triangle mashes don't overlap
+         //and as the support()-function extends the support point by contactThreshold
+         //the mashes are not even close enough to be considered in contact.
+         return false;
+   }
+   */
+   ////////////////////////////////////////////////////////////////////////
+   //GJK main loop
+   while (true) {
+      //get the support point in the current search direction
+      normalize(d_);
+      support = putSupport(geom1, geom2, d_, margin, simplex_, supportA_, supportB_, numPoints_);
+
+      //std::cerr << "GJK: Got support storing at " << (int)numPoints_ << ": "<< support << std::endl;
+      //check if "support" is passed the origin in search direction
+      if(support * d_ < 0.0){
+         // std::cerr << support * d_ << ": Returning false." << std::endl;
+         //we went as far as we could in direction 'd' but not passed the origin
+         //this means the triangle meshes don't overlap
+         //and as the support()-function extends the support point by contactThreshold
+         //the meshes are not even close enough to be considered in contact.
+         return false;
+      }
+
+      //add the new support point into the simplex
+      numPoints_++;
+
+      //std::cerr << "Num points " << (int)numPoints_ << std::endl;
+      ////////////////////////////////////////////////////////////////
+      //check if the origin is in the simplex
+      //if it is the triangle mashes are overlapping
+      switch(numPoints_)
+      {
+      case 2:
+      {
+         if(simplex2(d_)) {
+
+            //std::cerr << "Simplex2 success." << std::endl;
+            while(simplex_.size() > numPoints_){
+               simplex_.pop_back();
+               supportA_.pop_back();
+               supportB_.pop_back();
+            }
+            return true;
+         }
+      }
+         break;
+
+      case 3:
+      {
+         if(simplex3(d_)) {
+            //std::cerr << "Simplex3 success." << std::endl;
+            while(simplex_.size() > numPoints_){
+               simplex_.pop_back();
+               supportA_.pop_back();
+               supportB_.pop_back();
+            }
+            return true;
+         }
+      }
+         break;
+
+      case 4:
+      {
+         if(simplex4(d_)) {
+            //std::cerr << "Simplex4 success." << std::endl;
+            return true;
+         }
+      }
+         break;
+
+      default:
+      {
+         //std::cerr << "numPoints_="<< numPoints_ <<std::endl;
+         WALBERLA_ABORT( "Number of points in the simplex is not 1<=n<=4" );
+      }
+         break;
+      }
+   }
+
+   return false; //never reach this point
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/**
+ * \brief Calculate clostes Point in the simplex and its distance to the origin.
+ */
+inline real_t GJK::calcDistance( Vec3& normal, Vec3& contactPoint )
+{
+   //find the point in the simplex closest to the origin#
+   //its distance to the origin is the distance of the two objects
+   real_t dist= 0.0;
+
+   real_t barCoords[3] = { 0.0, 0.0, 0.0};
+   real_t& u = barCoords[0];
+   real_t& v = barCoords[1];
+   real_t& w = barCoords[2];
+
+   Vec3& A = simplex_[0];
+   Vec3& B = simplex_[1];
+   Vec3& C = simplex_[2];
+   //std::cerr << (int) numPoints_ << " " << A << B << C << std::endl;
+   switch(numPoints_){
+   case 1:
+   {
+      //the only point in simplex is closest to Origin
+      dist = std::sqrt(A.sqrLength());
+      u = real_t(1.0);
+      break;
+   }
+   case 2:
+   {
+      //calc distance Origin to line segment
+      //it is definitively closest do the segment not to one of the end points
+      //as the voronoi regions of the points also consist of the border borderline between
+      //point region and segment region
+      // compare "Real-Time Collision Detection" by Christer Ericson page 129ff
+      Vec3 ab = B - A;
+      //Vec3 ac = -A;
+      //Vec3 bc = -simplex[1];
+
+      //calc baryzenctric coordinats
+      // compare "Real-Time Collision Detection" by Christer Ericson page 129
+      //double t = ac*ab;
+      real_t t     = real_t(-1.0) * (A * ab);
+      real_t denom = std::sqrt(ab.sqrLength());
+      u = t / denom;
+      v = real_t(1.0) - u;
+      Vec3 closestPoint = u*A + v*B;
+      dist = std::sqrt(closestPoint.sqrLength());
+      // compare "Real-Time Collision Detection" by Christer Ericson page 130
+      //double& e = t;
+      //double& f = denom;
+      //dist = ac.sqrLength() -  e*e/f;
+      break;
+   }
+   case 3:
+   {
+      //origin is surly in the voronoi region of the face itself
+      //not the bordering lines or one of the 3 points
+      //to be more precise it is also in normal direction.
+      // compare "Real-Time Collision Detection" by Christer Ericson page 139
+      //TODO: evlt kann man das berechnen ohne den projektionspunkt zu bestimmen
+
+      //Vec3 ab= B - A;
+      //Vec3 ac= C - A;
+      //Vec3 bc= C - B;
+      Vec3& n = d_; //we already know the normal
+      const Vec3&  nT = n;
+
+      real_t vc = nT * (A % B);
+      real_t va = nT * (B % C);
+      real_t vb = nT * (C % A);
+      real_t denom = real_t(1.0) / (va + vb + vc);
+      u = va * denom;
+      v = vb * denom;
+      w = real_t(1.0) - u - v;
+      //std::cerr << u << " " << v << " " << w << std::endl;
+      Vec3 closestPoint = u*A + v*B + w*C;
+      dist = std::sqrt(closestPoint.sqrLength());
+
+      break;
+   }
+   default:
+   {
+      std::cout << "falsche anzahl an Punkten im simplex" <<std::endl;
+      break;
+   }
+   }
+
+   Vec3 pointOnA = u * supportA_[0];
+   Vec3 pointOnB = u * supportB_[0];
+   for( size_t i = 1; i < numPoints_; ++i) {
+      pointOnA += barCoords[i] * supportA_[i];
+      pointOnB += barCoords[i] * supportB_[i];
+   }
+
+   normal = (pointOnA - pointOnB).getNormalized();
+   contactPoint = (pointOnA + pointOnB) * real_t(0.5);
+
+
+   return dist;
+}
+//*************************************************************************************************
+
+
+
+//=================================================================================================
+//
+//  UTILITY FUNCTIONS
+//
+//=================================================================================================
+
+//*************************************************************************************************
+/*! \brief Process a simplex with two nodes.
+ */
+bool GJK::simplex2(Vec3& d)
+{
+   //the simplex is a line
+   const Vec3& A = simplex_[1];  //The Point last added to the simplex
+   const Vec3& B = simplex_[0];  //The Point that was already in the simplex
+   const Vec3  AO  = -A;         //The vector A->O with 0 the origin
+   const Vec3&  AOt = AO;         //The transposed vector A->O with O the origin
+   const Vec3  AB  = B-A;        //The vector A->B
+
+   if( sameDirection(AOt, AB) ) {
+      //The origin O is in the same direction as B is so the line AB is closest to the origin
+      //=> keep A and B in the simplex
+      d = AB % AO % AB;
+   }
+   else {
+      //The origin is not in the direction of B seen from A.
+      //So O lies in the voronoi region of A
+      //=> simplex is just A
+      simplex_[0] = A; //aka simplex_[1]
+      supportA_[0] = supportA_[1];
+      supportB_[0] = supportB_[1];
+      numPoints_  = 1;
+      d = AO;
+   }
+
+   //if the new search direction has zero length
+   //than the origin is on the simplex
+   if(zeroLengthVector(d)) {
+      d_ = Vec3(real_t(0.0),real_t(0.6),real_t(0.8)); // give the GJK a chance to rerun
+      return true;
+   }
+   return false;
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Process a simplex with three nodes.
+ */
+bool GJK::simplex3(Vec3& d)
+{
+   //the simplex is a triangle
+   const Vec3& A = simplex_[2];  //The Point last added to the simplex
+   const Vec3& B = simplex_[1];  //One Point that was already in the simplex
+   const Vec3& C = simplex_[0];  //One Point that was already in the simplex
+   //ABC is a conterclockwise triangle
+
+   const Vec3  AO  = -A;        //The vector A->O with 0 the origin
+   const Vec3&  AOt = AO;        //The transposed vector A->O with O the origin
+   const Vec3  AB  = B-A;       //The vector A->B
+   const Vec3  AC  = C-A;       //The vector A->C
+   const Vec3  ABC = AB%AC;     //The the normal pointing towards the viewer if he sees a CCW triangle ABC
+
+   if( sameDirection(AOt, (AB % ABC)) ) {
+      //Origin is on the outside of the triangle of the line AB
+      if( AOt * AB > 0.0) {
+         //Origin in the voronoi region of AB outside the triangle
+         //=> AB is the new simplex
+         simplex_[0] = B; //aka simplex_[1]
+         simplex_[1] = A; //aka simplex_[2]
+         supportA_[0] = supportA_[1];
+         supportA_[1] = supportA_[2];
+         supportB_[0] = supportB_[1];
+         supportB_[1] = supportB_[2];
+         numPoints_  = 2;
+         d = AB % AO % AB;
+
+
+      }
+      else {
+         //STAR
+         if( sameDirection(AOt,AC) ) {
+            //Origin is on a subspace of the voronio region of AC
+            //=> AC is the new simplex
+            //simplex_[0] = C; //aka simplex_[0] already there
+            simplex_[1] = A; //aka simplex_[2]
+            supportA_[1] = supportA_[2];
+            supportB_[1] = supportB_[2];
+            numPoints_  = 2;
+            d = AC % AO % AC;
+         }
+         else {
+            //Origin is in the voronio region of A
+            //=> A is the new simplex
+            simplex_[0] = A; //aka simplex_[2]
+            supportA_[0] = supportA_[2];
+            supportB_[0] = supportB_[2];
+            numPoints_  = 1;
+            d = AO;
+         }
+      }
+   }
+   else {
+      if( sameDirection(AOt, (ABC % AC)) ) {
+         //Origin is on the outside of the triangle of the line AC
+         //STAR
+         if( AOt * AC > 0.0) {
+            //Origin is on a subspace of the voronio region of AC
+            //=> AC is the new simplex
+            //simplex_[0] = C; //aka simplex_[0] already there
+            simplex_[1] = A; //aka simplex_[2]
+            supportA_[1] = supportA_[2];
+            supportB_[1] = supportB_[2];
+            numPoints_  = 2;
+            d = AC % AO % AC;
+         }
+         else {
+            //Origin is in the voronio region of A
+            //=> A is the new simplex
+            simplex_[0] = A; //aka simplex_[2]
+            supportA_[0] = supportA_[2];
+            supportB_[0] = supportB_[2];
+            numPoints_  = 1;
+            d = AO;
+         }
+      }
+      else {
+         //origin is above or below the triangle ABC but its mapping on the plane ABC lies within ABC
+         if( sameDirection(AOt, ABC) ) {
+            //Origin is above the triangle
+            //=>Keep triangle as simplex seen from the origin it is already CCW
+            d = ABC;
+         }
+         else {
+            if( sameDirection(AOt, -ABC) ) {
+               //Origin is below the triangle
+               //=>Keep triangle as simplex.
+               //seen from the origin ABC is CW so change the winding
+               Vec3 temp = B; //aka simplex_[1]
+               simplex_[1] = C; //aka simplex_[0]
+               simplex_[0] = temp;
+               //simplex_[2] = A; //aka simplex_[2] already there
+               //old simplex 2:A 1:B 0:C
+               //simplex now contains 2:A 1:C 0:B
+
+               temp = supportA_[1];
+               supportA_[1] = supportA_[0];
+               supportA_[0] = temp;
+               temp = supportB_[1];
+               supportB_[1] = supportB_[0];
+               supportB_[0] = temp;
+
+               d = -ABC;
+            }
+            else{
+               //Origin lies in the triangle
+               return true;
+            }
+         }
+      }
+   }
+
+   //if the new search direction has zero length
+   //than the origin is on the boundary of the simplex
+   if(zeroLengthVector(d)) {
+      d_ = Vec3(real_t(0.0),real_t(0.6),real_t(0.8)); // give the GJK a chance to rerun
+      return true;
+   }
+   return false;
+}
+//*************************************************************************************************
+
+
+//*************************************************************************************************
+/*! \brief Process a simplex with four nodes.
+ */
+bool GJK::simplex4(Vec3& d)
+{
+   //the simplex is a tetrahedron
+   const Vec3& A  = simplex_[3];  //The Point last added to the tetrahedron
+   //t in front mens just a temp varialble
+   const Vec3& B = simplex_[2];  //One Point that was already in the simplex
+   const Vec3& C = simplex_[1];  //One Point that was already in the simplex
+   const Vec3& D = simplex_[0];
+   //BCD is a clockwise triangle wenn seen from A
+
+   const Vec3  AO  = -A;      //The vector A->O with 0 the origin
+   const Vec3&  AOt = AO;      //The transposed vector A->O with O the origin
+   const Vec3  AB  = B-A;     //The vector A->B
+   const Vec3  AC  = C-A;     //The vector A->C
+   const Vec3  AD  = D-A;     //The vector A-D
+
+   //https://mollyrocket.com/forums/viewtopic.php?p=1829#1829
+   unsigned char testWhere = 0;
+
+   const Vec3 ABC = AB % AC; //The the normal pointing out of the tetrahedron towards the viewer if he sees a CCW triangle ABC
+   const Vec3 ACD = AC % AD; //The the normal pointing out of the tetrahedron towards the viewer if he sees a CCW triangle ACD
+   const Vec3 ADB = AD % AB; //The the normal pointing out of the tetrahedron towards the viewer if he sees a CCW triangle ADB
+
+   if(sameDirection(AOt, ABC)) {
+      testWhere |= 0x1;
+   }
+
+   if(sameDirection(AOt, ACD)) {
+      testWhere |= 0x2;
+   }
+
+   if(sameDirection(AOt, ADB)) {
+      testWhere |= 0x4;
+   }
+
+   switch(testWhere)
+   {
+   case 0:
+   {
+      //origin is in the tetrahedro
+      //=> the two triangle mashes overlap
+      //std::cout << "Origin is within the tetrahedron\nA=" << A << " B=" << B << " C="<< C << " D="<< D << std::endl;
+      return true;
+   } break;
+
+   case 1:
+   {
+      // In front of ABC only
+      //Origin is outside the tetrahedron above ABC
+      //=> rearrange simplex to use the triangle case
+      simplex_[0] = C;  //aka simplex_[1] 0:C
+      simplex_[1] = B;  //aka simplex_[2] 1:B
+      simplex_[2] = A;  //aka simplex_[3] 2:A
+
+      supportA_[0] = supportA_[1];
+      supportA_[1] = supportA_[2];
+      supportA_[2] = supportA_[3];
+      supportB_[0] = supportB_[1];
+      supportB_[1] = supportB_[2];
+      supportB_[2] = supportB_[3];
+
+      numPoints_ = 3;
+
+      return simplex3(d);
+   } break;
+
+   case 2:
+   {
+      // In front of ACD only
+      //Origin is outside the tetrahedron above ACD
+      //=> rearrange simplex to use the triangle case
+      //simplex_[0] = D; //aka simplex_[0] 0:D already the case
+      //simplex_[1] = C; //aka simplex_[1] 1:C already the case
+      simplex_[2] = A;   //aka simplex_[3] 2:A
+
+      supportA_[2] = supportA_[3];
+      supportB_[2] = supportB_[3];
+
+      numPoints_ = 3;
+
+      return simplex3(d);
+   } break;
+
+   case 4:
+   {
+      // In front of ADB only
+      //Origin is outside the tetrahedron above ADB
+      //=> rearrange simplex to use the triangle case
+      simplex_[1] = D; //aka simplex_[0] 1:D
+      simplex_[0] = B; //aka simplex_[2] 0:B already there
+      simplex_[2] = A; //aka simplex_[3] 2:A
+
+      supportA_[1] = supportA_[0];
+      supportA_[0] = supportA_[2];
+      supportA_[2] = supportA_[3];
+      supportB_[1] = supportB_[0];
+      supportB_[0] = supportB_[2];
+      supportB_[2] = supportB_[3];
+
+      numPoints_ = 3;
+
+      return simplex3(d);
+   } break;
+
+   case 3:
+   {
+      // In front of ABC and ACD
+      if(sameDirection(AOt, ABC%AC)) {
+         if(sameDirection(AOt, AC%ACD)) {
+            if(sameDirection(AOt, AC)) {
+               //AddEdgeSimplex(A, C);
+               simplex_[0] = C; //aka simplex_[1] 0:C
+               simplex_[1] = A; //aka simplex_[3] 1:A
+
+               supportA_[0] = supportA_[1];
+               supportA_[1] = supportA_[3];
+               supportB_[0] = supportB_[1];
+               supportB_[1] = supportB_[3];
+
+               numPoints_ = 2;
+               d = AC % AO % AC;
+            }
+            else {
+               //AddPointSimplex;
+               simplex_[0] = A; //aka simplex_[3] 0:A
+
+               supportA_[0] = supportA_[3];
+               supportB_[0] = supportB_[3];
+
+               numPoints_ = 1;
+               d = AO;
+            }
+         }
+         else
+         {
+            if(sameDirection(AOt, ACD%AD)) {
+               //AddEdgeSimplex(A, D);
+               //simplex_[0] = D; //aka simplex_[0] 0:D already there
+               simplex_[1] = A; //aka simplex_[3] 1:A
+
+               supportA_[1] = supportA_[3];
+               supportB_[1] = supportB_[3];
+
+               numPoints_ = 2;
+               d = AD % AO % AD;
+            }
+            else {
+               //AddTriangleSimplex(A, C, D);
+               //simplex_[0] = D; //aka simplex_[0] 0:D already there
+               //simplex_[1] = C; //aka simplex_[1] 1:C already there
+               simplex_[2] = A; //aka simplex_[3] 2:A
+
+               supportA_[2] = supportA_[3];
+               supportB_[2] = supportB_[3];
+
+               numPoints_ = 3;
+               d = ACD;
+            }
+         }
+      }
+      else
+      {
+         if(sameDirection(AOt, AB%ABC)) {
+            if(sameDirection(AOt, AB)) {
+               //AddEdgeSimplex(A, B);
+               simplex_[0] = B; //aka simplex_[2] 0:B
+               simplex_[1] = A; //aka simplex_[3] 1:A
+
+               supportA_[0] = supportA_[2];
+               supportA_[1] = supportA_[3];
+               supportB_[0] = supportB_[2];
+               supportB_[1] = supportB_[3];
+
+               numPoints_ = 2;
+               d = AB % AO % AB;
+            }
+            else {
+               //AddPointSimplex;
+               simplex_[0] = A; //aka simplex_[3] 0:A
+
+               supportA_[0] = supportA_[3];
+               supportB_[0] = supportB_[3];
+
+               numPoints_ = 1;
+               d = AO;
+            }
+         }
+         else {
+            //AddTriangleSimplex(A, B, C);
+            simplex_[0] = C; //aka simplex_[1] 0:C
+            simplex_[1] = B; //aka simplex_[2] 1:B
+            simplex_[2] = A; //aka simplex_[3] 2:A
+
+            supportA_[0] = supportA_[1];
+            supportA_[1] = supportA_[2];
+            supportA_[2] = supportA_[3];
+            supportB_[0] = supportB_[1];
+            supportB_[1] = supportB_[2];
+            supportB_[2] = supportB_[3];
+
+            numPoints_ = 3;
+            d = ABC;
+         }
+      }
+   } break;
+
+
+   case 5:
+   {
+      // In front of ADB and ABC
+      if(sameDirection(AOt, ADB%AB)) {
+         if(sameDirection(AOt, AB%ABC)) {
+            if(sameDirection(AOt, AB)) {
+               //AddEdgeSimplex(A, B);
+               simplex_[0] = B; //aka simplex_[2] 0:B
+               simplex_[1] = A; //aka simplex_[3] 1:A
+
+               supportA_[0] = supportA_[2];
+               supportA_[1] = supportA_[3];
+               supportB_[0] = supportB_[2];
+               supportB_[1] = supportB_[3];
+
+               numPoints_ = 2;
+               d = AB % AO % AB;
+            }
+            else {
+               //AddPointSimplex;
+               simplex_[0] = A; //aka simplex_[3] 0:A
+
+               supportA_[0] = supportA_[3];
+               supportB_[0] = supportB_[3];
+
+               numPoints_ = 1;
+               d = AO;
+            }
+         }
+         else
+         {
+            if(sameDirection(AOt, ABC%AC)) {
+               //AddEdgeSimplex(A, C);
+               simplex_[0] = C; //aka simplex_[1] 0:C
+               simplex_[1] = A; //aka simplex_[3] 1:A
+
+               supportA_[0] = supportA_[1];
+               supportA_[1] = supportA_[3];
+               supportB_[0] = supportB_[1];
+               supportB_[1] = supportB_[3];
+
+               numPoints_ = 2;
+               d = AC % AO % AC;
+            }
+            else {
+               //AddTriangleSimplex(A, B, C);
+               simplex_[0] = C; //aka simplex_[1] 0:C
+               simplex_[1] = B; //aka simplex_[2] 1:B
+               simplex_[2] = A; //aka simplex_[3] 2:A
+
+               supportA_[0] = supportA_[1];
+               supportA_[1] = supportA_[2];
+               supportA_[2] = supportA_[3];
+               supportB_[0] = supportB_[1];
+               supportB_[1] = supportB_[2];
+               supportB_[2] = supportB_[3];
+
+               numPoints_ = 3;
+               d = ABC;
+            }
+         }
+      }
+      else
+      {
+         if(sameDirection(AOt, AD%ADB)) {
+            if(sameDirection(AOt, AD)) {
+               //AddEdgeSimplex(A, D);
+               //simplex_[0] = D; //aka simplex_[0] 0:D already there
+               simplex_[1] = A; //aka simplex_[3] 1:A
+
+               supportA_[1] = supportA_[3];
+               supportB_[1] = supportB_[3];
+
+               numPoints_ = 2;
+               d = AD % AO % AD;
+            }
+            else {
+               //AddPointSimplex;
+               simplex_[0] = A; //aka simplex_[3] 0:A
+
+               supportA_[0] = supportA_[3];
+               supportB_[0] = supportB_[3];
+
+               numPoints_ = 1;
+               d = AO;
+            }
+         }
+         else {
+            //AddTriangleSimplex(A, D, B);
+            simplex_[1] = D; //aka simplex[0] 1:D
+            simplex_[0] = B; //aka simplex[2] 0:B
+            simplex_[2] = A;  //aka simplex[3] 2:A
+
+            supportA_[1] = supportA_[0];
+            supportA_[0] = supportA_[2];
+            supportA_[2] = supportA_[3];
+            supportB_[1] = supportB_[0];
+            supportB_[0] = supportB_[2];
+            supportB_[2] = supportB_[3];
+
+            numPoints_  = 3;
+
+            numPoints_ = 3;
+            d = ADB;
+         }
+      }
+   } break;
+
+   case 6:
+   {
+      // In front of ACD and ADB
+      if(sameDirection(AOt, ACD%AD)) {
+         if(sameDirection(AOt, AD%ADB)) {
+            if(sameDirection(AOt, AD)) {
+               //AddEdgeSimplex(A, D);
+               //simplex_[0] = D; //aka simplex_[0] 0:D already there
+               simplex_[1] = A; //aka simplex_[3] 1:A
+
+               supportA_[1] = supportA_[3];
+               supportB_[1] = supportB_[3];
+
+               numPoints_ = 2;
+               d = AD % AO % AD;
+            }
+            else {
+               //AddPointSimplex;
+               simplex_[0] = A; //aka simplex_[3] 0:A
+
+               supportA_[0] = supportA_[3];
+               supportB_[0] = supportB_[3];
+
+               numPoints_ = 1;
+               d = AO;
+            }
+         }
+         else
+         {
+            if(sameDirection(AOt, ADB%AB)) {
+               //AddEdgeSimplex(A, B);
+               simplex_[0] = B; //aka simplex_[2] 0:B
+               simplex_[1] = A; //aka simplex_[3] 1:A
+
+               supportA_[0] = supportA_[2];
+               supportA_[1] = supportA_[3];
+               supportB_[0] = supportB_[2];
+               supportB_[1] = supportB_[3];
+
+               numPoints_ = 2;
+               d = AB % AO % AB;
+            }
+            else {
+               //AddTriangleSimplex(A, D, B);
+               simplex_[1] = D; //aka simplex[0] 1:D
+               simplex_[0] = B; //aka simplex[2] 0:B
+               simplex_[2] = A;  //aka simplex[3] 2:A
+
+               supportA_[1] = supportA_[0];
+               supportA_[0] = supportA_[2];
+               supportA_[2] = supportA_[3];
+               supportB_[1] = supportB_[0];
+               supportB_[0] = supportB_[2];
+               supportB_[2] = supportB_[3];
+
+               numPoints_  = 3;
+
+               numPoints_ = 3;
+               d = ADB;
+            }
+         }
+      }
+      else
+      {
+         if(sameDirection(AOt, AC%ACD)) {
+            if(sameDirection(AOt, AC)) {
+               //AddEdgeSimplex(A, C);
+               simplex_[0] = C; //aka simplex_[1] 0:C
+               simplex_[1] = A; //aka simplex_[3] 1:A
+
+               supportA_[0] = supportA_[1];
+               supportA_[1] = supportA_[3];
+               supportB_[0] = supportB_[1];
+               supportB_[1] = supportB_[3];
+
+               numPoints_ = 2;
+               d = AC % AO % AC;
+            }
+            else
+            {
+               //AddPointSimplex;
+               simplex_[0] = A; //aka simplex_[3] 0:A
+
+               supportA_[0] = supportA_[3];
+               supportB_[0] = supportB_[3];
+
+               numPoints_ = 1;
+               d = AO;
+            }
+         }
+         else
+         {
+            //AddTriangleSimplex(A, C, D);
+            //simplex_[0] = D; //aka simplex_[0] 0:D already there
+            //simplex_[1] = C; //aka simplex_[1] 1:C already there
+            simplex_[2] = A; //aka simplex_[3] 2:A
+
+            supportA_[2] = supportA_[3];
+            supportB_[2] = supportB_[3];
+
+            numPoints_ = 3;
+            d = ACD;
+         }
+      }
+   } break;
+
+   case 7:
+   {
+      // In front of ABC, ACD and ADB
+      if(sameDirection(AOt, AB)) {
+         simplex_[0] = B; //aka simplex_[2] 0:B
+         simplex_[1] = A; //aka simplex_[3] 1:A
+
+         supportA_[0] = supportA_[2];
+         supportA_[1] = supportA_[3];
+         supportB_[0] = supportB_[2];
+         supportB_[1] = supportB_[3];
+
+         numPoints_ = 2;
+         d = AB % AO % AB;
+      }
+      else
+      {
+         if(sameDirection(AOt, AC)) {
+            simplex_[0] = C; //aka simplex_[1] 0:C
+            simplex_[1] = A; //aka simplex_[3] 1:A
+
+            supportA_[0] = supportA_[1];
+            supportA_[1] = supportA_[3];
+            supportB_[0] = supportB_[1];
+            supportB_[1] = supportB_[3];
+
+            numPoints_ = 2;
+            d = AC % AO % AC;
+
+         }
+         else
+         {
+            if(sameDirection(AOt, AD)) {
+               //simplex_[0] = D; //aka simplex_[1] 0:D already there
+               simplex_[1] = A; //aka simplex_[3] 1:A
+
+               supportA_[1] = supportA_[3];
+               supportB_[1] = supportB_[3];
+
+               numPoints_ = 2;
+               d = AD % AO % AD;
+            }
+            else {
+               simplex_[0] = A; //aka simplex_[3] 0:A
+
+               supportA_[0] = supportA_[3];
+               supportB_[0] = supportB_[3];
+
+               numPoints_ = 1;
+               d = AO;
+            }
+         }
+      }
+   } break;
+   default:
+   {
+      //all 8 cases 0-7 are covered
+   } break;
+   }
+
+   return false;
+}
+//*************************************************************************************************
+
+} //collision_detection
+} //mesa_pd
+} //walberla
diff --git a/src/mesa_pd/collision_detection/GJK.h b/src/mesa_pd/collision_detection/GJK.h
new file mode 100644
index 0000000000000000000000000000000000000000..96d6d5be8220dd16ef93e73a4951a5a65af67277
--- /dev/null
+++ b/src/mesa_pd/collision_detection/GJK.h
@@ -0,0 +1,110 @@
+//======================================================================================================================
+//
+//  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 GJK.h
+//! \author Tobias Scharpff
+//! \author Tobias Leemann
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/collision_detection/Support.h>
+#include <mesa_pd/data/DataTypes.h>
+
+#include <vector>
+
+#include <core/Abort.h>
+#include <core/math/Limits.h>
+#include <core/math/Vector3.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace collision_detection {
+
+//*************************************************************************************************
+/*!\brief Impelementation of the Gilbert-Johnson-Keerthi Algorithm.
+ */
+class GJK
+{
+public:
+
+   //**Constructor*********************************************************************************
+   /*! \name Constructor */
+   //@{
+   explicit GJK();
+   //@}
+   //**********************************************************************************************
+
+   //**Query functions*****************************************************************************
+   /*! \name Query functions */
+   //@{
+   real_t doGJK( const Support &geom1, const Support &geom2, Vec3& normal, Vec3& contactPoint );
+
+   bool doGJKmargin( const Support &geom1, const Support &geom2, const real_t margin = real_t(0));
+   //@}
+   //**********************************************************************************************
+
+   //**Get functions*******************************************************************************
+   /*! \name Get functions */
+   //@{
+   inline const std::vector<Vec3>& getSimplex()     const { return simplex_; }
+   inline size_t                   getSimplexSize() const { return numPoints_; }
+   inline const std::vector<Vec3>& getSupportA()    const { return supportA_; }
+   inline const std::vector<Vec3>& getSupportB()    const { return supportB_; }
+   //@}
+   //**********************************************************************************************
+
+private:
+   //**Utility functions***************************************************************************
+   /*! \name Utility functions */
+   //@{
+   bool simplex2(Vec3& d);
+   bool simplex3(Vec3& d);
+   bool simplex4(Vec3& d);
+
+   /// Checks if two vectors roughly point in the same direction
+   inline bool sameDirection   ( const Vec3& vec1, const Vec3& vec2 ) const { return vec1 * vec2 > real_t(0.0); }
+   /// Checks if the length of a vector is zero or as close to zero that it can not be distinguished form zero
+   inline bool zeroLengthVector( const Vec3& vec )                    const { return vec.sqrLength() < math::Limits<real_t>::fpuAccuracy(); }
+   real_t calcDistance    ( Vec3& normal, Vec3& contactPoint );
+
+   inline const Vec3 putSupport(const Support &geom1,
+                                const Support &geom2,
+                                const Vec3& dir,
+                                const real_t margin,
+                                std::vector<Vec3> &simplex,
+                                std::vector<Vec3> &supportA,
+                                std::vector<Vec3> &supportB,
+                                size_t index);
+   //@}
+   //**********************************************************************************************
+
+   //**Member variables****************************************************************************
+   /*! \name Member variables */
+   //@{
+   std::vector<Vec3> simplex_;   ///< Container to hold the simplex.
+   std::vector<Vec3> supportA_;  ///< Container to hold the support points generated in triangle mesh mA
+   std::vector<Vec3> supportB_;  ///< Container to hold the support points generated in triangle mesh mB
+   unsigned char     numPoints_; ///< Current number of points in the simplex.
+   Vec3              d_;         ///< The next search direction.
+   //@}
+   //**********************************************************************************************
+};
+//*************************************************************************************************
+
+} // namespace collision_detection
+} // namespace mesa_pd
+} // namespace walberla
diff --git a/src/mesa_pd/collision_detection/GeneralContactDetection.h b/src/mesa_pd/collision_detection/GeneralContactDetection.h
new file mode 100644
index 0000000000000000000000000000000000000000..bf6d3fad69ca37ecd89bc56ed296a4ef66184a2b
--- /dev/null
+++ b/src/mesa_pd/collision_detection/GeneralContactDetection.h
@@ -0,0 +1,217 @@
+//======================================================================================================================
+//
+//  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 GeneralContactDetection.h
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/collision_detection/AnalyticContactDetection.h>
+#include <mesa_pd/collision_detection/EPA.h>
+#include <mesa_pd/collision_detection/GJK.h>
+#include <mesa_pd/collision_detection/Support.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/shape/BaseShape.h>
+#include <mesa_pd/data/shape/Box.h>
+#include <mesa_pd/data/shape/CylindricalBoundary.h>
+#include <mesa_pd/data/shape/Ellipsoid.h>
+#include <mesa_pd/data/shape/HalfSpace.h>
+#include <mesa_pd/data/shape/Sphere.h>
+
+#include <core/logging/Logging.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace collision_detection {
+
+/**
+ * Collision detection functor which uses recommended collision functions.
+ *
+ * Calculates and stores contact information between two particles.
+ * If a collision was successfully detected by the operator()
+ * contactPoint, contactNormal and penetrationDepth contain the
+ * contact information. If no collision was detected the values of
+ * these variables is undefined!
+ */
+class GeneralContactDetection : public AnalyticContactDetection
+{
+public:
+   using AnalyticContactDetection::operator();
+
+   template <typename GEO1_T, typename GEO2_T, typename Accessor>
+   bool operator()( const size_t idx1,
+                    const size_t idx2,
+                    const GEO1_T& geo1,
+                    const GEO2_T& geo2,
+                    Accessor& ac);
+
+   template <typename GEO2_T, typename Accessor>
+   bool operator()(const size_t idx1,
+                   const size_t idx2,
+                   const data::HalfSpace& geo1,
+                   const GEO2_T& geo2,
+                   Accessor& ac);
+
+   template <typename GEO1_T, typename Accessor>
+   bool operator()(const size_t idx1,
+                   const size_t idx2,
+                   const GEO1_T& geo1,
+                   const data::HalfSpace& geo2,
+                   Accessor& ac);
+
+   template <typename GEO2_T, typename Accessor>
+   bool operator()(const size_t idx1,
+                   const size_t idx2,
+                   const data::CylindricalBoundary& geo1,
+                   const GEO2_T& geo2,
+                   Accessor& ac);
+
+   template <typename GEO1_T, typename Accessor>
+   bool operator()(const size_t idx1,
+                   const size_t idx2,
+                   const GEO1_T& geo1,
+                   const data::CylindricalBoundary& geo2,
+                   Accessor& ac);
+private:
+   bool collideGJKEPA(Support& geom0, Support& geom1);
+};
+
+template <typename GEO1_T, typename GEO2_T, typename Accessor>
+bool GeneralContactDetection::operator()( const size_t idx1,
+                                          const size_t idx2,
+                                          const GEO1_T& geo1,
+                                          const GEO2_T& geo2,
+                                          Accessor& ac)
+{
+   WALBERLA_ASSERT_UNEQUAL(idx1, idx2, "colliding with itself!");
+
+   //ensure collision order idx2 has to be larger than idx1
+   if (ac.getUid(idx2) < ac.getUid(idx1))
+      return operator()(idx2, idx1, geo2, geo1, ac);
+
+   getIdx1() = idx1;
+   getIdx2() = idx2;
+
+   Support e1(ac.getPosition(getIdx1()), ac.getRotation(getIdx1()), geo1);
+   Support e2(ac.getPosition(getIdx2()), ac.getRotation(getIdx2()), geo2);
+   return collideGJKEPA(e1, e2);
+}
+
+template <typename GEO2_T, typename Accessor>
+bool GeneralContactDetection::operator()(const size_t idx1,
+                                         const size_t idx2,
+                                         const data::HalfSpace& geo1,
+                                         const GEO2_T& geo2,
+                                         Accessor& ac)
+{
+   getIdx1() = idx1;
+   getIdx2() = idx2;
+
+   Support sup(ac.getPosition(idx2), ac.getRotation(idx2), geo2);
+   Vec3 support_dir = -geo1.getNormal();
+   // We now have a direction facing to the "wall".
+   // Compute support point of body b in this direction. This will be the furthest point overlapping.
+   Vec3 contactp = sup.support(support_dir);
+   real_t pdepth = contactp * geo1.getNormal() - ac.getPosition(idx1) * geo1.getNormal();
+   if(pdepth < contactThreshold_)
+   { //We have a collision
+      contactNormal_ = support_dir;
+      penetrationDepth_ = pdepth;
+      contactPoint_ = contactp + real_t(0.5) * penetrationDepth_ * contactNormal_;
+      return true;
+   } else
+   { //No collision
+      return false;
+   }
+}
+
+template <typename GEO1_T, typename Accessor>
+bool GeneralContactDetection::operator()(const size_t idx1,
+                                         const size_t idx2,
+                                         const GEO1_T& geo1,
+                                         const data::HalfSpace& geo2,
+                                         Accessor& ac)
+{
+   return operator()(idx2, idx1, geo2, geo1, ac);
+}
+
+template <typename GEO2_T, typename Accessor>
+bool GeneralContactDetection::operator()(const size_t idx1,
+                                         const size_t idx2,
+                                         const data::CylindricalBoundary& geo1,
+                                         const GEO2_T& geo2,
+                                         Accessor& ac)
+{
+   getIdx1() = idx1;
+   getIdx2() = idx2;
+
+   Support sup(ac.getPosition(idx2), ac.getRotation(idx2), geo2);
+
+   WALBERLA_CHECK_FLOAT_EQUAL(geo1.getAxis().sqrLength(), real_t(1));
+
+   auto d = ac.getPosition(idx2) - (dot((ac.getPosition(idx2) - ac.getPosition(idx1)), geo1.getAxis()) * geo1.getAxis() + ac.getPosition(idx1));
+   Vec3 farestP = sup.support(d);
+   auto d2 = farestP - (dot((farestP - ac.getPosition(idx1)), geo1.getAxis()) * geo1.getAxis() + ac.getPosition(idx1));
+   real_t dist = d2.sqrLength();
+
+   if(dist > geo1.getRadius() * geo1.getRadius())
+   { //We have a collision
+      penetrationDepth_ = geo1.getRadius() - std::sqrt(dist);
+      normalize(d2);
+      contactNormal_ = d2;
+      contactPoint_ = farestP + d2 * penetrationDepth_ * real_t(0.5);
+      return true;
+   } else
+   { //No collision
+      return false;
+   }
+}
+
+template <typename GEO1_T, typename Accessor>
+bool GeneralContactDetection::operator()(const size_t idx1,
+                                         const size_t idx2,
+                                         const GEO1_T& geo1,
+                                         const data::CylindricalBoundary& geo2,
+                                         Accessor& ac)
+{
+   return operator()(idx2, idx1, geo2, geo1, ac);
+}
+
+bool GeneralContactDetection::collideGJKEPA(Support& geom0, Support& geom1)
+{
+   real_t margin = real_t(1e-4);
+   GJK gjk;
+   if(gjk.doGJKmargin(geom0, geom1, margin))
+   {
+      EPA epa;
+      epa.useSphereOptimization(false);
+      if (epa.doEPAmargin(geom0, geom1, gjk, contactNormal_, contactPoint_, penetrationDepth_, margin))
+      {
+         return true;
+      } else
+      {
+         return false;
+      }
+   } else
+   {
+      return false;
+   }
+}
+
+} //namespace collision_detection
+} //namespace mesa_pd
+} //namespace walberla
diff --git a/src/mesa_pd/collision_detection/Support.h b/src/mesa_pd/collision_detection/Support.h
new file mode 100644
index 0000000000000000000000000000000000000000..10e64af5e770d067ca496471fa39580eb29269d2
--- /dev/null
+++ b/src/mesa_pd/collision_detection/Support.h
@@ -0,0 +1,69 @@
+//======================================================================================================================
+//
+//  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 Support.h
+//! \author Tobias Scharpff
+//! \author Tobias Leemann
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/shape/BaseShape.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace collision_detection {
+
+class Support
+{
+public:
+   /// \attention Lifetime of shape has to surpass lifetime of Support!!!
+   /// No copy is stored.
+   Support(const Vec3& pos, const Rot3& rot, const data::BaseShape& shape)
+      : pos_(pos)
+      , rot_(rot)
+      , shape_(shape)
+   {}
+
+   /**
+    * \brief Estimates the point which is farthest in direction \a d.
+    *
+    * \param d The normalized search direction in world-frame coordinates.
+    * \return The support point in world-frame coordinates in direction a\ d.
+    */
+   Vec3 support( Vec3 d ) const;
+public:
+   Vec3 pos_;
+   Rot3 rot_;
+   const data::BaseShape& shape_;
+};
+
+inline
+Vec3 Support::support( Vec3 d ) const
+{
+   auto len = d.sqrLength();
+   if (math::equal(len, real_t(0)))
+      return Vec3(0,0,0);
+
+   d = rot_.getMatrix().getTranspose() * d.getNormalized(); //vectorFromWFtoBF
+   d = shape_.support(d);
+   return pos_ + rot_.getMatrix() * d; //vectorFromBFtoWF
+}
+
+} // namespace collision_detection
+} // namespace mesa_pd
+} // namespace walberla
diff --git a/src/mesa_pd/data/ParticleStorage.h b/src/mesa_pd/data/ParticleStorage.h
index aba34e3fe6f19f5cb4034398737abb07751c10b3..b4b41decb6835558ce00002945d2f70d67d88d83 100644
--- a/src/mesa_pd/data/ParticleStorage.h
+++ b/src/mesa_pd/data/ParticleStorage.h
@@ -528,6 +528,8 @@ void swap(ParticleStorage::Particle lhs, ParticleStorage::Particle rhs)
    std::swap(lhs.getNewContactHistoryRef(), rhs.getNewContactHistoryRef());
    std::swap(lhs.getTemperatureRef(), rhs.getTemperatureRef());
    std::swap(lhs.getHeatFluxRef(), rhs.getHeatFluxRef());
+   std::swap(lhs.getDvRef(), rhs.getDvRef());
+   std::swap(lhs.getDwRef(), rhs.getDwRef());
 }
 
 inline
diff --git a/src/mesa_pd/data/ShapeStorage.h b/src/mesa_pd/data/ShapeStorage.h
index 4ab850ff7ac86d53bb05d0ff21082c2f34465466..f7e158d0e40e9bc9c6f16e678b74001b7b27925c 100644
--- a/src/mesa_pd/data/ShapeStorage.h
+++ b/src/mesa_pd/data/ShapeStorage.h
@@ -33,6 +33,7 @@
 #include <mesa_pd/data/shape/HalfSpace.h>
 #include <mesa_pd/data/shape/CylindricalBoundary.h>
 #include <mesa_pd/data/shape/Box.h>
+#include <mesa_pd/data/shape/Ellipsoid.h>
 
 #include <core/Abort.h>
 #include <core/debug/Debug.h>
@@ -62,6 +63,7 @@ struct ShapeStorage
 static_assert( Sphere::SHAPE_TYPE != HalfSpace::SHAPE_TYPE, "Shape types have to be different!" );
 static_assert( Sphere::SHAPE_TYPE != CylindricalBoundary::SHAPE_TYPE, "Shape types have to be different!" );
 static_assert( Sphere::SHAPE_TYPE != Box::SHAPE_TYPE, "Shape types have to be different!" );
+static_assert( Sphere::SHAPE_TYPE != Ellipsoid::SHAPE_TYPE, "Shape types have to be different!" );
 
 template <typename ShapeT, typename... Args>
 size_t ShapeStorage::create(Args&&... args)
@@ -81,6 +83,7 @@ ReturnType ShapeStorage::singleDispatch( ParticleStorage& ps, size_t idx, func&
       case HalfSpace::SHAPE_TYPE : return f(ps, idx, *static_cast<HalfSpace*>(shapes[ps.getShapeID(idx)].get()));
       case CylindricalBoundary::SHAPE_TYPE : return f(ps, idx, *static_cast<CylindricalBoundary*>(shapes[ps.getShapeID(idx)].get()));
       case Box::SHAPE_TYPE : return f(ps, idx, *static_cast<Box*>(shapes[ps.getShapeID(idx)].get()));
+      case Ellipsoid::SHAPE_TYPE : return f(ps, idx, *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idx)].get()));
       default : WALBERLA_ABORT("Shape type (" << shapes[ps.getShapeID(idx)]->getShapeType() << ") could not be determined!");
    }
 }
@@ -116,6 +119,11 @@ ReturnType ShapeStorage::doubleDispatch( ParticleStorage& ps, size_t idx, size_t
                                                    *static_cast<Sphere*>(shapes[ps.getShapeID(idx)].get()),
                                                    idy,
                                                    *static_cast<Box*>(shapes[ps.getShapeID(idy)].get()));
+            case Ellipsoid::SHAPE_TYPE : return f(ps,
+                                                   idx,
+                                                   *static_cast<Sphere*>(shapes[ps.getShapeID(idx)].get()),
+                                                   idy,
+                                                   *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idy)].get()));
             default : WALBERLA_ABORT("Shape type (" << shapes[ps.getShapeID(idy)]->getShapeType() << ") could not be determined!");
          }
       case HalfSpace::SHAPE_TYPE :
@@ -141,6 +149,11 @@ ReturnType ShapeStorage::doubleDispatch( ParticleStorage& ps, size_t idx, size_t
                                                    *static_cast<HalfSpace*>(shapes[ps.getShapeID(idx)].get()),
                                                    idy,
                                                    *static_cast<Box*>(shapes[ps.getShapeID(idy)].get()));
+            case Ellipsoid::SHAPE_TYPE : return f(ps,
+                                                   idx,
+                                                   *static_cast<HalfSpace*>(shapes[ps.getShapeID(idx)].get()),
+                                                   idy,
+                                                   *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idy)].get()));
             default : WALBERLA_ABORT("Shape type (" << shapes[ps.getShapeID(idy)]->getShapeType() << ") could not be determined!");
          }
       case CylindricalBoundary::SHAPE_TYPE :
@@ -166,6 +179,11 @@ ReturnType ShapeStorage::doubleDispatch( ParticleStorage& ps, size_t idx, size_t
                                                    *static_cast<CylindricalBoundary*>(shapes[ps.getShapeID(idx)].get()),
                                                    idy,
                                                    *static_cast<Box*>(shapes[ps.getShapeID(idy)].get()));
+            case Ellipsoid::SHAPE_TYPE : return f(ps,
+                                                   idx,
+                                                   *static_cast<CylindricalBoundary*>(shapes[ps.getShapeID(idx)].get()),
+                                                   idy,
+                                                   *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idy)].get()));
             default : WALBERLA_ABORT("Shape type (" << shapes[ps.getShapeID(idy)]->getShapeType() << ") could not be determined!");
          }
       case Box::SHAPE_TYPE :
@@ -191,6 +209,41 @@ ReturnType ShapeStorage::doubleDispatch( ParticleStorage& ps, size_t idx, size_t
                                                    *static_cast<Box*>(shapes[ps.getShapeID(idx)].get()),
                                                    idy,
                                                    *static_cast<Box*>(shapes[ps.getShapeID(idy)].get()));
+            case Ellipsoid::SHAPE_TYPE : return f(ps,
+                                                   idx,
+                                                   *static_cast<Box*>(shapes[ps.getShapeID(idx)].get()),
+                                                   idy,
+                                                   *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idy)].get()));
+            default : WALBERLA_ABORT("Shape type (" << shapes[ps.getShapeID(idy)]->getShapeType() << ") could not be determined!");
+         }
+      case Ellipsoid::SHAPE_TYPE :
+         switch (shapes[ps.getShapeID(idy)]->getShapeType())
+         {
+            case Sphere::SHAPE_TYPE : return f(ps,
+                                                   idx,
+                                                   *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idx)].get()),
+                                                   idy,
+                                                   *static_cast<Sphere*>(shapes[ps.getShapeID(idy)].get()));
+            case HalfSpace::SHAPE_TYPE : return f(ps,
+                                                   idx,
+                                                   *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idx)].get()),
+                                                   idy,
+                                                   *static_cast<HalfSpace*>(shapes[ps.getShapeID(idy)].get()));
+            case CylindricalBoundary::SHAPE_TYPE : return f(ps,
+                                                   idx,
+                                                   *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idx)].get()),
+                                                   idy,
+                                                   *static_cast<CylindricalBoundary*>(shapes[ps.getShapeID(idy)].get()));
+            case Box::SHAPE_TYPE : return f(ps,
+                                                   idx,
+                                                   *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idx)].get()),
+                                                   idy,
+                                                   *static_cast<Box*>(shapes[ps.getShapeID(idy)].get()));
+            case Ellipsoid::SHAPE_TYPE : return f(ps,
+                                                   idx,
+                                                   *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idx)].get()),
+                                                   idy,
+                                                   *static_cast<Ellipsoid*>(shapes[ps.getShapeID(idy)].get()));
             default : WALBERLA_ABORT("Shape type (" << shapes[ps.getShapeID(idy)]->getShapeType() << ") could not be determined!");
          }
       default : WALBERLA_ABORT("Shape type (" << shapes[ps.getShapeID(idx)]->getShapeType() << ") could not be determined!");
diff --git a/src/mesa_pd/data/shape/BaseShape.h b/src/mesa_pd/data/shape/BaseShape.h
index 24936a75b6074d15b909ee5982c155801678b58b..d00b0adeeaf2f87361380cdb89598710044b374b 100644
--- a/src/mesa_pd/data/shape/BaseShape.h
+++ b/src/mesa_pd/data/shape/BaseShape.h
@@ -51,6 +51,8 @@ public:
 
    const int& getShapeType() const {return shapeType_;}
 
+   virtual Vec3 support( const Vec3& /*d*/ ) const {WALBERLA_ABORT("Not implemented!");}
+
    static const int INVALID_SHAPE = -1; ///< Unique *invalid* shape type identifier.\ingroup mesa_pd_shape
 protected:
    real_t mass_         = real_t(0);       ///< mass
diff --git a/src/mesa_pd/data/shape/Box.h b/src/mesa_pd/data/shape/Box.h
index 7a3de3a9a761fffe61cc479dd47fa24df7239125..95fcd9d0e027c8684255f42721e3c5287f53c19c 100644
--- a/src/mesa_pd/data/shape/Box.h
+++ b/src/mesa_pd/data/shape/Box.h
@@ -41,7 +41,9 @@ public:
    real_t getVolume() const override { return edgeLength_[0] * edgeLength_[1] * edgeLength_[2]; };
    void   updateMassAndInertia(const real_t density) override;
 
-   static const int SHAPE_TYPE = 3; ///< Unique shape type identifier for boxes.\ingroup mesa_pd_shape
+   Vec3 support( const Vec3& bfD ) const override;
+
+   constexpr static int SHAPE_TYPE = 3; ///< Unique shape type identifier for boxes.\ingroup mesa_pd_shape
 
 private:
    Vec3   edgeLength_;   ///< edge length of the box
@@ -56,8 +58,19 @@ void Box::updateMassAndInertia(const real_t density)
          edgeLength_[0]*edgeLength_[0] + edgeLength_[2]*edgeLength_[2] ,
          edgeLength_[0]*edgeLength_[0] + edgeLength_[1]*edgeLength_[1] ) * (m / static_cast<real_t>( 12 ));
 
-   getInvMass()      = real_t(1.0) / m;
-   getInvInertiaBF() = I.getInverse();
+   invMass_      = real_t(1.0) / m;
+   invInertiaBF_ = I.getInverse();
+}
+
+inline
+Vec3 Box::support( const Vec3& bfD ) const
+{
+   //As it is save to say we have atleast one component of the d-vector != 0 we can use
+   Vec3 relativSupport = Vec3( math::sign(bfD[0])*edgeLength_[0]*real_t(0.5),
+                               math::sign(bfD[1])*edgeLength_[1]*real_t(0.5),
+                               math::sign(bfD[2])*edgeLength_[2]*real_t(0.5) );
+
+   return relativSupport;
 }
 
 } //namespace data
diff --git a/src/mesa_pd/data/shape/CylindricalBoundary.h b/src/mesa_pd/data/shape/CylindricalBoundary.h
index dc2f3e67cc816b4ac275e2df7befcf80ec8ac624..86380cfa14e3aa672519e46665d0f4367a1be3c8 100644
--- a/src/mesa_pd/data/shape/CylindricalBoundary.h
+++ b/src/mesa_pd/data/shape/CylindricalBoundary.h
@@ -45,7 +45,7 @@ public:
    real_t getVolume() const override { return std::numeric_limits<real_t>::infinity(); };
    void   updateMassAndInertia(const real_t density) override;
 
-   static const int SHAPE_TYPE = 2; ///< Unique shape type identifier for cylindrical boundaries.\ingroup mesa_pd_shape
+   constexpr static int SHAPE_TYPE = 2; ///< Unique shape type identifier for cylindrical boundaries.\ingroup mesa_pd_shape
 
 private:
       real_t radius_; ///< radius of the cylinder
@@ -55,8 +55,8 @@ private:
 inline
 void CylindricalBoundary::updateMassAndInertia(const real_t /*density*/)
 {
-   getInvMass()      = real_t(0.0);
-   getInvInertiaBF() = Mat3(real_t(0.0));
+   invMass_      = real_t(0.0);
+   invInertiaBF_ = Mat3(real_t(0.0));
 }
 
 } //namespace data
diff --git a/src/mesa_pd/data/shape/Ellipsoid.h b/src/mesa_pd/data/shape/Ellipsoid.h
new file mode 100644
index 0000000000000000000000000000000000000000..6ab1d673a0c6fc02271ed05100e25a1ec0cc2af6
--- /dev/null
+++ b/src/mesa_pd/data/shape/Ellipsoid.h
@@ -0,0 +1,78 @@
+//======================================================================================================================
+//
+//  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 Ellipsoid.h
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/shape/BaseShape.h>
+
+#include <core/math/Constants.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace data {
+
+class Ellipsoid : public BaseShape
+{
+public:
+   explicit Ellipsoid(const Vec3& semiAxes)
+      : BaseShape(Ellipsoid::SHAPE_TYPE)
+      , semiAxes_(semiAxes)
+   {}
+
+   const Vec3&   getSemiAxes() const   { return semiAxes_; }
+
+   real_t getVolume() const override { return real_c(4.0/3.0) * math::M_PI * semiAxes_[0] * semiAxes_[1] * semiAxes_[2]; }
+   void   updateMassAndInertia(const real_t density) override;
+
+   Vec3 support( const Vec3& d_loc ) const override;
+
+   constexpr static int SHAPE_TYPE = 4; ///< Unique shape type identifier for boxes.\ingroup mesa_pd_shape
+
+private:
+   Vec3   semiAxes_;   ///< edge length of the box
+};
+
+inline
+void Ellipsoid::updateMassAndInertia(const real_t density)
+{
+   const real_t m = getVolume() * density;
+   const Mat3   I = Mat3::makeDiagonalMatrix(
+                       real_c(0.2) * m * (semiAxes_[1] * semiAxes_[1] + semiAxes_[2] * semiAxes_[2]),
+         real_c(0.2) * m * (semiAxes_[2] * semiAxes_[2] + semiAxes_[0] * semiAxes_[0]),
+         real_c(0.2) * m * (semiAxes_[0] * semiAxes_[0] + semiAxes_[1] * semiAxes_[1]));
+
+   invMass_      = real_t(1.0) / m;
+   invInertiaBF_ = I.getInverse();
+}
+
+inline
+Vec3 Ellipsoid::support( const Vec3& d_loc ) const
+{
+   Vec3 norm_vec(d_loc[0] * semiAxes_[0], d_loc[1] * semiAxes_[1], d_loc[2] * semiAxes_[2]);
+   real_t norm_length = norm_vec.length();
+   Vec3 local_support = (real_t(1.0) / norm_length) * Vec3(semiAxes_[0] * semiAxes_[0] * d_loc[0],
+         semiAxes_[1] * semiAxes_[1] * d_loc[1],
+         semiAxes_[2] * semiAxes_[2] * d_loc[2]);
+   return local_support;
+}
+
+} //namespace data
+} //namespace mesa_pd
+} //namespace walberla
diff --git a/src/mesa_pd/data/shape/HalfSpace.h b/src/mesa_pd/data/shape/HalfSpace.h
index 9088900df7ac9302f97d2b21f20b9dbc9a0fac37..d24db7ad1b5fbd359aba714c8e485f035ad623b1 100644
--- a/src/mesa_pd/data/shape/HalfSpace.h
+++ b/src/mesa_pd/data/shape/HalfSpace.h
@@ -43,7 +43,7 @@ public:
 
    const Vec3& getNormal() const { return normal_; }
 
-   static const int SHAPE_TYPE = 0; ///< Unique shape type identifier for planes.\ingroup mesa_pd_shape
+   constexpr static int SHAPE_TYPE = 0; ///< Unique shape type identifier for planes.\ingroup mesa_pd_shape
 private:
    /**
     * Normal of the plane in reference to the global world frame.
diff --git a/src/mesa_pd/data/shape/ShapeTypes.cpp b/src/mesa_pd/data/shape/ShapeTypes.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d33273b37a30d7fcfb940bffa4e6f21b1cb4fe79
--- /dev/null
+++ b/src/mesa_pd/data/shape/ShapeTypes.cpp
@@ -0,0 +1,39 @@
+//======================================================================================================================
+//
+//  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 ShapeTypes.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/shape/Box.h>
+#include <mesa_pd/data/shape/CylindricalBoundary.h>
+#include <mesa_pd/data/shape/HalfSpace.h>
+#include <mesa_pd/data/shape/Ellipsoid.h>
+#include <mesa_pd/data/shape/Sphere.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace data {
+
+const int Box::SHAPE_TYPE                ;
+const int CylindricalBoundary::SHAPE_TYPE;
+const int HalfSpace::SHAPE_TYPE          ;
+const int Ellipsoid::SHAPE_TYPE          ;
+const int Sphere::SHAPE_TYPE             ;
+
+} //namespace data
+} //namespace mesa_pd
+} //namespace walberla
diff --git a/src/mesa_pd/data/shape/Sphere.h b/src/mesa_pd/data/shape/Sphere.h
index 79137ea18b38369439263f616bd7d0f439c417f9..3c24aff0cc87635fcc7b5a7de70c9b957d9d07c7 100644
--- a/src/mesa_pd/data/shape/Sphere.h
+++ b/src/mesa_pd/data/shape/Sphere.h
@@ -39,7 +39,9 @@ public:
 
    real_t getVolume() const override { return (real_t(4) / real_t(3)) * math::pi * getRadius() * getRadius() * getRadius(); }
 
-   static const int SHAPE_TYPE = 1; ///< Unique shape type identifier for spheres.\ingroup mesa_pd_shape
+   Vec3 support( const Vec3& d ) const override;
+
+   constexpr static int SHAPE_TYPE = 1; ///< Unique shape type identifier for spheres.\ingroup mesa_pd_shape
 
 private:
       real_t radius_; ///< radius of the sphere
@@ -58,6 +60,12 @@ void Sphere::updateMassAndInertia(const real_t density)
    invInertiaBF_ = I.getInverse();
 }
 
+inline
+Vec3 Sphere::support( const Vec3& d ) const
+{
+   return radius_ * d;
+}
+
 } //namespace data
 } //namespace mesa_pd
 } //namespace walberla
diff --git a/src/mesa_pd/kernel/DoubleCast.h b/src/mesa_pd/kernel/DoubleCast.h
index 5b4cee0e212f0cdb259be97fea78b6b1ffc2312d..46e917cc7adf7f2360763256b6c53c51b4a762ac 100644
--- a/src/mesa_pd/kernel/DoubleCast.h
+++ b/src/mesa_pd/kernel/DoubleCast.h
@@ -33,6 +33,7 @@
 #include <mesa_pd/data/shape/HalfSpace.h>
 #include <mesa_pd/data/shape/CylindricalBoundary.h>
 #include <mesa_pd/data/shape/Box.h>
+#include <mesa_pd/data/shape/Ellipsoid.h>
 
 #include <core/Abort.h>
 #include <core/debug/Debug.h>
@@ -88,6 +89,11 @@ auto DoubleCast::operator()( size_t idx, size_t idy, Accessor& ac, func& f, Args
                                                    *static_cast<Sphere*>(ac.getShape(idx)),
                                                    *static_cast<Box*>(ac.getShape(idy)),
                                                    std::forward<Args>(args)...);
+            case Ellipsoid::SHAPE_TYPE : return f(idx,
+                                                   idy,
+                                                   *static_cast<Sphere*>(ac.getShape(idx)),
+                                                   *static_cast<Ellipsoid*>(ac.getShape(idy)),
+                                                   std::forward<Args>(args)...);
             default : WALBERLA_ABORT("Shape type (" << ac.getShape(idy)->getShapeType() << ") could not be determined!");
          }
       case HalfSpace::SHAPE_TYPE :
@@ -113,6 +119,11 @@ auto DoubleCast::operator()( size_t idx, size_t idy, Accessor& ac, func& f, Args
                                                    *static_cast<HalfSpace*>(ac.getShape(idx)),
                                                    *static_cast<Box*>(ac.getShape(idy)),
                                                    std::forward<Args>(args)...);
+            case Ellipsoid::SHAPE_TYPE : return f(idx,
+                                                   idy,
+                                                   *static_cast<HalfSpace*>(ac.getShape(idx)),
+                                                   *static_cast<Ellipsoid*>(ac.getShape(idy)),
+                                                   std::forward<Args>(args)...);
             default : WALBERLA_ABORT("Shape type (" << ac.getShape(idy)->getShapeType() << ") could not be determined!");
          }
       case CylindricalBoundary::SHAPE_TYPE :
@@ -138,6 +149,11 @@ auto DoubleCast::operator()( size_t idx, size_t idy, Accessor& ac, func& f, Args
                                                    *static_cast<CylindricalBoundary*>(ac.getShape(idx)),
                                                    *static_cast<Box*>(ac.getShape(idy)),
                                                    std::forward<Args>(args)...);
+            case Ellipsoid::SHAPE_TYPE : return f(idx,
+                                                   idy,
+                                                   *static_cast<CylindricalBoundary*>(ac.getShape(idx)),
+                                                   *static_cast<Ellipsoid*>(ac.getShape(idy)),
+                                                   std::forward<Args>(args)...);
             default : WALBERLA_ABORT("Shape type (" << ac.getShape(idy)->getShapeType() << ") could not be determined!");
          }
       case Box::SHAPE_TYPE :
@@ -163,6 +179,41 @@ auto DoubleCast::operator()( size_t idx, size_t idy, Accessor& ac, func& f, Args
                                                    *static_cast<Box*>(ac.getShape(idx)),
                                                    *static_cast<Box*>(ac.getShape(idy)),
                                                    std::forward<Args>(args)...);
+            case Ellipsoid::SHAPE_TYPE : return f(idx,
+                                                   idy,
+                                                   *static_cast<Box*>(ac.getShape(idx)),
+                                                   *static_cast<Ellipsoid*>(ac.getShape(idy)),
+                                                   std::forward<Args>(args)...);
+            default : WALBERLA_ABORT("Shape type (" << ac.getShape(idy)->getShapeType() << ") could not be determined!");
+         }
+      case Ellipsoid::SHAPE_TYPE :
+         switch (ac.getShape(idy)->getShapeType())
+         {
+            case Sphere::SHAPE_TYPE : return f(idx,
+                                                   idy,
+                                                   *static_cast<Ellipsoid*>(ac.getShape(idx)),
+                                                   *static_cast<Sphere*>(ac.getShape(idy)),
+                                                   std::forward<Args>(args)...);
+            case HalfSpace::SHAPE_TYPE : return f(idx,
+                                                   idy,
+                                                   *static_cast<Ellipsoid*>(ac.getShape(idx)),
+                                                   *static_cast<HalfSpace*>(ac.getShape(idy)),
+                                                   std::forward<Args>(args)...);
+            case CylindricalBoundary::SHAPE_TYPE : return f(idx,
+                                                   idy,
+                                                   *static_cast<Ellipsoid*>(ac.getShape(idx)),
+                                                   *static_cast<CylindricalBoundary*>(ac.getShape(idy)),
+                                                   std::forward<Args>(args)...);
+            case Box::SHAPE_TYPE : return f(idx,
+                                                   idy,
+                                                   *static_cast<Ellipsoid*>(ac.getShape(idx)),
+                                                   *static_cast<Box*>(ac.getShape(idy)),
+                                                   std::forward<Args>(args)...);
+            case Ellipsoid::SHAPE_TYPE : return f(idx,
+                                                   idy,
+                                                   *static_cast<Ellipsoid*>(ac.getShape(idx)),
+                                                   *static_cast<Ellipsoid*>(ac.getShape(idy)),
+                                                   std::forward<Args>(args)...);
             default : WALBERLA_ABORT("Shape type (" << ac.getShape(idy)->getShapeType() << ") could not be determined!");
          }
       default : WALBERLA_ABORT("Shape type (" << ac.getShape(idx)->getShapeType() << ") could not be determined!");
diff --git a/src/mesa_pd/kernel/SingleCast.h b/src/mesa_pd/kernel/SingleCast.h
index 316fbb62a847fb4fce90b181c263d8e53ff90cf1..12f85b93009ddba097dd3b8f5cf05b0a01ee0354 100644
--- a/src/mesa_pd/kernel/SingleCast.h
+++ b/src/mesa_pd/kernel/SingleCast.h
@@ -33,6 +33,7 @@
 #include <mesa_pd/data/shape/HalfSpace.h>
 #include <mesa_pd/data/shape/CylindricalBoundary.h>
 #include <mesa_pd/data/shape/Box.h>
+#include <mesa_pd/data/shape/Ellipsoid.h>
 
 #include <core/Abort.h>
 #include <core/debug/Debug.h>
@@ -68,6 +69,7 @@ auto SingleCast::operator()( size_t idx, Accessor& ac, func& f, Args&&... args )
       case HalfSpace::SHAPE_TYPE : return f(idx, *static_cast<HalfSpace*>(ac.getShape(idx)), std::forward<Args>(args)...);
       case CylindricalBoundary::SHAPE_TYPE : return f(idx, *static_cast<CylindricalBoundary*>(ac.getShape(idx)), std::forward<Args>(args)...);
       case Box::SHAPE_TYPE : return f(idx, *static_cast<Box*>(ac.getShape(idx)), std::forward<Args>(args)...);
+      case Ellipsoid::SHAPE_TYPE : return f(idx, *static_cast<Ellipsoid*>(ac.getShape(idx)), std::forward<Args>(args)...);
       default : WALBERLA_ABORT("Shape type (" << ac.getShape(idx)->getShapeType() << ") could not be determined!");
    }
 }
diff --git a/src/mesa_pd/vtk/OutputSelector.h b/src/mesa_pd/vtk/OutputSelector.h
index 85a5436800e5ec06a6504d98e6235445485dceaa..177b38f8b4c49c25a422e53f3e2ccfd75797a9cd 100644
--- a/src/mesa_pd/vtk/OutputSelector.h
+++ b/src/mesa_pd/vtk/OutputSelector.h
@@ -67,6 +67,6 @@ private:
 };
 
 } // namespace vtk
-} // namespace pe
+} // namespace mesa_pd
 } // namespace walberla
 
diff --git a/src/mesa_pd/vtk/TensorGlyph.cpp b/src/mesa_pd/vtk/TensorGlyph.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4f058f6677ac05e473bc470dbffcc0b626924b89
--- /dev/null
+++ b/src/mesa_pd/vtk/TensorGlyph.cpp
@@ -0,0 +1,44 @@
+//======================================================================================================================
+//
+//  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 TensorGlyph.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/vtk/TensorGlyph.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace vtk {
+
+TensorGlyph createTensorGlyph(const Vec3& semiAxes, const Rot3& rot)
+{
+   // compute tensor glyph for visualization with ParaView (tensorGlyph)
+   const Mat3& rotMat = rot.getMatrix();
+   Vector3<real_t> directionVectorX(rotMat[0], rotMat[3], rotMat[6]);
+   Vector3<real_t> directionVectorY(rotMat[1], rotMat[4], rotMat[7]);
+   Vector3<real_t> directionVectorZ(rotMat[2], rotMat[5], rotMat[8]);
+   Mat3 axa = math::dyadicProduct(directionVectorX, directionVectorX);
+   Mat3 bxb = math::dyadicProduct(directionVectorY, directionVectorY);
+   Mat3 cxc = math::dyadicProduct(directionVectorZ, directionVectorZ);
+   Mat3 tensor = axa * semiAxes[0] + bxb * semiAxes[1] + cxc * semiAxes[2];
+   // use symmetry to only write 6 of the 9 elements: XX YY ZZ XY YZ XZ
+   return {{tensor(0,0), tensor(1,1), tensor(2,2), tensor(0,1), tensor(1,2), tensor(0,2)}};
+}
+
+} // namespace vtk
+} // namespace pe
+} // namespace walberla
diff --git a/src/mesa_pd/vtk/TensorGlyph.h b/src/mesa_pd/vtk/TensorGlyph.h
new file mode 100644
index 0000000000000000000000000000000000000000..d739f8b0070297ca702cddab2046bce93284e935
--- /dev/null
+++ b/src/mesa_pd/vtk/TensorGlyph.h
@@ -0,0 +1,37 @@
+//======================================================================================================================
+//
+//  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 TensorGlyph.h
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+
+#include <array>
+
+namespace walberla {
+namespace mesa_pd {
+namespace vtk {
+
+using TensorGlyph = std::array<real_t, 6>;
+
+TensorGlyph createTensorGlyph(const Vec3& semiAxes, const Rot3& rot);
+
+} // namespace vtk
+} // namespace pe
+} // namespace walberla
diff --git a/src/mesa_pd/vtk/WriteOutput.h b/src/mesa_pd/vtk/WriteOutput.h
index ab22c75132d8bfe4dad8628aaea8c8dc23e832e6..b6016ea796622d6b87baad35a1b01bc1d02762e5 100644
--- a/src/mesa_pd/vtk/WriteOutput.h
+++ b/src/mesa_pd/vtk/WriteOutput.h
@@ -21,6 +21,7 @@
 #pragma once
 
 #include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/vtk/TensorGlyph.h>
 
 #include <core/debug/CheckFunctions.h>
 #include <vtk/Base64Writer.h>
@@ -42,6 +43,14 @@ void writeOutput(std::ostream& os, const T& data, const uint_t component)
    walberla::vtk::toStream(os, data);
 }
 
+template <>
+inline
+void writeOutput(std::ostream& os, const TensorGlyph& data, const uint_t component)
+{
+   WALBERLA_ASSERT_LESS(component, 6);
+   walberla::vtk::toStream(os, data[component]);
+}
+
 template <>
 inline
 void writeOutput(std::ostream& os, const Vec3& data, const uint_t component)
@@ -60,6 +69,14 @@ void writeOutput(walberla::vtk::Base64Writer& b64, const T& data, const uint_t c
    b64 << data;
 }
 
+template <>
+inline
+void writeOutput(walberla::vtk::Base64Writer& b64, const TensorGlyph& data, const uint_t component)
+{
+   WALBERLA_ASSERT_LESS(component, 6);
+   b64 << data[component];
+}
+
 template <>
 inline
 void writeOutput(walberla::vtk::Base64Writer& b64, const Vec3& data, const uint_t component)
diff --git a/tests/mesa_pd/CMakeLists.txt b/tests/mesa_pd/CMakeLists.txt
index 9abfc2b20472f2decf915335dc799689b9d5ecfd..07083c0e89ac832200caa1c0e5e304910c4b3bb2 100644
--- a/tests/mesa_pd/CMakeLists.txt
+++ b/tests/mesa_pd/CMakeLists.txt
@@ -10,6 +10,27 @@ waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_AnalyticCollisionFuncti
 waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_AnalyticContactDetection FILES collision_detection/AnalyticContactDetection.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_AnalyticContactDetection )
 
+waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_BoxSupport FILES collision_detection/BoxSupport.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_BoxSupport )
+
+waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_EllipsoidSupport FILES collision_detection/EllipsoidSupport.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_EllipsoidSupport )
+
+waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_EPA FILES collision_detection/EPA.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_EPA )
+
+waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_GeneralContactDetection FILES collision_detection/GeneralContactDetection.cpp DEPENDS blockforest core pe)
+waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_GeneralContactDetection )
+
+waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_GJK FILES collision_detection/GJK.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_GJK )
+
+waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_GJKEPA FILES collision_detection/GJK_EPA.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_GJKEPA )
+
+waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_SphereSupport FILES collision_detection/SphereSupport.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_SphereSupport )
+
 waLBerla_compile_test( NAME   MESA_PD_COMMON_IntersectionRatio FILES common/IntersectionRatio.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_COMMON_IntersectionRatio )
 
@@ -42,6 +63,9 @@ waLBerla_execute_test( NAME   MESA_PD_Domain_DynamicRefinement PROCESSES 8)
 waLBerla_compile_test( NAME   MESA_PD_Domain_SerializeDeserialize FILES domain/SerializeDeserialize.cpp DEPENDS blockforest core pe)
 waLBerla_execute_test( NAME   MESA_PD_Domain_SerializeDeserialize PROCESSES 8 )
 
+waLBerla_compile_test( NAME   MESA_PD_DropTest FILES DropTest.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_DropTest )
+
 waLBerla_compile_test( NAME   MESA_PD_Kernel_ClearNextNeighborSync FILES kernel/ClearNextNeighborSync.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_Kernel_ClearNextNeighborSync PROCESSES 2 )
 
diff --git a/tests/mesa_pd/ContactDetection.cpp b/tests/mesa_pd/ContactDetection.cpp
index b60126d3d1b6043b2eb5465aab0f6d3d4454005c..3b63a9a7e07ad57d9faa97096519b4c7915735d9 100644
--- a/tests/mesa_pd/ContactDetection.cpp
+++ b/tests/mesa_pd/ContactDetection.cpp
@@ -21,6 +21,7 @@
 #include <mesa_pd/vtk/ParticleVtkOutput.h>
 
 #include <mesa_pd/collision_detection/AnalyticContactDetection.h>
+#include <mesa_pd/collision_detection/GeneralContactDetection.h>
 #include <mesa_pd/data/LinkedCells.h>
 #include <mesa_pd/data/ParticleAccessor.h>
 #include <mesa_pd/data/ParticleStorage.h>
diff --git a/tests/mesa_pd/DropTest.cpp b/tests/mesa_pd/DropTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d9d249e99e126cdfd40e223594e00d7973971750
--- /dev/null
+++ b/tests/mesa_pd/DropTest.cpp
@@ -0,0 +1,175 @@
+//======================================================================================================================
+//
+//  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   DropTest.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/collision_detection/GeneralContactDetection.h>
+
+#include <mesa_pd/data/ParticleAccessor.h>
+#include <mesa_pd/data/ParticleStorage.h>
+#include <mesa_pd/data/ShapeStorage.h>
+
+#include <mesa_pd/kernel/DoubleCast.h>
+#include <mesa_pd/kernel/ExplicitEulerWithShape.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>
+
+namespace walberla {
+namespace mesa_pd {
+
+class ParticleAccessorWithShape : public data::ParticleAccessor
+{
+public:
+   ParticleAccessorWithShape(std::shared_ptr<data::ParticleStorage>& ps, std::shared_ptr<data::ShapeStorage>& ss)
+         : ParticleAccessor(ps)
+         , ss_(ss)
+   {}
+
+   const auto& getInvMass(const size_t p_idx) const {return ss_->shapes[ps_->getShapeID(p_idx)]->getInvMass();}
+
+   const auto& getInvInertiaBF(const size_t p_idx) const {return ss_->shapes[ps_->getShapeID(p_idx)]->getInvInertiaBF();}
+
+   data::BaseShape* getShape(const size_t p_idx) const {return ss_->shapes[ps_->getShapeID(p_idx)].get();}
+private:
+   std::shared_ptr<data::ShapeStorage> ss_;
+};
+
+int main( int argc, char ** argv )
+{
+   using namespace walberla::timing;
+
+   Environment env(argc, argv);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   if (std::is_same<walberla::real_t, float>::value)
+   {
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
+   }
+
+   //init data structures
+   auto ps = std::make_shared<data::ParticleStorage>(100);
+   auto ss = std::make_shared<data::ShapeStorage>();
+   ParticleAccessorWithShape accessor(ps, ss);
+
+   auto p                       = ps->create();
+   p->getPositionRef()          = Vec3(real_t(0), real_t(0), real_t(0));
+   p->getInteractionRadiusRef() = std::numeric_limits<real_t>::infinity();
+   p->getShapeIDRef()           = ss->create<data::HalfSpace>( Vec3(real_t(0), real_t(0), real_t(1)) );
+   p->getOwnerRef()             = walberla::mpi::MPIManager::instance()->rank();
+   p->getTypeRef()              = 0;
+
+   auto sp                       = ps->create();
+   sp->getPositionRef()          = Vec3(real_t(0), real_t(0), real_t(0.01));
+   sp->getInteractionRadiusRef() = real_t(1);
+   sp->getShapeIDRef()           = ss->create<data::Sphere>( real_t(0.004) );
+   ss->shapes[1]->updateMassAndInertia(real_t(2707));
+   WALBERLA_LOG_DEVEL_VAR(ss->shapes[1]->getInvMass());
+   WALBERLA_LOG_DEVEL_VAR(ss->shapes[1]->getInvInertiaBF());
+   sp->getOwnerRef()             = walberla::mpi::MPIManager::instance()->rank();
+   sp->getTypeRef()              = 0;
+
+   auto bx                       = ps->create();
+   bx->getPositionRef()          = Vec3(real_t(1), real_t(0), real_t(0.01));
+   bx->getInteractionRadiusRef() = real_t(1);
+   bx->getShapeIDRef()           = ss->create<data::Box>( Vec3(real_t(0.008*0.8)) );
+   ss->shapes[2]->updateMassAndInertia(real_t(2707));
+   WALBERLA_LOG_DEVEL_VAR(ss->shapes[2]->getInvMass());
+   WALBERLA_LOG_DEVEL_VAR(ss->shapes[2]->getInvInertiaBF());
+   bx->getOwnerRef()             = walberla::mpi::MPIManager::instance()->rank();
+   bx->getTypeRef()              = 0;
+
+   auto el                       = ps->create();
+   el->getPositionRef()          = Vec3(real_t(2), real_t(0), real_t(0.01));
+   el->getInteractionRadiusRef() = real_t(1);
+   el->getShapeIDRef()           = ss->create<data::Ellipsoid>( Vec3(real_t(0.004)) );
+   ss->shapes[3]->updateMassAndInertia(real_t(2707));
+   WALBERLA_LOG_DEVEL_VAR(ss->shapes[3]->getInvMass());
+   WALBERLA_LOG_DEVEL_VAR(ss->shapes[3]->getInvInertiaBF());
+   el->getOwnerRef()             = walberla::mpi::MPIManager::instance()->rank();
+   el->getTypeRef()              = 0;
+
+   int64_t simulationSteps = 2000;
+   real_t dt = real_t(0.000001);
+
+   // Init kernels
+   kernel::ExplicitEulerWithShape        explicitEulerWithShape( dt );
+   kernel::SpringDashpot                 dem(1);
+   dem.setStiffness(0, 0, real_t(8.11e6));
+   dem.setDampingN (0, 0, real_t(6.86e1));
+   dem.setDampingT (0, 0, real_t(6.86e1));
+   dem.setFriction (0, 0, real_t(1.2));
+
+   collision_detection::GeneralContactDetection gcd;
+   kernel::DoubleCast                 double_cast;
+
+
+   for (int64_t i=0; i < simulationSteps; ++i)
+   {
+      ps->forEachParticle(false,
+                          kernel::SelectLocal(),
+                          accessor,
+                          [&](const size_t idx, auto& ac){ac.setForce(idx, Vec3(real_t(0), real_t(0), real_t(-9.81)) );},
+      accessor);
+
+      ps->forEachParticlePairHalf(true,
+                                  kernel::SelectAll(),
+                                  accessor,
+                                  [&](const size_t idx1, const size_t idx2, auto& ac)
+      {
+         if (double_cast(idx1, idx2, ac, gcd, ac ))
+         {
+            dem(gcd.getIdx1(), gcd.getIdx2(), ac, gcd.getContactPoint(), gcd.getContactNormal(), gcd.getPenetrationDepth());
+         }
+      },
+      accessor );
+
+      ps->forEachParticle(true,
+                          kernel::SelectLocal(),
+                          accessor,
+                          explicitEulerWithShape,
+                          accessor);
+
+//      if(i%1 == 0)
+//         WALBERLA_LOG_DEVEL(bx->getPosition()[2]);
+   }
+
+   WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(sp->getPosition()[2], real_t(0.004),     real_t(1e-3));
+   WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(bx->getPosition()[2], real_t(0.004*0.8), real_t(1e-3));
+   WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(el->getPosition()[2], real_t(0.004),     real_t(1e-3));
+
+   return EXIT_SUCCESS;
+}
+
+} // namespace mesa_pd
+} // namespace walberla
+
+int main( int argc, char* argv[] )
+{
+   return walberla::mesa_pd::main( argc, argv );
+}
diff --git a/tests/mesa_pd/collision_detection/BoxSupport.cpp b/tests/mesa_pd/collision_detection/BoxSupport.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..08ae7bcc0e5110a463735eee663a5d9347652f77
--- /dev/null
+++ b/tests/mesa_pd/collision_detection/BoxSupport.cpp
@@ -0,0 +1,82 @@
+//======================================================================================================================
+//
+//  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   BoxSupport.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/collision_detection/Support.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/shape/Box.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <cmath>
+#include <iostream>
+
+namespace walberla {
+namespace mesa_pd {
+
+void checkContour(const Vec3& pt, const Vec3& edgeLength )
+{
+   WALBERLA_CHECK_FLOAT_EQUAL(std::abs(pt[0]), edgeLength[0] * real_t(0.5), pt);
+   WALBERLA_CHECK_FLOAT_EQUAL(std::abs(pt[1]), edgeLength[1] * real_t(0.5), pt);
+   WALBERLA_CHECK_FLOAT_EQUAL(std::abs(pt[2]), edgeLength[2] * real_t(0.5), pt);
+}
+
+void check( )
+{
+   using namespace walberla::mesa_pd::collision_detection;
+
+   Vec3 pos        = Vec3(real_t(1),real_t(2),real_t(3));
+   Vec3 edgeLength = Vec3(real_t(2),real_t(3),real_t(1));
+
+   auto bx = data::Box(edgeLength);
+   Support b0(pos, Rot3(), bx);
+   WALBERLA_CHECK_FLOAT_EQUAL(b0.support(Vec3(real_t(+1),real_t(+1),real_t(+1))),  Vec3(real_t(2),real_t(3.5),real_t(3.5)));
+   WALBERLA_CHECK_FLOAT_EQUAL(b0.support(Vec3(real_t(-1),real_t(+1),real_t(+1))),  Vec3(real_t(0),real_t(3.5),real_t(3.5)));
+   WALBERLA_CHECK_FLOAT_EQUAL(b0.support(Vec3(real_t(+1),real_t(-1),real_t(+1))),  Vec3(real_t(2),real_t(0.5),real_t(3.5)));
+   WALBERLA_CHECK_FLOAT_EQUAL(b0.support(Vec3(real_t(-1),real_t(-1),real_t(+1))),  Vec3(real_t(0),real_t(0.5),real_t(3.5)));
+   WALBERLA_CHECK_FLOAT_EQUAL(b0.support(Vec3(real_t(+1),real_t(+1),real_t(-1))),  Vec3(real_t(2),real_t(3.5),real_t(2.5)));
+   WALBERLA_CHECK_FLOAT_EQUAL(b0.support(Vec3(real_t(-1),real_t(+1),real_t(-1))),  Vec3(real_t(0),real_t(3.5),real_t(2.5)));
+   WALBERLA_CHECK_FLOAT_EQUAL(b0.support(Vec3(real_t(+1),real_t(-1),real_t(-1))),  Vec3(real_t(2),real_t(0.5),real_t(2.5)));
+   WALBERLA_CHECK_FLOAT_EQUAL(b0.support(Vec3(real_t(-1),real_t(-1),real_t(-1))),  Vec3(real_t(0),real_t(0.5),real_t(2.5)));
+
+   checkContour(b0.support(Vec3(real_t(-1),real_t(-2),real_t(-3)).getNormalized()) - pos, edgeLength );
+   checkContour(b0.support(Vec3(real_t(-2),real_t(-3),real_t(-1)).getNormalized()) - pos, edgeLength );
+   checkContour(b0.support(Vec3(real_t(-3),real_t(-1),real_t(-2)).getNormalized()) - pos, edgeLength );
+
+   Rot3 rot = Rot3(Vec3(1,3,2).getNormalized(), real_t(1.56));
+   Support b1(pos, rot, bx);
+   checkContour(rot.getMatrix().getTranspose() * (b1.support(Vec3(real_t(-1),real_t(-2),real_t(-3)).getNormalized()) - pos), edgeLength );
+   checkContour(rot.getMatrix().getTranspose() * (b1.support(Vec3(real_t(-2),real_t(-3),real_t(-1)).getNormalized()) - pos), edgeLength );
+   checkContour(rot.getMatrix().getTranspose() * (b1.support(Vec3(real_t(-3),real_t(-1),real_t(-2)).getNormalized()) - pos), edgeLength );
+}
+
+} //namespace mesa_pd
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   walberla::Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   walberla::mesa_pd::check();
+
+   return EXIT_SUCCESS;
+}
diff --git a/tests/mesa_pd/collision_detection/EPA.cpp b/tests/mesa_pd/collision_detection/EPA.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fa171a209012b7c496d58b4217ae84477126a805
--- /dev/null
+++ b/tests/mesa_pd/collision_detection/EPA.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   EPA.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/collision_detection/AnalyticCollisionFunctions.h>
+#include <mesa_pd/collision_detection/EPA.h>
+#include <mesa_pd/collision_detection/GJK.h>
+#include <mesa_pd/collision_detection/Support.h>
+#include <mesa_pd/data/shape/Sphere.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <cmath>
+#include <iostream>
+#include <memory>
+
+namespace walberla {
+namespace mesa_pd {
+
+void check( )
+{
+   using namespace walberla::mesa_pd::collision_detection;
+
+   auto pos0    = Vec3(real_t(1),real_t(2),real_t(3));
+   auto radius0 = real_t(0.6);
+   auto pos1    = Vec3(real_t(2),real_t(2),real_t(3));
+   auto radius1 = real_t(0.6);
+
+   auto sp0 = data::Sphere(real_t(0.6));
+   auto geom0 = Support(pos0, Rot3(), sp0);
+   auto geom1 = Support(pos1, Rot3(), sp0);
+
+   Vec3   epa_normal;
+   Vec3   epa_contactPoint;
+   real_t epa_penetrationDepth;
+
+   real_t margin = real_t(1e-4);
+   GJK gjk;
+   WALBERLA_CHECK(gjk.doGJKmargin(geom0, geom1, margin));
+   EPA epa;
+   epa.useSphereOptimization(true);
+   WALBERLA_CHECK(epa.doEPAmargin(geom0, geom1, gjk, epa_normal, epa_contactPoint, epa_penetrationDepth, margin));
+
+   Vec3   analytic_normal;
+   Vec3   analytic_contactPoint;
+   real_t analytic_penetrationDepth;
+   WALBERLA_CHECK(analytic::detectSphereSphereCollision(pos0,
+                                                        radius0,
+                                                        pos1,
+                                                        radius1,
+                                                        analytic_contactPoint,
+                                                        analytic_normal,
+                                                        analytic_penetrationDepth,
+                                                        margin));
+
+   WALBERLA_CHECK_FLOAT_EQUAL(epa_contactPoint, analytic_contactPoint);
+   WALBERLA_CHECK_FLOAT_EQUAL(epa_normal, analytic_normal);
+   WALBERLA_CHECK_FLOAT_EQUAL(epa_penetrationDepth, analytic_penetrationDepth);
+}
+
+} //namespace mesa_pd
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   walberla::Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   walberla::mesa_pd::check();
+
+   return EXIT_SUCCESS;
+}
diff --git a/tests/mesa_pd/collision_detection/EllipsoidSupport.cpp b/tests/mesa_pd/collision_detection/EllipsoidSupport.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..820d34005c376b98c6e9b3c17eb8b1980223c12c
--- /dev/null
+++ b/tests/mesa_pd/collision_detection/EllipsoidSupport.cpp
@@ -0,0 +1,81 @@
+//======================================================================================================================
+//
+//  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   EllipsoidSupport.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/collision_detection/Support.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/shape/Ellipsoid.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <cmath>
+#include <iostream>
+
+namespace walberla {
+namespace mesa_pd {
+
+inline
+real_t contour(const Vec3& point, const Vec3& semiAxes)
+{
+   return (point[0] * point[0]) / (semiAxes[0] * semiAxes[0]) +
+          (point[1] * point[1]) / (semiAxes[1] * semiAxes[1]) +
+          (point[2] * point[2]) / (semiAxes[2] * semiAxes[2]);
+}
+
+void check( )
+{
+   using namespace walberla::mesa_pd::collision_detection;
+
+   Vec3 pos      = Vec3(real_t(1),real_t(2),real_t(3));
+   Vec3 semiAxes = Vec3(real_t(2),real_t(3),real_t(1));
+
+   auto el = data::Ellipsoid(semiAxes);
+   Support e0(pos, Rot3(), el);
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(1),real_t(0),real_t(0))), Vec3(real_t(3),real_t(2),real_t(3)));
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(0),real_t(1),real_t(0))), Vec3(real_t(1),real_t(5),real_t(3)));
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(0),real_t(0),real_t(1))), Vec3(real_t(1),real_t(2),real_t(4)));
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(-1),real_t(0),real_t(0))), Vec3(real_t(-1),real_t(2),real_t(3)));
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(0),real_t(-1),real_t(0))), Vec3(real_t(1),real_t(-1),real_t(3)));
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(0),real_t(0),real_t(-1))), Vec3(real_t(1),real_t(2),real_t(2)));
+
+   WALBERLA_CHECK_FLOAT_EQUAL( contour(e0.support(Vec3(real_t(-1),real_t(-2),real_t(-3)).getNormalized()) - pos, semiAxes), real_t(1) );
+   WALBERLA_CHECK_FLOAT_EQUAL( contour(e0.support(Vec3(real_t(-2),real_t(-3),real_t(-1)).getNormalized()) - pos, semiAxes), real_t(1) );
+   WALBERLA_CHECK_FLOAT_EQUAL( contour(e0.support(Vec3(real_t(-3),real_t(-1),real_t(-2)).getNormalized()) - pos, semiAxes), real_t(1) );
+
+   Rot3 rot = Rot3(Vec3(1,3,2).getNormalized(), real_t(1.56));
+   Support e1(pos, rot, el);
+   WALBERLA_CHECK_FLOAT_EQUAL( contour( rot.getMatrix().getTranspose() * (e1.support(Vec3(real_t(-1),real_t(-2),real_t(-3)).getNormalized()) - pos), semiAxes), real_t(1) );
+   WALBERLA_CHECK_FLOAT_EQUAL( contour( rot.getMatrix().getTranspose() * (e1.support(Vec3(real_t(-2),real_t(-3),real_t(-1)).getNormalized()) - pos), semiAxes), real_t(1) );
+   WALBERLA_CHECK_FLOAT_EQUAL( contour( rot.getMatrix().getTranspose() * (e1.support(Vec3(real_t(-3),real_t(-1),real_t(-2)).getNormalized()) - pos), semiAxes), real_t(1) );
+}
+
+} //namespace mesa_pd
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   walberla::Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   walberla::mesa_pd::check();
+
+   return EXIT_SUCCESS;
+}
diff --git a/tests/mesa_pd/collision_detection/GJK.cpp b/tests/mesa_pd/collision_detection/GJK.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..43f044c5de663f11d9221662409a503c059f7c98
--- /dev/null
+++ b/tests/mesa_pd/collision_detection/GJK.cpp
@@ -0,0 +1,61 @@
+//======================================================================================================================
+//
+//  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   GJK.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/collision_detection/GJK.h>
+#include <mesa_pd/collision_detection/Support.h>
+#include <mesa_pd/data/shape/Sphere.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <cmath>
+#include <iostream>
+#include <memory>
+
+namespace walberla {
+namespace mesa_pd {
+
+void check( )
+{
+   using namespace walberla::mesa_pd::collision_detection;
+
+   auto sp0 = data::Sphere(real_t(0.6));
+   auto sp1 = data::Sphere(real_t(0.4));
+
+   GJK gjk;
+   WALBERLA_CHECK( gjk.doGJKmargin( Support(Vec3(real_t(1),real_t(2),real_t(3)), Rot3(), sp0),
+                                    Support(Vec3(real_t(2),real_t(2),real_t(3)), Rot3(), sp0) ));
+   WALBERLA_CHECK( !gjk.doGJKmargin( Support(Vec3(real_t(1),real_t(2),real_t(3)), Rot3(), sp1),
+                                     Support(Vec3(real_t(2),real_t(2),real_t(3)), Rot3(), sp1) ));
+}
+
+} //namespace mesa_pd
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   walberla::Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   walberla::mesa_pd::check();
+
+   return EXIT_SUCCESS;
+}
diff --git a/tests/mesa_pd/collision_detection/GJK_EPA.cpp b/tests/mesa_pd/collision_detection/GJK_EPA.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c9eb7921d03c5e477a04a0a7e34b4f5736bff1de
--- /dev/null
+++ b/tests/mesa_pd/collision_detection/GJK_EPA.cpp
@@ -0,0 +1,365 @@
+//======================================================================================================================
+//
+//  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 GJK_EPA.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/collision_detection/AnalyticCollisionFunctions.h>
+#include <mesa_pd/collision_detection/EPA.h>
+#include <mesa_pd/collision_detection/GJK.h>
+#include <mesa_pd/collision_detection/Support.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/shape/Box.h>
+#include <mesa_pd/data/shape/Ellipsoid.h>
+#include <mesa_pd/data/shape/Sphere.h>
+
+#include "core/debug/TestSubsystem.h"
+#include "core/DataTypes.h"
+#include "core/math/Constants.h"
+#include "core/math/Random.h"
+#include "core/math/Vector2.h"
+
+namespace walberla {
+namespace mesa_pd {
+
+using namespace walberla::mesa_pd::collision_detection;
+using namespace walberla::mesa_pd::collision_detection::analytic;
+
+bool gjkEPAcollideHybrid(Support &geom1, Support &geom2, Vec3& normal, Vec3& contactPoint, real_t& penetrationDepth)
+{
+   // For more information on hybrid GJK/EPA see page 166 in "Collision Detecton in Interactive 3D
+   // Environments" by Gino van den Bergen.
+
+   //1. Run GJK with considerably enlarged objects.
+   real_t margin = real_t(1e-6);
+   GJK gjk;
+   if(gjk.doGJKmargin(geom1, geom2, margin)){
+      //2. If collision is possible perform EPA.
+      //std::cerr << "Peforming EPA.";
+      EPA epa;
+      epa.useSphereOptimization( true );
+      return epa.doEPAmargin(geom1, geom2, gjk, normal, contactPoint, penetrationDepth, margin);
+   }else{
+      return false;
+   }
+}
+
+//Define Test values for different precision levels
+#ifdef WALBERLA_DOUBLE_ACCURACY
+static const int distancecount = 6;
+static const real_t depth[distancecount] = {real_t(-1e-5), real_t(1e-5), real_t(1e-4), real_t(1e-2), real_t(0.1), real_t(1.0)};
+static const real_t test_accuracy = real_t(1e-3);
+#else
+static const int distancecount = 3;
+static const real_t depth[distancecount] = {real_t(1e-2), real_t(0.1), real_t(1.0)};
+static const real_t test_accuracy = real_t(1e-2); //Single Precision is v. bad!
+#endif
+
+
+/** Compares Computed Contact c1 to analytical Contact c2,
+ * and tests for equivalence.
+ * The computed position must only be in the same plane, if planeNormal has not length 0. */
+void checkContact(const Vec3& contactPoint1,
+                  const Vec3& normal1,
+                  const real_t penetrationDepth1,
+                  const Vec3& contactPoint2,
+                  const Vec3& normal2,
+                  const real_t penetrationDepth2,
+                  const Vec3& planeNormal,
+                  const real_t accuracy = test_accuracy )
+{
+   WALBERLA_CHECK_LESS( fabs((normal1 - normal2).sqrLength()), accuracy*accuracy );
+   WALBERLA_CHECK_LESS( fabs(penetrationDepth1- penetrationDepth2), accuracy );
+   
+   //Unfortunately position accuracy is one-two orders of magnitude lower...
+   if(floatIsEqual(planeNormal.sqrLength(), real_t(0.0))){
+      WALBERLA_CHECK_LESS( fabs((contactPoint1- contactPoint2).sqrLength()), real_t(1e4)*accuracy*accuracy  );
+   }else{
+      //check for containment in plane only.
+      WALBERLA_CHECK_LESS( fabs(contactPoint1*planeNormal-contactPoint2*planeNormal), real_t(1e2)*accuracy );
+   }
+   
+}
+
+/** \brief Executes a test setup for collision data collection.
+    * \param rb1 first rigid body
+    * \param rb2 second rigid body
+    * \param dir1 direction of rb2 moving towards rb1 (unit vector)
+    * \param penetration_factor Increment of the penetration if rb2 is moved by dir1 (=1.0 in most cases)
+    * \param real_axis Analytical collision normal (unit vector)
+    * \param witnesspoint Analytical touching point of rb1 and rb2
+    * \param witnessmove Movement of the touching point, if rb2 is moved by dir1
+    * \param planeNormal The normal of the touching plane (if the touching point is unique,
+    * a Vector of length 0.0 shall be passed)
+    * \param accuracy Acceptance threshold
+    * Before the test, rb1 and rb2 shall be in touching contact.
+    * This function checks the collision data returned for different penetration depths and argument orders.
+    */
+void runCollisionDataTest(Support &rb1, Support &rb2, const Vec3& dir1, const real_t penetration_factor,
+                          const Vec3& real_axis, const Vec3& witnesspoint, const Vec3& witnessmove, const Vec3& planeNormal, const real_t accuracy = test_accuracy){
+
+   Vec3 org_pos = rb2.pos_; //Safe position
+
+   Vec3 normal1, normal2;
+   Vec3 pos1, pos2;
+   real_t comp_pen_depth1, comp_pen_depth2;
+
+   for(int j = 0; j < distancecount; j++){
+      //move rb1.
+      rb2.pos_ = (org_pos + depth[j]*dir1);
+//      WALBERLA_LOG_INFO("Using depth: "+ std::to_string(depth[j]));
+      //Compute collision between rb1 and rb2 and vice versa
+      bool result1 = gjkEPAcollideHybrid(rb1, rb2, normal1, pos1, comp_pen_depth1);
+//      WALBERLA_LOG_DEVEL( normal1 << " " << pos1 << " " <<  comp_pen_depth1);
+      bool result2 = gjkEPAcollideHybrid(rb2, rb1, normal2, pos2, comp_pen_depth2);
+//      WALBERLA_LOG_DEVEL( normal2 << " " << pos2 << " " <<  comp_pen_depth2);
+      if(depth[j] > real_t(0.0)){
+         WALBERLA_CHECK(result1);
+         WALBERLA_CHECK(result2);
+         //Check contact information
+         checkContact( pos1, normal1, comp_pen_depth1,
+                       witnesspoint + depth[j] * witnessmove, real_axis, -depth[j] * penetration_factor, planeNormal, accuracy );
+         checkContact( pos2, normal2, comp_pen_depth2,
+                       witnesspoint + depth[j] * witnessmove, real_t(-1.0)*real_axis, -depth[j] * penetration_factor, planeNormal, accuracy );
+      }
+      if(depth[j] < real_t(0.0)){
+         WALBERLA_CHECK(!result1);
+         WALBERLA_CHECK(!result2);
+      }
+   }
+}
+
+/** Test the GJK-EPA implementation on a variety of configuations 
+ * and penetation depths */
+void MainTest()
+{
+   using namespace walberla::mesa_pd::data;
+   // Original SPHERE <-> SPHERE
+   auto sp = data::Sphere(real_t(1));
+   auto sp1 = Support( Vec3(0,0,0), Rot3(), sp);
+   auto sp2 = Support( Vec3(1.5,0,0), Rot3(), sp);
+   auto sp3 = Support( Vec3(3.0,0,0), Rot3(), sp);
+
+   Vec3     normal;
+   Vec3     contactPoint;
+   real_t   penetrationDepth;
+
+
+   WALBERLA_LOG_INFO("Original: SPHERE <-> SPHERE");
+   WALBERLA_CHECK( !gjkEPAcollideHybrid(sp1, sp3, normal, contactPoint, penetrationDepth) );
+   WALBERLA_CHECK(  gjkEPAcollideHybrid(sp1, sp2, normal, contactPoint, penetrationDepth) );
+   checkContact( contactPoint,  normal, penetrationDepth,
+                 Vec3(real_t(0.75), 0, 0), Vec3(real_t(-1.0), 0, 0), real_t(-0.5), Vec3(0,0,0) );
+
+   //Testcase 01 Box Sphere
+   WALBERLA_LOG_INFO("Test 01: BOX <-> SPHERE");
+   real_t sqr3_inv = real_t(1.0)/std::sqrt(real_t(3.0));
+   real_t coordinate= real_t(5.0)* sqr3_inv + real_t(5.0); // 5*(1+ (1/sqrt(3)))
+   Box bx_101010(Vec3(10, 10, 10));
+   Support box1_1(Vec3(0, 0, 0), Rot3(), bx_101010);
+   Sphere s_5(real_t(5));
+   Support sphere1_2(Vec3(coordinate, coordinate, coordinate), Rot3(), s_5);
+   Vec3 wp1(real_t(5.0), real_t(5.0), real_t(5.0));
+   Vec3 wpm1(sqr3_inv*real_t(-0.5), sqr3_inv*real_t(-0.5), sqr3_inv*real_t(-0.5));
+   Vec3 axis1(-sqr3_inv, -sqr3_inv, -sqr3_inv);
+   runCollisionDataTest(box1_1, sphere1_2, axis1, real_t(1.0), axis1, wp1, wpm1, Vec3(0,0,0));
+
+   //Testcase 02 Box LongBox (touching plane)
+   //Reuse box1_1
+   WALBERLA_LOG_INFO("Test 02: BOX <-> LONG BOX");
+   Box bx_3011(Vec3(real_t(30.0),1,1));
+   Support box2_1(Vec3(real_t(20.0),0,0), Rot3(), bx_3011);
+   Vec3 wp2(5, 0, 0);
+   Vec3 wpm2(real_t(-0.5),0,0);
+   Vec3 axis2(-1,0,0);
+   runCollisionDataTest(box1_1, box2_1, axis2, real_t(1.0), axis2, wp2, wpm2, axis2);
+
+   //Testcase 03 Sphere Sphere
+   WALBERLA_LOG_INFO("Test 03: SPHERE <-> SPHERE");
+   Support sphere3_1(Vec3(0,0,0), Rot3(),    s_5);
+   Support sphere3_2(Vec3(real_t(10.0),0,0), Rot3(), s_5);
+   Vec3 wp3(5, 0, 0);
+   Vec3 wpm3(real_t(-0.5),0,0);
+   Vec3 axis3(-1,0,0);
+   runCollisionDataTest(sphere3_1, sphere3_2, axis3, real_t(1.0), axis3, wp3, wpm3, Vec3(0,0,0));
+
+   //Testcase 04 Cube with turned Cube
+   WALBERLA_LOG_INFO("Test 04: CUBE <-> TURNED CUBE");
+   //compute rotation.
+   real_t angle = walberla::math::M_PI/real_t(4.0);
+   Vec3 zaxis(0, 0, 1);
+   Quat q4(zaxis, angle);
+
+   //create turned box
+   real_t sqr2 = std::sqrt(real_t(2.0));
+   Support box4_1( Vec3(real_t(5.0)*(real_t(1.0)+sqr2), real_t(-5.0), 0), Rot3(q4), bx_101010);
+   Support box4_2( Vec3(0, 0, 0),                       Rot3(), bx_101010);
+   Vec3 wp4(5, -5, 0);
+   Vec3 wpm4(real_t(-0.25),real_t(+0.25),0);
+   Vec3 collision_axis4(-sqr2/real_t(2.0),+sqr2/real_t(2.0),0);
+   Vec3 axis4(-1, 0, 0);
+
+   runCollisionDataTest(box4_2, box4_1, axis4, sqr2/real_t(2.0), collision_axis4, wp4, wpm4, Vec3(0,real_t(1.0),0));
+
+   //Testcase 05 Cube and Long Box non-centric (touching plane)
+   WALBERLA_LOG_INFO("Test 05: CUBE <-> LONG BOX (NON_CENTRIC)");
+   Support box5_1(Vec3(0, 0, 0),     Rot3(), bx_101010);
+   Support box5_2(Vec3(real_t(15.0),real_t(5.5), 0), Rot3(), bx_3011);
+   Vec3 wp5(real_t(3.75), 5, 0);
+   Vec3 wpm5(0, real_t(-0.5), 0);
+   Vec3 axis5(0, -1, 0);
+   runCollisionDataTest(box5_1, box5_2, axis5, real_t(1.0), axis5, wp5, wpm5, axis5);  //check only for containment in plane.
+
+
+   //Testcase 06:
+   WALBERLA_LOG_INFO("Test 06: CUBE <-> TURNED CUBE 2");
+   //compute rotation.
+
+   real_t sqr6_2 = std::sqrt(real_t(2.0));
+   real_t sqr6_3 = std::sqrt(real_t(3.0));
+   real_t angle6 = std::acos(real_t(1.0)/sqr6_3); //acos(1/sqrt(3))
+   Vec3 rot_axis6(0, real_t(1.0)/sqr6_2, -real_t(1.0)/sqr6_2);
+   Quat q6(rot_axis6, angle6);
+
+   //create turned box with pos = (5*(1+sqrt(3)), 0, 0)
+   Support box6_1(Vec3(real_t(5.0)*(real_t(1.0)+sqr6_3), 0, 0), Rot3(q6), bx_101010);
+   Support box6_2(Vec3(0, 0, 0), Rot3(), bx_101010);
+   Vec3 wp6(5, 0, 0);
+   Vec3 wpm6(real_t(-0.5), 0, 0);
+   Vec3 axis6(-1, 0, 0);
+   runCollisionDataTest(box6_2, box6_1, axis6, real_t(1.0), axis6, wp6, wpm6, Vec3(0,0,0));
+
+   //Testcase 07:
+   // BOX <-> SPHERE
+   WALBERLA_LOG_INFO("Test 07: BOX <-> SPHERE");
+   Support sphere7_1(Vec3(0,0,0), Rot3(), s_5);
+   Box bx_555( Vec3(5, 5, 5));
+   Support box7_2(Vec3(0, 0,real_t(7.5)), Rot3(), bx_555);
+   Vec3 wpm7(0, 0, real_t(-0.5));
+   Vec3 wp7(0, 0, real_t(5.0));
+   Vec3 axis7(0, 0,  real_t(-1.0));
+   runCollisionDataTest(sphere7_1, box7_2, axis7, real_t(1.0), axis7, wp7, wpm7, Vec3(0,0,0));
+
+   //Testcase 09:
+   // ELLIPSOID <-> ELLIPSOID
+   WALBERLA_LOG_INFO("Test 09: ELLIPSOID <-> ELLIPSOID");
+   Ellipsoid ell9_1_(Vec3(10,5,5));
+   Support ell9_1(Vec3(0,0,0), Rot3(), ell9_1_);
+   Ellipsoid ell9_2_(Vec3(5,10,5));
+   Support ell9_2(Vec3(15,0,0), Rot3(), ell9_2_);
+   Vec3 wpm9(real_t(-0.5), 0, 0);
+   Vec3 wp9(real_t(10), 0, 0);
+   Vec3 axis9(real_t(-1.0), 0, 0);
+   runCollisionDataTest(ell9_1, ell9_2, axis9, real_t(1.0), axis9, wp9, wpm9, Vec3(0,0,0));
+
+}
+
+void FuzzyEllipsoidEllipsoid(uint_t iterations)
+{
+   using namespace walberla::mesa_pd::data;
+
+   Vec3     normal;
+   Vec3     contactPoint;
+   real_t   penetrationDepth;
+
+   Ellipsoid el(Vec3(2,2,2));
+   Support e1(Vec3(0,0,0), Rot3(), el);
+   Support e2(Vec3(real_t(3.999),0,0), Rot3(), el);
+
+   for (uint_t i = 0; i < iterations; ++i)
+   {
+      e1.rot_.rotate( Vec3(math::realRandom(), math::realRandom(), math::realRandom()) );
+      e2.rot_.rotate( Vec3(math::realRandom(), math::realRandom(), math::realRandom()) );
+      WALBERLA_CHECK(gjkEPAcollideHybrid(e1, e2, normal, contactPoint, penetrationDepth));
+      WALBERLA_CHECK_FLOAT_EQUAL(normal, Vec3(real_t(-1),0,0));
+      WALBERLA_CHECK_FLOAT_EQUAL(contactPoint, Vec3(real_t(3.999) * real_t(0.5), 0, 0));
+      WALBERLA_CHECK_FLOAT_EQUAL(penetrationDepth, real_t(3.999) - real_t(4));
+   }
+}
+
+void FuzzyEllipsoidBox(uint_t iterations)
+{
+   using namespace walberla::mesa_pd::data;
+
+   Vec3     normal;
+   Vec3     contactPoint;
+   real_t   penetrationDepth;
+
+   Vec3     gjk_normal;
+   Vec3     gjk_contactPoint;
+   real_t   gjk_penetrationDepth;
+
+   Box       bx(Vec3(2,2,2));
+   Ellipsoid el(Vec3(2,2,2));
+   Support p1(Vec3(0,0,0), Rot3(), el);
+   Support p2(Vec3(real_t(3.999),0,0), Rot3(), bx);
+
+   for (uint_t i = 0; i < iterations; ++i)
+   {
+      //if(i%100==0) WALBERLA_LOG_INFO(i);
+      p1.rot_.rotate( Vec3(math::realRandom(), math::realRandom(), math::realRandom()) );
+      p2.rot_.rotate( Vec3(math::realRandom(), math::realRandom(), math::realRandom()) );
+      p2.pos_ = Vec3(4,0,0);
+      bool bCollision = false;
+      while (!bCollision)
+      {
+         p2.pos_ -= Vec3(real_t(0.1),0,0);
+         if (p2.pos_[0]<real_t(0)) WALBERLA_ABORT("ERROR");
+         bCollision = detectSphereBoxCollision(p1.pos_,
+                                               el.getSemiAxes()[0],
+                                               p2.pos_,
+                                               bx.getEdgeLength(),
+                                               p2.rot_,
+                                               contactPoint,
+                                               normal,
+                                               penetrationDepth,
+                                               real_t(1e-4));
+      }
+
+      WALBERLA_CHECK(gjkEPAcollideHybrid(p1, p2, gjk_normal, gjk_contactPoint, gjk_penetrationDepth), penetrationDepth);
+      //WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(gjk_normal, normal, real_t(1e-2));
+      //WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(gjk_contactPoint, contactPoint, real_t(1e-2));
+      //WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(gjk_penetrationDepth, penetrationDepth, real_t(1e-2));
+   }
+}
+
+int main( int argc, char** argv )
+{
+   walberla::debug::enterTestMode();
+
+   walberla::MPIManager::instance()->initializeMPI( &argc, &argv );
+
+   if (std::is_same<walberla::real_t, float>::value)
+   {
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
+   }
+
+   MainTest();
+   FuzzyEllipsoidEllipsoid(1000);
+   FuzzyEllipsoidBox(1000);
+
+   return EXIT_SUCCESS;
+}
+
+} // namespace mesa_pd
+} // namespace walberla
+
+int main( int argc, char* argv[] )
+{
+   return walberla::mesa_pd::main( argc, argv );
+}
diff --git a/tests/mesa_pd/collision_detection/GeneralContactDetection.cpp b/tests/mesa_pd/collision_detection/GeneralContactDetection.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9f3be3feb720aced306b7042bbd9ca47f193afcb
--- /dev/null
+++ b/tests/mesa_pd/collision_detection/GeneralContactDetection.cpp
@@ -0,0 +1,105 @@
+//======================================================================================================================
+//
+//  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   GeneralContactDetection.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/collision_detection/GeneralContactDetection.h>
+#include <mesa_pd/data/ParticleAccessor.h>
+#include <mesa_pd/data/ParticleStorage.h>
+#include <mesa_pd/data/ShapeStorage.h>
+#include <mesa_pd/kernel/DoubleCast.h>
+
+#include <core/Abort.h>
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+#include <core/waLBerlaBuildInfo.h>
+
+#include <memory>
+
+namespace walberla {
+namespace mesa_pd {
+
+class ParticleAccessorWithShape : public data::ParticleAccessor
+{
+public:
+   ParticleAccessorWithShape(std::shared_ptr<data::ParticleStorage>& ps, std::shared_ptr<data::ShapeStorage>& ss)
+      : ParticleAccessor(ps)
+      , ss_(ss)
+   {}
+
+   data::BaseShape* getShape(const size_t p_idx) const {return ss_->shapes[ps_->getShapeIDRef(p_idx)].get();}
+private:
+   std::shared_ptr<data::ShapeStorage> ss_;
+};
+
+void generalContactDetection()
+{
+   //init data structures
+   auto ps = std::make_shared<data::ParticleStorage>(100);
+   auto ss = std::make_shared<data::ShapeStorage>();
+   ParticleAccessorWithShape accessor(ps, ss);
+
+   auto e0 = ps->create();
+   e0->setPosition(Vec3(real_t(0),real_t(0),real_t(0)));
+   e0->setShapeID(ss->create<data::Ellipsoid>(Vec3(real_t(1),real_t(2),real_t(3))));
+
+   auto e1 = ps->create();
+   e1->setPosition(Vec3(real_t(1.9),real_t(0),real_t(0)));
+   e1->setShapeID(ss->create<data::Ellipsoid>(Vec3(real_t(1),real_t(2),real_t(3))));
+
+   auto p1 = ps->create();
+   p1->setPosition(Vec3(real_t(-0.9),real_t(0),real_t(0)));
+   p1->setShapeID(ss->create<data::HalfSpace>(Vec3(real_t(1),real_t(0),real_t(0))));
+
+   auto cb1 = ps->create();
+   cb1->setPosition(Vec3(real_t(0),real_t(0),real_t(0)));
+   cb1->setShapeID(ss->create<data::CylindricalBoundary>(real_t(3), Vec3(real_t(0),real_t(0),real_t(1))));
+
+   collision_detection::GeneralContactDetection gcd;
+   kernel::DoubleCast double_cast;
+
+   WALBERLA_CHECK(double_cast(0, 1, accessor, gcd, accessor));
+   WALBERLA_CHECK_FLOAT_EQUAL( gcd.getContactPoint(), Vec3(real_t(0.95),real_t(0),real_t(0)) );
+   WALBERLA_CHECK_FLOAT_EQUAL_EPSILON( gcd.getContactNormal(), Vec3(real_t(-1),real_t(0),real_t(0)), real_t(1e-3) );
+   WALBERLA_CHECK_FLOAT_EQUAL_EPSILON( gcd.getPenetrationDepth(), real_t(-0.1), real_t(1e-3)  );
+   e1->setPosition(Vec3(real_t(2.1),real_t(0),real_t(0)));
+   WALBERLA_CHECK(!double_cast(0, 1, accessor, gcd, accessor));
+
+   WALBERLA_CHECK(double_cast(0, 2, accessor, gcd, accessor));
+   WALBERLA_CHECK_FLOAT_EQUAL( gcd.getContactPoint(), Vec3(real_t(-0.95),real_t(0),real_t(0)) );
+   WALBERLA_CHECK_FLOAT_EQUAL_EPSILON( gcd.getContactNormal(), Vec3(real_t(-1),real_t(0),real_t(0)), real_t(1e-3) );
+   WALBERLA_CHECK_FLOAT_EQUAL_EPSILON( gcd.getPenetrationDepth(), real_t(-0.1), real_t(1e-3) );
+   WALBERLA_CHECK(!double_cast(1, 2, accessor, gcd, accessor));
+
+   WALBERLA_CHECK(double_cast(1, 3, accessor, gcd, accessor));
+   WALBERLA_CHECK_FLOAT_EQUAL( gcd.getContactPoint(), Vec3(real_t(3.05),real_t(0),real_t(0)) );
+   WALBERLA_CHECK_FLOAT_EQUAL_EPSILON( gcd.getContactNormal(), Vec3(real_t(+1),real_t(0),real_t(0)), real_t(1e-3) );
+   WALBERLA_CHECK_FLOAT_EQUAL_EPSILON( gcd.getPenetrationDepth(), real_t(-0.1), real_t(1e-3) );
+   WALBERLA_CHECK(!double_cast(0, 3, accessor, gcd, accessor));
+}
+
+} // namespace mesa_pd
+} // namespace walberla
+
+int main( int argc, char* argv[] )
+{
+   walberla::Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   walberla::mesa_pd::generalContactDetection();
+   return EXIT_SUCCESS;
+}
diff --git a/tests/mesa_pd/collision_detection/SphereSupport.cpp b/tests/mesa_pd/collision_detection/SphereSupport.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2c94e0fc5878ffad2525d4d70787bbdfaa31484f
--- /dev/null
+++ b/tests/mesa_pd/collision_detection/SphereSupport.cpp
@@ -0,0 +1,73 @@
+//======================================================================================================================
+//
+//  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   SphereSupport.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/collision_detection/Support.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/shape/Sphere.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <cmath>
+#include <iostream>
+
+namespace walberla {
+namespace mesa_pd {
+
+inline
+real_t contour(const Vec3& point, const real_t& radius)
+{
+   return ((point[0] * point[0]) + (point[1] * point[1]) + (point[2] * point[2])) / (radius * radius);
+}
+
+void check( )
+{
+   using namespace walberla::mesa_pd::collision_detection;
+
+   Vec3 pos      = Vec3(real_t(1),real_t(2),real_t(3));
+   real_t radius = real_t(2.356);
+
+   auto sp = data::Sphere(radius);
+   Support e0(pos, Rot3(), sp);
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(1),real_t(0),real_t(0))),  Vec3(real_t(1+2.356),real_t(2),real_t(3)));
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(0),real_t(1),real_t(0))),  Vec3(real_t(1),real_t(2+2.356),real_t(3)));
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(0),real_t(0),real_t(1))),  Vec3(real_t(1),real_t(2),real_t(3+2.356)));
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(-1),real_t(0),real_t(0))), Vec3(real_t(1-2.356),real_t(2),real_t(3)));
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(0),real_t(-1),real_t(0))), Vec3(real_t(1),real_t(2-2.356),real_t(3)));
+   WALBERLA_CHECK_FLOAT_EQUAL(e0.support(Vec3(real_t(0),real_t(0),real_t(-1))), Vec3(real_t(1),real_t(2),real_t(3-2.356)));
+
+   WALBERLA_CHECK_FLOAT_EQUAL( contour(e0.support(Vec3(real_t(-1),real_t(-2),real_t(-3)).getNormalized()) - pos, radius), real_t(1) );
+   WALBERLA_CHECK_FLOAT_EQUAL( contour(e0.support(Vec3(real_t(-2),real_t(-3),real_t(-1)).getNormalized()) - pos, radius), real_t(1) );
+   WALBERLA_CHECK_FLOAT_EQUAL( contour(e0.support(Vec3(real_t(-3),real_t(-1),real_t(-2)).getNormalized()) - pos, radius), real_t(1) );
+}
+
+} //namespace mesa_pd
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   walberla::Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   walberla::mesa_pd::check();
+
+   return EXIT_SUCCESS;
+}