From d04c9922437aed54c121b981acc5b7f2c438032c Mon Sep 17 00:00:00 2001
From: Michael Kuron <mkuron@icp.uni-stuttgart.de>
Date: Thu, 28 Mar 2019 13:57:16 +0100
Subject: [PATCH] change BodyTypeTuple of Union to variadic template

so Union<std::tuple<Sphere,Box>> becomes Union<Sphere,Box>
---
 src/pe/Types.h                            |   2 +-
 src/pe/communication/DynamicMarshalling.h |   2 -
 src/pe/communication/rigidbody/Union.h    |  22 ++--
 src/pe/fcd/AnalyticCollisionDetection.h   |  30 +++--
 src/pe/fcd/GJKEPACollideFunctor.h         |  49 ++++----
 src/pe/fcd/SimpleFCD.h                    |   1 -
 src/pe/raytracing/Intersects.h            |   1 -
 src/pe/rigidbody/RigidBody.h              |   4 +-
 src/pe/rigidbody/Union.h                  | 133 +++++++++++-----------
 src/pe/rigidbody/UnionFactory.h           |  36 +++---
 src/pe/vtk/SphereVtkOutput.cpp            |  16 +--
 tests/mesh/MeshMarshalling.cpp            |   3 +-
 tests/pe/Collision.cpp                    |   2 +-
 tests/pe/CollisionTobiasGJK.cpp           |  10 +-
 tests/pe/Marshalling.cpp                  |   7 +-
 tests/pe/PeDocumentationSnippets.cpp      |   5 +-
 tests/pe/SetBodyTypeIDs.cpp               |   2 +-
 tests/pe/ShadowCopy.cpp                   |   4 +-
 tests/pe/Union.cpp                        |   4 +-
 19 files changed, 160 insertions(+), 173 deletions(-)

diff --git a/src/pe/Types.h b/src/pe/Types.h
index ce32a6205..d66c9df92 100644
--- a/src/pe/Types.h
+++ b/src/pe/Types.h
@@ -74,7 +74,7 @@ class Sphere;
 class Spring;
 class Squirmer;
 class TriangleMesh;
-template <typename BodyTypeTuple>
+template <typename... BodyTypes>
 class Union;
 
 typedef RigidBody             BodyType;            //!< Type of the rigid bodies.
diff --git a/src/pe/communication/DynamicMarshalling.h b/src/pe/communication/DynamicMarshalling.h
index 7786dd4b1..8e996c3c6 100644
--- a/src/pe/communication/DynamicMarshalling.h
+++ b/src/pe/communication/DynamicMarshalling.h
@@ -38,8 +38,6 @@
 
 #include "core/Abort.h"
 
-#include <tuple>
-
 
 namespace walberla {
 namespace pe {
diff --git a/src/pe/communication/rigidbody/Union.h b/src/pe/communication/rigidbody/Union.h
index 67ba4db6d..bfaf37881 100644
--- a/src/pe/communication/rigidbody/Union.h
+++ b/src/pe/communication/rigidbody/Union.h
@@ -31,6 +31,8 @@
 #include "pe/communication/Marshalling.h"
 #include "pe/rigidbody/Union.h"
 
+#include <tuple>
+
 namespace walberla {
 namespace pe {
 namespace communication {
@@ -57,8 +59,8 @@ struct UnionParameters : public GeomPrimitiveParameters {
  * \param obj The object to be marshalled.
  * \return void
  */
-template < typename BodyTypeTuple >
-void marshal( mpi::SendBuffer& buffer, const Union<BodyTypeTuple>& obj )
+template < typename... BodyTypes >
+void marshal( mpi::SendBuffer& buffer, const Union<BodyTypes...>& obj )
 {
    // Material of union is not relevant for reconstruction thus marshal RigidBody instead of GeomPrimitive.
    marshal( buffer, static_cast<const RigidBody&>( obj ) );
@@ -72,12 +74,12 @@ void marshal( mpi::SendBuffer& buffer, const Union<BodyTypeTuple>& obj )
    buffer << static_cast<size_t> (obj.size());                  // Encoding the number of contained bodies
 
    // Encoding the contained primitives
-   const typename Union<BodyTypeTuple>::const_iterator begin( obj.begin() );
-   const typename Union<BodyTypeTuple>::const_iterator end  ( obj.end()   );
-   for(  typename Union<BodyTypeTuple>::const_iterator body=begin; body!=end; ++body )
+   const typename Union<BodyTypes...>::const_iterator begin( obj.begin() );
+   const typename Union<BodyTypes...>::const_iterator end  ( obj.end()   );
+   for(  typename Union<BodyTypes...>::const_iterator body=begin; body!=end; ++body )
    {
       buffer << body->getTypeID();
-      MarshalDynamically<BodyTypeTuple>::execute( buffer, *body );
+      MarshalDynamically<std::tuple<BodyTypes...>>::execute( buffer, *body );
    }
 }
 
@@ -109,13 +111,13 @@ void unmarshal( mpi::RecvBuffer& buffer, UnionParameters& objparam )
 //*************************************************************************************************
 
 
-template <typename BodyTypeTuple>
-inline std::unique_ptr<Union<BodyTypeTuple>> instantiate( mpi::RecvBuffer& buffer, const math::AABB& domain, const math::AABB& block, Union<BodyTypeTuple>*& newBody )
+template <typename... BodyTypes>
+inline std::unique_ptr<Union<BodyTypes...>> instantiate( mpi::RecvBuffer& buffer, const math::AABB& domain, const math::AABB& block, Union<BodyTypes...>*& newBody )
 {
    UnionParameters subobjparam;
    unmarshal( buffer, subobjparam );
    correctBodyPosition(domain, block.center(), subobjparam.gpos_);
-   auto un = std::make_unique<Union<BodyTypeTuple>>( subobjparam.sid_,
+   auto un = std::make_unique<Union<BodyTypes...>>( subobjparam.sid_,
                                                      subobjparam.uid_,
                                                      subobjparam.gpos_,
                                                      subobjparam.rpos_,
@@ -131,7 +133,7 @@ inline std::unique_ptr<Union<BodyTypeTuple>> instantiate( mpi::RecvBuffer& buffe
    {
       decltype ( static_cast<BodyID>(nullptr)->getTypeID() ) type;
       buffer >> type;
-      BodyPtr obj( UnmarshalDynamically<BodyTypeTuple>::execute(buffer, type, domain, block) );
+      BodyPtr obj( UnmarshalDynamically<std::tuple<BodyTypes...>>::execute(buffer, type, domain, block) );
       obj->setRemote( true );
       un->add(std::move(obj));
    }
diff --git a/src/pe/fcd/AnalyticCollisionDetection.h b/src/pe/fcd/AnalyticCollisionDetection.h
index 0287f6d8d..848134d48 100644
--- a/src/pe/fcd/AnalyticCollisionDetection.h
+++ b/src/pe/fcd/AnalyticCollisionDetection.h
@@ -37,8 +37,6 @@
 #include "core/math/Shims.h"
 #include "geometry/GeometricalFunctions.h"
 
-#include <tuple>
-
 namespace walberla {
 namespace pe {
 namespace fcd {
@@ -110,17 +108,17 @@ template <typename Container>
 inline
 bool collide( CapsuleID c, BoxID b, Container& container );
 
-template <typename BodyTypeTuple, typename BodyB, typename Container>
+template <typename... BodyTypes, typename BodyB, typename Container>
 inline
-bool collide( Union<BodyTypeTuple>* bd1, BodyB* bd2, Container& container );
+bool collide( Union<BodyTypes...>* bd1, BodyB* bd2, Container& container );
 
-template <typename BodyA, typename BodyTypeTuple, typename Container>
+template <typename BodyA, typename... BodyTypes, typename Container>
 inline
-bool collide( BodyA* bd1, Union<BodyTypeTuple>* bd2, Container& container );
+bool collide( BodyA* bd1, Union<BodyTypes...>* bd2, Container& container );
 
-template <typename BodyTypeTupleA, typename BodyTypeTupleB, typename Container>
+template <typename... BodyTypesA, typename... BodyTypesB, typename Container>
 inline
-bool collide( Union<BodyTypeTupleA>* bd1, Union<BodyTypeTupleB>* bd2, Container& container );
+bool collide( Union<BodyTypesA...>* bd1, Union<BodyTypesB...>* bd2, Container& container );
 
 } //namespace analytic
 
@@ -2120,29 +2118,29 @@ bool collide( CapsuleID c, BoxID b, Container& container )
    return collide(b, c, container);
 }
 
-template <typename BodyTypeTuple, typename BodyB, typename Container>
+template <typename... BodyTypes, typename BodyB, typename Container>
 inline
-bool collide( Union<BodyTypeTuple>* bd1, BodyB* bd2, Container& container )
+bool collide( Union<BodyTypes...>* bd1, BodyB* bd2, Container& container )
 {
    AnalyticSingleCollideFunctor<BodyB, Container> func(bd2, container);
    bool collision = false;
    for( auto it=bd1->begin(); it!=bd1->end(); ++it )
    {
-      collision |= SingleCast<BodyTypeTuple, AnalyticSingleCollideFunctor<BodyB, Container>, bool>::execute(it.getBodyID(), func);
+      collision |= SingleCast<std::tuple<BodyTypes...>, AnalyticSingleCollideFunctor<BodyB, Container>, bool>::execute(it.getBodyID(), func);
    }
    return collision;
 }
 
-template <typename BodyA, typename BodyTypeTuple, typename Container>
+template <typename BodyA, typename... BodyTypes, typename Container>
 inline
-bool collide( BodyA* bd1, Union<BodyTypeTuple>* bd2, Container& container )
+bool collide( BodyA* bd1, Union<BodyTypes...>* bd2, Container& container )
 {
    return collide (bd2, bd1, container);
 }
 
-template <typename BodyTypeTupleA, typename BodyTypeTupleB, typename Container>
+template <typename... BodyTypesA, typename... BodyTypesB, typename Container>
 inline
-bool collide( Union<BodyTypeTupleA>* bd1, Union<BodyTypeTupleB>* bd2, Container& container )
+bool collide( Union<BodyTypesA...>* bd1, Union<BodyTypesB...>* bd2, Container& container )
 {
    AnalyticCollideFunctor<Container> func(container);
    bool collision = false;
@@ -2150,7 +2148,7 @@ bool collide( Union<BodyTypeTupleA>* bd1, Union<BodyTypeTupleB>* bd2, Container&
    {
       for( auto it2=bd2->begin(); it2!=bd2->end(); ++it2 )
       {
-         collision |= DoubleCast<BodyTypeTupleA, BodyTypeTupleB, AnalyticCollideFunctor<Container>, bool>::execute(it1.getBodyID(), it2.getBodyID(), func);
+         collision |= DoubleCast<std::tuple<BodyTypesA...>, std::tuple<BodyTypesB...>, AnalyticCollideFunctor<Container>, bool>::execute(it1.getBodyID(), it2.getBodyID(), func);
       }
    }
    return collision;
diff --git a/src/pe/fcd/GJKEPACollideFunctor.h b/src/pe/fcd/GJKEPACollideFunctor.h
index c163ce145..d88304677 100644
--- a/src/pe/fcd/GJKEPACollideFunctor.h
+++ b/src/pe/fcd/GJKEPACollideFunctor.h
@@ -28,7 +28,6 @@
 #include "pe/rigidbody/Plane.h"
 #include "pe/rigidbody/Union.h"
 #include <pe/Thresholds.h>
-#include <tuple>
 
 namespace walberla{
 namespace pe{
@@ -50,25 +49,25 @@ namespace gjkepa{
    inline bool generateContacts(Plane *a, Plane *b, Container& contacts_);
 
    //Unions
-   template<typename BodyTupleA, typename BodyB, typename Container>
-   inline bool generateContacts(Union<BodyTupleA> *a, BodyB *b, Container& contacts_);
+   template<typename... BodyTypesA, typename BodyB, typename Container>
+   inline bool generateContacts(Union<BodyTypesA...> *a, BodyB *b, Container& contacts_);
 
-   template<typename BodyA, typename BodyTupleB, typename Container>
-   inline bool generateContacts(BodyA *a, Union<BodyTupleB> *b, Container& contacts_);
+   template<typename BodyA, typename... BodyTypesB, typename Container>
+   inline bool generateContacts(BodyA *a, Union<BodyTypesB...> *b, Container& contacts_);
 
-   template<typename BodyTupleA, typename BodyTupleB, typename Container>
-   inline bool generateContacts(Union<BodyTupleA> *a, Union<BodyTupleB>  *b, Container& contacts_);
+   template<typename... BodyTypesA, typename... BodyTypesB, typename Container>
+   inline bool generateContacts(Union<BodyTypesA...> *a, Union<BodyTypesB...>  *b, Container& contacts_);
 
    //Union and Plane
-   template<typename BodyTupleA, typename Container>
-   inline bool generateContacts(Union<BodyTupleA> *a, Plane *b, Container& contacts_);
+   template<typename... BodyTypesA, typename Container>
+   inline bool generateContacts(Union<BodyTypesA...> *a, Plane *b, Container& contacts_);
 
-   template<typename BodyTupleB, typename Container>
-   inline bool generateContacts(Plane *a, Union<BodyTupleB> *b, Container& contacts_);
+   template<typename... BodyTypesB, typename Container>
+   inline bool generateContacts(Plane *a, Union<BodyTypesB...> *b, Container& contacts_);
 }
 
 /* Iterative Collide Functor for contact Generation with iterative collision detection (GJK and EPA algorithms).
- * Usage: fcd::GenericFCD<BodyTuple, fcd::GJKEPACollideFunctor> testFCD;
+ * Usage: fcd::GenericFCD<BodyTypes..., fcd::GJKEPACollideFunctor> testFCD;
  * testFCD.generateContacts(...);
  */
 template <typename Container>
@@ -163,50 +162,50 @@ namespace gjkepa{
    }
 
    //Unions
-   template<typename BodyTupleA, typename BodyB, typename Container>
-   inline bool generateContacts(Union<BodyTupleA> *a, BodyB *b, Container& contacts_){
+   template<typename... BodyTypesA, typename BodyB, typename Container>
+   inline bool generateContacts(Union<BodyTypesA...> *a, BodyB *b, Container& contacts_){
       GJKEPASingleCollideFunctor<BodyB, Container> func(b, contacts_);
       bool collision = false;
       for( auto it=a->begin(); it!=a->end(); ++it )
       {
-         collision |= SingleCast<BodyTupleA, GJKEPASingleCollideFunctor<BodyB, Container>, bool>::execute(it.getBodyID(), func);
+         collision |= SingleCast<std::tuple<BodyTypesA...>, GJKEPASingleCollideFunctor<BodyB, Container>, bool>::execute(it.getBodyID(), func);
       }
       return collision;
    }
 
-   template<typename BodyA, typename BodyTupleB, typename Container>
-   inline bool generateContacts(BodyA *a, Union<BodyTupleB> *b, Container& contacts_){
+   template<typename BodyA, typename... BodyTypesB, typename Container>
+   inline bool generateContacts(BodyA *a, Union<BodyTypesB...> *b, Container& contacts_){
       return generateContacts(b, a, contacts_);
    }
 
-   template<typename BodyTupleA, typename BodyTupleB, typename Container>
-   inline bool generateContacts(Union<BodyTupleA> *a, Union<BodyTupleB>  *b, Container& contacts_){
+   template<typename... BodyTypesA, typename... BodyTypesB, typename Container>
+   inline bool generateContacts(Union<BodyTypesA...> *a, Union<BodyTypesB...>  *b, Container& contacts_){
       GJKEPACollideFunctor<Container> func(contacts_);
       bool collision = false;
       for( auto it1=a->begin(); it1!=a->end(); ++it1 )
       {
          for( auto it2=b->begin(); it2!=b->end(); ++it2 )
          {
-            collision |= DoubleCast<BodyTupleA, BodyTupleB, GJKEPACollideFunctor<Container>, bool>::execute(it1.getBodyID(), it2.getBodyID(), func);
+            collision |= DoubleCast<std::tuple<BodyTypesA...>, std::tuple<BodyTypesB...>, GJKEPACollideFunctor<Container>, bool>::execute(it1.getBodyID(), it2.getBodyID(), func);
          }
       }
       return collision;
    }
 
    //Union and Plane (these calls are ambigous if not implemented seperatly)
-   template<typename BodyTupleA, typename Container>
-   inline bool generateContacts(Union<BodyTupleA> *a, Plane *b, Container& contacts_){
+   template<typename... BodyTypesA, typename Container>
+   inline bool generateContacts(Union<BodyTypesA...> *a, Plane *b, Container& contacts_){
       GJKEPASingleCollideFunctor<Plane, Container> func(b, contacts_);
       bool collision = false;
       for( auto it=a->begin(); it!=a->end(); ++it )
       {
-         collision |= SingleCast<BodyTupleA, GJKEPASingleCollideFunctor<Plane, Container>, bool>::execute(it.getBodyID(), func);
+         collision |= SingleCast<std::tuple<BodyTypesA...>, GJKEPASingleCollideFunctor<Plane, Container>, bool>::execute(it.getBodyID(), func);
       }
       return collision;
    }
 
-   template<typename BodyTupleB, typename Container>
-   inline bool generateContacts(Plane *a, Union<BodyTupleB> *b, Container& contacts_){
+   template<typename... BodyTypesB, typename Container>
+   inline bool generateContacts(Plane *a, Union<BodyTypesB...> *b, Container& contacts_){
       return generateContacts(b, a, contacts_);
    }
 
diff --git a/src/pe/fcd/SimpleFCD.h b/src/pe/fcd/SimpleFCD.h
index a7f08ff4d..6f38ec190 100644
--- a/src/pe/fcd/SimpleFCD.h
+++ b/src/pe/fcd/SimpleFCD.h
@@ -23,7 +23,6 @@
 #include "AnalyticCollisionDetection.h"
 #include "GenericFCD.h"
 
-#include <tuple>
 #include <type_traits>
 
 namespace walberla{
diff --git a/src/pe/raytracing/Intersects.h b/src/pe/raytracing/Intersects.h
index eea0f9719..3746ce6d0 100644
--- a/src/pe/raytracing/Intersects.h
+++ b/src/pe/raytracing/Intersects.h
@@ -30,7 +30,6 @@
 #include "pe/rigidbody/Union.h"
 #include "pe/utility/BodyCast.h"
 #include <core/math/Utility.h>
-#include <tuple>
 
 #include <pe/raytracing/Ray.h>
 
diff --git a/src/pe/rigidbody/RigidBody.h b/src/pe/rigidbody/RigidBody.h
index 4f872a685..d987ca4e7 100644
--- a/src/pe/rigidbody/RigidBody.h
+++ b/src/pe/rigidbody/RigidBody.h
@@ -39,7 +39,7 @@
 namespace walberla{
 namespace pe{
 
-template <typename BodyTypeTuple>
+template <typename... BodyTypes>
 class Union;
 
 /**
@@ -50,7 +50,7 @@ class RigidBody : public ccd::HashGridsBodyTrait
                 , private NonCopyable
 {
 private:
-   template <typename BodyTypeTuple>
+   template <typename... BodyTypes>
    friend class Union;
 
 protected:
diff --git a/src/pe/rigidbody/Union.h b/src/pe/rigidbody/Union.h
index 02ce734c1..3311851f5 100644
--- a/src/pe/rigidbody/Union.h
+++ b/src/pe/rigidbody/Union.h
@@ -43,8 +43,6 @@
 #include <iostream>
 #include <stdexcept>
 
-#include <tuple>
-
 namespace walberla {
 namespace pe {
 
@@ -63,13 +61,10 @@ namespace pe {
  * the basic functionality of a sphere. For a full description of the sphere geometry,
  * see the Sphere class description.
  */
-template <typename BodyTypeTuple>
+template <typename... BodyTypes>
 class Union : public RigidBody
 {
 public:
-   //**Type definitions****************************************************************************
-   using BodyTypeTupleT        = BodyTypeTuple;
-
    //**********************************************************************************************
 
    using size_type             = BodyStorage::size_type;           //!< Size type of the body storage.
@@ -232,8 +227,8 @@ private:
  * \param communicating specifies if the sphere should take part in synchronization (syncNextNeighbour, syncShadowOwner)
  * \param infiniteMass specifies if the sphere has infinite mass and will be treated as an obstacle
  */
-template <typename BodyTypeTuple>
-Union<BodyTypeTuple>::Union( id_t sid, id_t uid, const Vec3& gpos, const Vec3& rpos, const Quat& q,
+template <typename... BodyTypes>
+Union<BodyTypes...>::Union( id_t sid, id_t uid, const Vec3& gpos, const Vec3& rpos, const Quat& q,
                              const bool global, const bool communicating, const bool /*infiniteMass*/ )
    : RigidBody( getStaticTypeID(), sid, uid )  // Initialization of the parent class
 {
@@ -265,8 +260,8 @@ Union<BodyTypeTuple>::Union( id_t sid, id_t uid, const Vec3& gpos, const Vec3& r
 //*************************************************************************************************
 /*!\brief Destructor for the Sphere class.
  */
-template <typename BodyTypeTuple>
-Union<BodyTypeTuple>::~Union()
+template <typename... BodyTypes>
+Union<BodyTypes...>::~Union()
 {
    // Clearing the bodies
    bodies_.clear();
@@ -281,8 +276,8 @@ Union<BodyTypeTuple>::~Union()
  *
  * \return The volume of the union.
  */
-template <typename BodyTypeTuple>
-inline real_t Union<BodyTypeTuple>::getVolume() const
+template <typename... BodyTypes>
+inline real_t Union<BodyTypes...>::getVolume() const
 {
    real_t volume(0);
    for( auto bodyIt=bodies_.begin(); bodyIt!=bodies_.end(); ++bodyIt )
@@ -302,8 +297,8 @@ inline real_t Union<BodyTypeTuple>::getVolume() const
  * communication to set the remote status of a union within the simulation world. Using
  * this function explicitly may lead to simulation errors during a parallel simulation!
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::setRemote( bool remote )
+template <typename... BodyTypes>
+void Union<BodyTypes...>::setRemote( bool remote )
 {
    remote_ = remote;
 
@@ -322,8 +317,8 @@ void Union<BodyTypeTuple>::setRemote( bool remote )
  * This function updates the axis-aligned bounding box of the union according to the current
  * position and orientation of the contained rigid bodies.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::calcBoundingBox()
+template <typename... BodyTypes>
+void Union<BodyTypes...>::calcBoundingBox()
 {
    // Setting the bounding box of an empty union
    if( bodies_.isEmpty() ) {
@@ -356,8 +351,8 @@ void Union<BodyTypeTuple>::calcBoundingBox()
  *
  * \return void
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::calcCenterOfMass()
+template <typename... BodyTypes>
+void Union<BodyTypes...>::calcCenterOfMass()
 {
    // Checking the state of the union
    WALBERLA_ASSERT( checkInvariants(), "Invalid union state detected" );
@@ -440,8 +435,8 @@ void Union<BodyTypeTuple>::calcCenterOfMass()
  *
  * \return void
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::calcInertia()
+template <typename... BodyTypes>
+void Union<BodyTypes...>::calcInertia()
 {
    // Checking the state of the union
    WALBERLA_ASSERT( checkInvariants(), "Invalid union state detected" );
@@ -506,8 +501,8 @@ void Union<BodyTypeTuple>::calcInertia()
  * rigid body, the function shifts the union to reposition its anchor point according to the
  * given global coordinate.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::setPositionImpl( real_t px, real_t py, real_t pz )
+template <typename... BodyTypes>
+void Union<BodyTypes...>::setPositionImpl( real_t px, real_t py, real_t pz )
 {
    Vec3 gpos = Vec3( px, py, pz );
 
@@ -521,7 +516,7 @@ void Union<BodyTypeTuple>::setPositionImpl( real_t px, real_t py, real_t pz )
    for( auto& b : bodies_ )
       b.update( dp );
 
-   Union<BodyTypeTuple>::calcBoundingBox();    // Setting the axis-aligned bounding box
+   Union<BodyTypes...>::calcBoundingBox();    // Setting the axis-aligned bounding box
    wake();               // Waking the union from sleep mode
    signalTranslation();  // Signaling the position change to the superordinate body
 }
@@ -544,8 +539,8 @@ void Union<BodyTypeTuple>::setPositionImpl( real_t px, real_t py, real_t pz )
  * the anchor point of the union (if the union is infinite). The orientation of the rigid bodies
  * within the union in reference to the body frame of the union is not changed.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::setOrientationImpl( real_t r, real_t i, real_t j, real_t k )
+template <typename... BodyTypes>
+void Union<BodyTypes...>::setOrientationImpl( real_t r, real_t i, real_t j, real_t k )
 {
    const Quat q ( r, i, j, k );
    const Quat dq( q * q_.getInverse() );
@@ -557,7 +552,7 @@ void Union<BodyTypeTuple>::setOrientationImpl( real_t r, real_t i, real_t j, rea
    for( auto& b : bodies_ )
       b.update( dq );
 
-   Union<BodyTypeTuple>::calcBoundingBox();  // Setting the axis-aligned bounding box
+   Union<BodyTypes...>::calcBoundingBox();  // Setting the axis-aligned bounding box
    wake();             // Waking the union from sleep mode
    signalRotation();   // Signaling the change of orientation to the superordinate body
 }
@@ -581,8 +576,8 @@ void Union<BodyTypeTuple>::setOrientationImpl( real_t r, real_t i, real_t j, rea
  * movement. This movement involves a change in the global position and the axis-aligned
  * bounding box.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::update( const Vec3& dp )
+template <typename... BodyTypes>
+void Union<BodyTypes...>::update( const Vec3& dp )
 {
    // Checking the state of the union
    WALBERLA_ASSERT( checkInvariants(), "Invalid union state detected" );
@@ -596,7 +591,7 @@ void Union<BodyTypeTuple>::update( const Vec3& dp )
       b.update( dp );
 
    // Setting the axis-aligned bounding box
-   Union<BodyTypeTuple>::calcBoundingBox();
+   Union<BodyTypes...>::calcBoundingBox();
 
    // Checking the state of the union
    WALBERLA_ASSERT( checkInvariants(), "Invalid union state detected" );
@@ -614,8 +609,8 @@ void Union<BodyTypeTuple>::update( const Vec3& dp )
  * This movement involves a change in the global position, the orientation/rotation and the
  * axis-aligned bounding box of the box.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::update( const Quat& dq )
+template <typename... BodyTypes>
+void Union<BodyTypes...>::update( const Quat& dq )
 {
    // Checking the state of the union
    WALBERLA_ASSERT( checkInvariants(), "Invalid union state detected" );
@@ -633,7 +628,7 @@ void Union<BodyTypeTuple>::update( const Quat& dq )
       b.update( dq );
 
    // Setting the axis-aligned bounding box
-   Union<BodyTypeTuple>::calcBoundingBox();
+   Union<BodyTypes...>::calcBoundingBox();
 
    // Checking the state of the union
    WALBERLA_ASSERT( checkInvariants(), "Invalid union state detected" );
@@ -699,8 +694,8 @@ void Union<BodyTypeTuple>::update( const Quat& dq )
  *    individually for the rigid body (to exclusively make the body (in-)visible) or the entire
  *    union (to make the entire union (in-)visible.
  */
-template <typename BodyTypeTuple>
-RigidBody& Union<BodyTypeTuple>::add( std::unique_ptr<RigidBody>&& body )
+template <typename... BodyTypes>
+RigidBody& Union<BodyTypes...>::add( std::unique_ptr<RigidBody>&& body )
 {
    // Checking for "self-assignment"
    if( body.get() == BodyID( this ) ) return *this;
@@ -770,8 +765,8 @@ RigidBody& Union<BodyTypeTuple>::add( std::unique_ptr<RigidBody>&& body )
  * Changing the global position of the entire union's center of mass. All contained rigid bodies
  * are moved by the same displacement.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::translateImpl( real_t dx, real_t dy, real_t dz )
+template <typename... BodyTypes>
+void Union<BodyTypes...>::translateImpl( real_t dx, real_t dy, real_t dz )
 {
    Vec3 dp(dx, dy, dz);
 
@@ -782,7 +777,7 @@ void Union<BodyTypeTuple>::translateImpl( real_t dx, real_t dy, real_t dz )
    for( auto& b : bodies_ )
       b.update( dp );
 
-   Union<BodyTypeTuple>::calcBoundingBox();    // Setting the axis-aligned bounding box
+   Union<BodyTypes...>::calcBoundingBox();    // Setting the axis-aligned bounding box
    wake();               // Waking the union from sleep mode
    signalTranslation();  // Signaling the position change to the superordinate body
 }
@@ -809,8 +804,8 @@ void Union<BodyTypeTuple>::translateImpl( real_t dx, real_t dy, real_t dz )
  * around the anchor point of the union (if the union is infinite). The orientation of the
  * bodies within the union in reference to the body frame of the union is not changed.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::rotateImpl( const Quat& dq )
+template <typename... BodyTypes>
+void Union<BodyTypes...>::rotateImpl( const Quat& dq )
 {
    if (isFixed())
       WALBERLA_ABORT("Trying to rotate a fixed body: " << *this);
@@ -822,7 +817,7 @@ void Union<BodyTypeTuple>::rotateImpl( const Quat& dq )
    for( auto& b : bodies_ )
       b.update( dq );
 
-   Union<BodyTypeTuple>::calcBoundingBox();  // Setting the axis-aligned bounding box
+   Union<BodyTypes...>::calcBoundingBox();  // Setting the axis-aligned bounding box
    wake();             // Waking the union from sleep mode
    signalRotation();   // Signaling the change of orientation to the superordinate body
 }
@@ -843,8 +838,8 @@ void Union<BodyTypeTuple>::rotateImpl( const Quat& dq )
  * are applied in the order x, y, z. The orientation of the bodies within the union in
  * reference to the body frame of the union is not changed.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::rotateAroundOriginImpl( const Quat& dq )
+template <typename... BodyTypes>
+void Union<BodyTypes...>::rotateAroundOriginImpl( const Quat& dq )
 {
    q_    = dq * q_;                // Updating the orientation of the union
    R_    = q_.toRotationMatrix();  // Updating the rotation of the union
@@ -854,7 +849,7 @@ void Union<BodyTypeTuple>::rotateAroundOriginImpl( const Quat& dq )
    for( auto& b : bodies_ )
       b.update( dq );
 
-   Union<BodyTypeTuple>::calcBoundingBox();    // Setting the axis-aligned bounding box
+   Union<BodyTypes...>::calcBoundingBox();    // Setting the axis-aligned bounding box
    wake();               // Waking the union from sleep mode
    signalTranslation();  // Signaling the position change to the superordinate body
 }
@@ -874,8 +869,8 @@ void Union<BodyTypeTuple>::rotateAroundOriginImpl( const Quat& dq )
  * all contained rigid bodies change their position and orientation accordingly. The orientation
  * of the bodies within the union in reference to the body frame of the union is not changed.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::rotateAroundPointImpl( const Vec3& point, const Quat &dq )
+template <typename... BodyTypes>
+void Union<BodyTypes...>::rotateAroundPointImpl( const Vec3& point, const Quat &dq )
 {
    const Vec3 dp( gpos_ - point );
 
@@ -887,7 +882,7 @@ void Union<BodyTypeTuple>::rotateAroundPointImpl( const Vec3& point, const Quat
    for( auto& b : bodies_ )
       b.update( dq );
 
-   Union<BodyTypeTuple>::calcBoundingBox();    // Setting the axis-aligned bounding box
+   Union<BodyTypes...>::calcBoundingBox();    // Setting the axis-aligned bounding box
    wake();               // Waking the union from sleep mode
    signalTranslation();  // Signaling the position change to the superordinate body
 }
@@ -910,8 +905,8 @@ void Union<BodyTypeTuple>::rotateAroundPointImpl( const Vec3& point, const Quat
  * \param pz The z-component of the relative coordinate.
  * \return \a true if the point lies inside the sphere, \a false if not.
  */
-template <typename BodyTypeTuple>
-bool Union<BodyTypeTuple>::containsRelPointImpl( real_t px, real_t py, real_t pz ) const
+template <typename... BodyTypes>
+bool Union<BodyTypes...>::containsRelPointImpl( real_t px, real_t py, real_t pz ) const
 {
    const Vec3 gpos( pointFromBFtoWF( px, py, pz ) );
    for( auto& b : bodies_ )
@@ -931,8 +926,8 @@ bool Union<BodyTypeTuple>::containsRelPointImpl( real_t px, real_t py, real_t pz
  *
  * The tolerance level of the check is pe::surfaceThreshold.
  */
-template <typename BodyTypeTuple>
-bool Union<BodyTypeTuple>::isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const
+template <typename... BodyTypes>
+bool Union<BodyTypes...>::isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const
 {
    bool surface( false );
    const Vec3 gpos( pointFromBFtoWF( px, py, pz ) );
@@ -964,8 +959,8 @@ bool Union<BodyTypeTuple>::isSurfaceRelPointImpl( real_t px, real_t py, real_t p
  * In case one of the contained rigid bodies is interally modified, this function is called to
  * recalculate the changed properties of the union.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::handleModification()
+template <typename... BodyTypes>
+void Union<BodyTypes...>::handleModification()
 {
    // Setting the finite flag
    finite_ = true;
@@ -990,7 +985,7 @@ void Union<BodyTypeTuple>::handleModification()
    calcInertia();
 
    // Updating the axis-aligned bounding box
-   Union<BodyTypeTuple>::calcBoundingBox();
+   Union<BodyTypes...>::calcBoundingBox();
 
    // Signaling the internal modification to the superordinate body
    signalModification();
@@ -1006,8 +1001,8 @@ void Union<BodyTypeTuple>::handleModification()
  * In case one of the contained rigid bodies changes its position, this function is called
  * to recalculate the changed properties of the union.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::handleTranslation()
+template <typename... BodyTypes>
+void Union<BodyTypes...>::handleTranslation()
 {
    // Setting the union's total mass and center of mass
    calcCenterOfMass();
@@ -1019,7 +1014,7 @@ void Union<BodyTypeTuple>::handleTranslation()
    // Setting the moment of inertia
    calcInertia();
 
-   Union<BodyTypeTuple>::calcBoundingBox();    // Setting the axis-aligned bounding box
+   Union<BodyTypes...>::calcBoundingBox();    // Setting the axis-aligned bounding box
    wake();               // Waking the union from sleep mode
    signalTranslation();  // Signaling the position change to the superordinate body
 }
@@ -1034,13 +1029,13 @@ void Union<BodyTypeTuple>::handleTranslation()
  * In case one of the contained rigid bodies changes its orientation, this function is called
  * to recalculate the changed properties of the union.
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::handleRotation()
+template <typename... BodyTypes>
+void Union<BodyTypes...>::handleRotation()
 {
    // Setting the moment of inertia
    calcInertia();
 
-   Union<BodyTypeTuple>::calcBoundingBox();  // Setting the axis-aligned bounding box
+   Union<BodyTypes...>::calcBoundingBox();  // Setting the axis-aligned bounding box
    wake();             // Waking the union from sleep mode
    signalRotation();   // Signaling the change of orientation to the superordinate body
 }
@@ -1062,8 +1057,8 @@ void Union<BodyTypeTuple>::handleRotation()
  * \param tab Indentation in front of every line of the union output.
  * \return void
  */
-template <typename BodyTypeTuple>
-void Union<BodyTypeTuple>::print( std::ostream& os, const char* tab ) const
+template <typename... BodyTypes>
+void Union<BodyTypes...>::print( std::ostream& os, const char* tab ) const
 {
    using std::setw;
 
@@ -1125,8 +1120,8 @@ void Union<BodyTypeTuple>::print( std::ostream& os, const char* tab ) const
  * \param u Reference to a constant union object.
  * \return Reference to the output stream.
  */
-template <typename BodyTypeTuple>
-std::ostream& operator<<( std::ostream& os, const Union<BodyTypeTuple>& u )
+template <typename... BodyTypes>
+std::ostream& operator<<( std::ostream& os, const Union<BodyTypes...>& u )
 {
    os << "--" << "UNION PARAMETERS"
       << "--------------------------------------------------------------\n";
@@ -1145,8 +1140,8 @@ std::ostream& operator<<( std::ostream& os, const Union<BodyTypeTuple>& u )
  * \param u Constant union handle.
  * \return Reference to the output stream.
  */
-template <typename BodyTypeTuple>
-std::ostream& operator<<( std::ostream& os, Union<BodyTypeTuple> const * u )
+template <typename... BodyTypes>
+std::ostream& operator<<( std::ostream& os, Union<BodyTypes...> const * u )
 {
    os << "--" << "UNION PARAMETERS"
       << "--------------------------------------------------------------\n";
@@ -1170,14 +1165,14 @@ std::ostream& operator<<( std::ostream& os, Union<BodyTypeTuple> const * u )
  *
  * \return geometry specific type id
  */
-template <typename BodyTypeTuple>
-inline id_t Union<BodyTypeTuple>::getStaticTypeID()
+template <typename... BodyTypes>
+inline id_t Union<BodyTypes...>::getStaticTypeID()
 {
    return staticTypeID_;
 }
 
-template <typename BodyTypeTuple>
-id_t Union<BodyTypeTuple>::staticTypeID_ = std::numeric_limits<id_t>::max();
+template <typename... BodyTypes>
+id_t Union<BodyTypes...>::staticTypeID_ = std::numeric_limits<id_t>::max();
 
 } // namespace pe
 }  // namespace walberla
diff --git a/src/pe/rigidbody/UnionFactory.h b/src/pe/rigidbody/UnionFactory.h
index d4b4b98b8..62fe5828e 100644
--- a/src/pe/rigidbody/UnionFactory.h
+++ b/src/pe/rigidbody/UnionFactory.h
@@ -50,7 +50,7 @@ namespace pe {
  * \ingroup pe
  * \brief Setup of a new Union.
  *
- * \tparam BodyTypeTuple std::tuple of all geometries the Union should be able to contain
+ * \tparam BodyTypes all geometries the Union should be able to contain
  * \param globalStorage process local global storage
  * \param blocks storage of all the blocks on this process
  * \param storageID BlockDataID of the BlockStorage block datum
@@ -67,23 +67,23 @@ namespace pe {
  * \snippet PeDocumentationSnippets.cpp Definition of Union Types
  * \snippet PeDocumentationSnippets.cpp Create a Union
  */
-template <typename BodyTypeTuple>
-Union<BodyTypeTuple>* createUnion(   BodyStorage& globalStorage, BlockStorage& blocks, BlockDataID storageID,
+template <typename... BodyTypes>
+Union<BodyTypes...>* createUnion(   BodyStorage& globalStorage, BlockStorage& blocks, BlockDataID storageID,
                                      id_t uid, const Vec3& gpos,
                                      bool global = false, bool communicating = true, bool infiniteMass = false )
 {
-   if (Union<BodyTypeTuple>::getStaticTypeID() == std::numeric_limits<id_t>::max())
+   if (Union<BodyTypes...>::getStaticTypeID() == std::numeric_limits<id_t>::max())
       throw std::runtime_error("Union TypeID not initalized!");
 
-   Union<BodyTypeTuple>* bd = nullptr;
+   Union<BodyTypes...>* bd = nullptr;
 
    if (global)
    {
       const id_t sid = UniqueID<RigidBody>::createGlobal();
       WALBERLA_ASSERT_EQUAL(communicating, false);
       WALBERLA_ASSERT_EQUAL(infiniteMass, true);
-      auto un = std::make_unique<Union<BodyTypeTuple>>(sid, uid, gpos, Vec3(0,0,0), Quat(), global, false, true);
-      bd = static_cast<Union<BodyTypeTuple>*>(&globalStorage.add(std::move(un)));
+      auto un = std::make_unique<Union<BodyTypes...>>(sid, uid, gpos, Vec3(0,0,0), Quat(), global, false, true);
+      bd = static_cast<Union<BodyTypes...>*>(&globalStorage.add(std::move(un)));
    } else
    {
       for (auto& block : blocks){
@@ -92,9 +92,9 @@ Union<BodyTypeTuple>* createUnion(   BodyStorage& globalStorage, BlockStorage& b
             const id_t sid( UniqueID<RigidBody>::create() );
 
             BodyStorage& bs = (*block.getData<Storage>(storageID))[0];
-            auto un = std::make_unique<Union<BodyTypeTuple>>(sid, uid, gpos, Vec3(0,0,0), Quat(), global, communicating, infiniteMass);
+            auto un = std::make_unique<Union<BodyTypes...>>(sid, uid, gpos, Vec3(0,0,0), Quat(), global, communicating, infiniteMass);
             un->MPITrait.setOwner(Owner(MPIManager::instance()->rank(), block.getId().getID()));
-            bd = static_cast<Union<BodyTypeTuple>*>(&bs.add(std::move(un)));
+            bd = static_cast<Union<BodyTypes...>*>(&bs.add(std::move(un)));
          }
       }
    }
@@ -116,15 +116,15 @@ Union<BodyTypeTuple>* createUnion(   BodyStorage& globalStorage, BlockStorage& b
  * \ingroup pe
  * \brief Setup of a new Box directly attached to a Union.
  *
- * \tparam BodyTypeTuple std::tuple of all geometries the Union is able to contain
+ * \tparam BodyTypes all geometries the Union is able to contain
  * \exception std::runtime_error    Box TypeID not initalized!
  * \exception std::invalid_argument createBox: Union argument is NULL
  * \exception std::logic_error      createBox: Union is remote
  * \exception std::invalid_argument Invalid side length
  * \see createBox for more details
  */
-template <typename BodyTypeTuple>
-BoxID createBox( Union<BodyTypeTuple>* un,
+template <typename... BodyTypes>
+BoxID createBox( Union<BodyTypes...>* un,
                  id_t uid, const Vec3& gpos, const Vec3& lengths,
                  MaterialID material = Material::find("iron"),
                  bool global = false, bool communicating = true, bool infiniteMass = false )
@@ -180,7 +180,7 @@ BoxID createBox( Union<BodyTypeTuple>* un,
  * \ingroup pe
  * \brief Setup of a new Capsule directly attached to a Union.
  *
- * \tparam BodyTypeTuple std::tuple of all geometries the Union is able to contain
+ * \tparam BodyTypes all geometries the Union is able to contain
  * \exception std::runtime_error    Capsule TypeID not initalized!
  * \exception std::invalid_argument createCapsule: Union argument is NULL
  * \exception std::logic_error      createCapsule: Union is remote
@@ -189,8 +189,8 @@ BoxID createBox( Union<BodyTypeTuple>* un,
  *
  * \see createCapsule for more details
  */
-template <typename BodyTypeTuple>
-CapsuleID createCapsule( Union<BodyTypeTuple>* un,
+template <typename... BodyTypes>
+CapsuleID createCapsule( Union<BodyTypes...>* un,
                          id_t uid, const Vec3& gpos, const real_t radius, const real_t length,
                          MaterialID material = Material::find("iron"),
                          bool global = false, bool communicating = true, bool infiniteMass = false )
@@ -242,7 +242,7 @@ CapsuleID createCapsule( Union<BodyTypeTuple>* un,
  * \ingroup pe
  * \brief Setup of a new Sphere directly attached to a Union.
  *
- * \tparam BodyTypeTuple std::tuple of all geometries the Union is able to contain
+ * \tparam BodyTypes all geometries the Union is able to contain
  * \exception std::runtime_error    Sphere TypeID not initalized!
  * \exception std::invalid_argument createSphere: Union argument is NULL
  * \exception std::logic_error      createSphere: Union is remote
@@ -250,8 +250,8 @@ CapsuleID createCapsule( Union<BodyTypeTuple>* un,
  *
  * \see createSphere for more details
  */
-template <typename BodyTypeTuple>
-SphereID createSphere( Union<BodyTypeTuple>* un,
+template <typename... BodyTypes>
+SphereID createSphere( Union<BodyTypes...>* un,
                        id_t uid, const Vec3& gpos, real_t radius,
                        MaterialID material = Material::find("iron"),
                        bool global = false, bool communicating = true, bool infiniteMass = false )
diff --git a/src/pe/vtk/SphereVtkOutput.cpp b/src/pe/vtk/SphereVtkOutput.cpp
index 2e68ed497..f02130a72 100644
--- a/src/pe/vtk/SphereVtkOutput.cpp
+++ b/src/pe/vtk/SphereVtkOutput.cpp
@@ -58,36 +58,36 @@ void SphereVtkOutput::configure()
       {
          if (body.getTypeID() == Sphere::getStaticTypeID() || body.getTypeID() == Squirmer::getStaticTypeID())
             bodies_.push_back( static_cast<Sphere const *> (&body) );
-         if (body.getTypeID() == Union<std::tuple<Sphere> >::getStaticTypeID())
+         if (body.getTypeID() == Union<Sphere>::getStaticTypeID())
          {
-            auto un = static_cast<Union<std::tuple<Sphere> > const * > (&body);
+            auto un = static_cast<Union<Sphere> const * > (&body);
             for( auto it2 = un->begin(); it2 != un->end(); ++it2 )
             {
                if (it2->getTypeID() == Sphere::getStaticTypeID())
                   bodies_.push_back( static_cast<ConstSphereID> (it2.getBodyID()) );
             }
          }
-         if (body.getTypeID() == Union<std::tuple<Squirmer> >::getStaticTypeID())
+         if (body.getTypeID() == Union<Squirmer>::getStaticTypeID())
          {
-            auto un = static_cast<Union<std::tuple<Squirmer> > const * > (&body);
+            auto un = static_cast<Union<Squirmer> const * > (&body);
             for( auto it2 = un->begin(); it2 != un->end(); ++it2 )
             {
                if (it2->getTypeID() == Squirmer::getStaticTypeID())
                   bodies_.push_back( static_cast<ConstSphereID> (it2.getBodyID()) );
             }
          }
-         if (body.getTypeID() == Union<std::tuple<Sphere,Squirmer> >::getStaticTypeID())
+         if (body.getTypeID() == Union<Sphere,Squirmer>::getStaticTypeID())
          {
-            auto un = static_cast<Union<std::tuple<Sphere,Squirmer> > const * > (&body);
+            auto un = static_cast<Union<Sphere,Squirmer> const * > (&body);
             for( auto it2 = un->begin(); it2 != un->end(); ++it2 )
             {
                if (it2->getTypeID() == Sphere::getStaticTypeID() || it2->getTypeID() == Squirmer::getStaticTypeID())
                   bodies_.push_back( static_cast<ConstSphereID> (it2.getBodyID()) );
             }
          }
-         if (body.getTypeID() == Union<std::tuple<Squirmer,Sphere> >::getStaticTypeID())
+         if (body.getTypeID() == Union<Squirmer,Sphere>::getStaticTypeID())
          {
-            auto un = static_cast<Union<std::tuple<Squirmer,Sphere> > const * > (&body);
+            auto un = static_cast<Union<Squirmer,Sphere> const * > (&body);
             for( auto it2 = un->begin(); it2 != un->end(); ++it2 )
             {
                if (it2->getTypeID() == Sphere::getStaticTypeID() || it2->getTypeID() == Squirmer::getStaticTypeID())
diff --git a/tests/mesh/MeshMarshalling.cpp b/tests/mesh/MeshMarshalling.cpp
index 8b9222a2b..15e6bd675 100644
--- a/tests/mesh/MeshMarshalling.cpp
+++ b/tests/mesh/MeshMarshalling.cpp
@@ -41,8 +41,7 @@ namespace walberla {
 using namespace walberla::pe;
 using namespace walberla::pe::communication;
 
-using UnionTypeTuple = std::tuple<mesh::pe::ConvexPolyhedron>;
-using UnionT = Union<UnionTypeTuple>;
+using UnionT = Union<mesh::pe::ConvexPolyhedron>;
 using UnionID = UnionT *;
 using UnionPtr = std::unique_ptr<UnionT>;
 
diff --git a/tests/pe/Collision.cpp b/tests/pe/Collision.cpp
index e6d528d5a..76f054583 100644
--- a/tests/pe/Collision.cpp
+++ b/tests/pe/Collision.cpp
@@ -235,7 +235,7 @@ void CapsuleTest2()
 
 void UnionTest()
 {
-   using UnionT = Union<std::tuple<Sphere> >;
+   using UnionT = Union<Sphere>;
    UnionT  un1(120, 0, Vec3(0,0,0), Vec3(0,0,0), Quat(), false, true, false);
    UnionT  un2(121, 0, Vec3(real_t(1.5),0,0), Vec3(0,0,0), Quat(), false, true, false);
    auto sp1 = createSphere(&un1, 123, Vec3(0,0,0), 1);
diff --git a/tests/pe/CollisionTobiasGJK.cpp b/tests/pe/CollisionTobiasGJK.cpp
index fb93470af..6473222d0 100644
--- a/tests/pe/CollisionTobiasGJK.cpp
+++ b/tests/pe/CollisionTobiasGJK.cpp
@@ -49,7 +49,7 @@
 namespace walberla {
 using namespace walberla::pe;
 
-typedef std::tuple<Box, Capsule, Plane, Sphere, Union<std::tuple<Sphere>>, Union<std::tuple<Sphere, Union<std::tuple<Sphere>>>>, Ellipsoid> BodyTuple ;
+typedef std::tuple<Box, Capsule, Plane, Sphere, Union<Sphere>, Union<Sphere, Union<Sphere>>, Ellipsoid> BodyTuple ;
 
 bool gjkEPAcollideHybrid(GeomPrimitive &geom1, GeomPrimitive &geom2, Vec3& normal, Vec3& contactPoint, real_t& penetrationDepth)
 {
@@ -356,20 +356,20 @@ void UnionTest(){
    Box box(179, 179, Vec3(0,0,0), Vec3(0,0,0), Quat(), Vec3(real_t(10),real_t(2), real_t(10)), iron, false, true, false);
 
 
-   using UnionT = Union<std::tuple<Sphere>>;
+   using UnionT = Union<Sphere>;
    auto unsub = std::make_unique<UnionT>(192, 192, Vec3(0,real_t(3.8),0), Vec3(0,0,0), Quat(), false, true, false);
 
    auto sp1 = createSphere(unsub.get(), 180, Vec3(-3,real_t(3.8),0), real_t(3.0));
    auto sp2 = createSphere(unsub.get(), 181, Vec3(3,real_t(3.8),0), real_t(3.0));
 
    //Create another union, and add sub union
-   Union<std::tuple<Sphere, Union<std::tuple<Sphere>>>> un(193, 193, Vec3(0, 0, 0), Vec3(0,0,0), Quat(), false, true, false);
+   Union<Sphere, Union<Sphere>> un(193, 193, Vec3(0, 0, 0), Vec3(0,0,0), Quat(), false, true, false);
    createSphere(&un, 182, Vec3(0,real_t(6),0), real_t(3.0));
    un.add(std::move(unsub));
 
 
    PossibleContacts pcs;
-   pcs.push_back(std::pair<Union<std::tuple<Sphere,Union<std::tuple<Sphere>>>>*, Box*>(&un, &box));
+   pcs.push_back(std::pair<Union<Sphere,Union<Sphere>>*, Box*>(&un, &box));
    Contacts& container = testFCD.generateContacts(pcs);
    WALBERLA_CHECK(container.size() == 2);
 
@@ -397,7 +397,7 @@ void UnionTest(){
    pcs.clear();
 
    //Vice Versa
-   pcs.push_back(std::pair<Box*, Union<std::tuple<Sphere, Union<std::tuple<Sphere>>>>* >(&box, &un));
+   pcs.push_back(std::pair<Box*, Union<Sphere, Union<Sphere>>* >(&box, &un));
    container = testFCD.generateContacts(pcs);
    WALBERLA_CHECK(container.size() == 2);
 
diff --git a/tests/pe/Marshalling.cpp b/tests/pe/Marshalling.cpp
index ed1fdce61..fc18f19eb 100644
--- a/tests/pe/Marshalling.cpp
+++ b/tests/pe/Marshalling.cpp
@@ -38,8 +38,7 @@ namespace walberla {
 using namespace walberla::pe;
 using namespace walberla::pe::communication;
 
-using UnionTypeTuple = std::tuple<Sphere>;
-using UnionT = Union<UnionTypeTuple>;
+using UnionT = Union<Sphere>;
 using UnionID = UnionT *;
 using UnionPtr = std::unique_ptr<UnionT>;
 
@@ -171,8 +170,8 @@ void testUnion()
 {
    WALBERLA_LOG_INFO_ON_ROOT("*** testUnion ***");
    UnionT u1(159, 423, Vec3(real_c(1), real_c(2), real_c(3)), Vec3(0,0,0), Quat(), false, false, false);
-   SphereID s11 = createSphere< UnionTypeTuple >(&u1, 1234794, Vec3(real_c(1), real_c(2), real_c(3)), 2);
-   SphereID s21 = createSphere< UnionTypeTuple >(&u1, 4567789, Vec3(real_c(3), real_c(2), real_c(3)), real_c(1.5));
+   SphereID s11 = createSphere(&u1, 1234794, Vec3(real_c(1), real_c(2), real_c(3)), 2);
+   SphereID s21 = createSphere(&u1, 4567789, Vec3(real_c(3), real_c(2), real_c(3)), real_c(1.5));
    WALBERLA_CHECK_NOT_NULLPTR( s11 );
    WALBERLA_CHECK_NOT_NULLPTR( s21 );
 
diff --git a/tests/pe/PeDocumentationSnippets.cpp b/tests/pe/PeDocumentationSnippets.cpp
index aed806bf5..eb667d526 100644
--- a/tests/pe/PeDocumentationSnippets.cpp
+++ b/tests/pe/PeDocumentationSnippets.cpp
@@ -47,8 +47,7 @@ namespace walberla {
 using namespace walberla::pe;
 
 //! [Definition of Union Types]
-typedef std::tuple<Box, Capsule, Sphere>  UnionTypeTuple;
-using UnionT = Union<UnionTypeTuple>;
+using UnionT = Union<Box, Capsule, Sphere>;
 using UnionID = UnionT *;
 //! [Definition of Union Types]
 
@@ -123,7 +122,7 @@ int main( int argc, char ** argv )
 
    //! [Create a Union]
    // Create a union and add a box, capsule and sphere.
-   UnionID un = createUnion<UnionTypeTuple>( *globalBodyStorage, forest->getBlockStorage(), storageID, 1, Vec3(2,3,4) );
+   UnionID un = createUnion<Box, Capsule, Sphere>( *globalBodyStorage, forest->getBlockStorage(), storageID, 1, Vec3(2,3,4) );
    if (un != nullptr)
    {
       createBox    ( un, 1, Vec3(2,3,4), Vec3(2.5,2.5,2.5) );
diff --git a/tests/pe/SetBodyTypeIDs.cpp b/tests/pe/SetBodyTypeIDs.cpp
index 72dc3af90..21e2fcd5c 100644
--- a/tests/pe/SetBodyTypeIDs.cpp
+++ b/tests/pe/SetBodyTypeIDs.cpp
@@ -125,7 +125,7 @@ int main( int argc, char** argv )
    WALBERLA_CHECK_UNEQUAL(A::getStaticTypeID(), C::getStaticTypeID());
    WALBERLA_CHECK_UNEQUAL(B::getStaticTypeID(), C::getStaticTypeID());
 
-   typedef std::tuple<Plane, Sphere, Box, Capsule, Union< std::tuple<Sphere, Box> > > BodyTuple2 ;
+   typedef std::tuple<Plane, Sphere, Box, Capsule, Union<Sphere, Box> > BodyTuple2 ;
    SetBodyTypeIDs<BodyTuple2>::execute();
    WALBERLA_CHECK_UNEQUAL(Plane::getStaticTypeID(), Sphere::getStaticTypeID());
    WALBERLA_CHECK_UNEQUAL(Plane::getStaticTypeID(), Box::getStaticTypeID());
diff --git a/tests/pe/ShadowCopy.cpp b/tests/pe/ShadowCopy.cpp
index ff5ff3940..dcbc58559 100644
--- a/tests/pe/ShadowCopy.cpp
+++ b/tests/pe/ShadowCopy.cpp
@@ -34,7 +34,7 @@
 namespace walberla {
 using namespace walberla::pe;
 
-using UnionT = Union<std::tuple<Sphere> >;
+using UnionT = Union<Sphere>;
 typedef std::tuple<Sphere, UnionT> BodyTuple ;
 
 int main( int argc, char** argv )
@@ -126,7 +126,7 @@ int main( int argc, char** argv )
    destroyBodyBySID( *globalBodyStorage, forest->getBlockStorage(), storageID, sid );
 
    WALBERLA_LOG_PROGRESS_ON_ROOT( " *** UNION *** ");
-   UnionT* un   = createUnion< std::tuple<Sphere> >( *globalBodyStorage, forest->getBlockStorage(), storageID, 0, Vec3(2,2,2) );
+   UnionT* un   = createUnion<Sphere>( *globalBodyStorage, forest->getBlockStorage(), storageID, 0, Vec3(2,2,2) );
    auto sp1 = createSphere(un, 10, Vec3(real_t(4.9),2,2), real_t(1));
    auto sp2 = createSphere(un, 11, Vec3(3,2,2), real_t(1.5));
    un->setPosition( Vec3( real_t(4.9), 2, 2) );
diff --git a/tests/pe/Union.cpp b/tests/pe/Union.cpp
index ce0ea5e96..49fd6e66b 100644
--- a/tests/pe/Union.cpp
+++ b/tests/pe/Union.cpp
@@ -44,7 +44,7 @@
 namespace walberla {
 using namespace walberla::pe;
 
-using UnionType = Union<std::tuple<Sphere> > ;
+using UnionType = Union<Sphere> ;
 typedef std::tuple<Sphere, Plane, UnionType> BodyTuple ;
 
 void SnowManFallingOnPlane()
@@ -75,7 +75,7 @@ void SnowManFallingOnPlane()
 
    createPlane( *globalBodyStorage, 0, Vec3(0,0,1), Vec3(0,0,0) );
 
-   UnionType* un  = createUnion< std::tuple<Sphere> >( *globalBodyStorage, forest->getBlockStorage(), storageID, 0, Vec3(5,5,5) );
+   UnionType* un  = createUnion<Sphere>( *globalBodyStorage, forest->getBlockStorage(), storageID, 0, Vec3(5,5,5) );
    auto sp1       = createSphere(un, 10, Vec3(5,5,1), real_t(1));
    auto sp2       = createSphere(un, 11, Vec3(real_t(6.7),5,real_t(1.2)), real_t(1.1));
 
-- 
GitLab