diff --git a/src/pe/Types.h b/src/pe/Types.h index d37661d54fb2d76466a4ae9b3f4cc8fe608456c2..35d559f4f7c19d81a0ba36d04482a9d7639c7c43 100644 --- a/src/pe/Types.h +++ b/src/pe/Types.h @@ -65,6 +65,7 @@ class Box; class Capsule; class Contact; class Cylinder; +class CylindricalBoundary; class FixedJoint; class ForceGenerator; class GeomPrimitive; @@ -106,6 +107,10 @@ typedef Cylinder CylinderType; //!< Type of the cylinder geo typedef Cylinder* CylinderID; //!< Handle for a cylinder primitive. typedef const Cylinder* ConstCylinderID; //!< Handle for a constant cylinder primitive. +typedef CylindricalBoundary CylindricalBoundaryType; //!< Type of the cylindrical boundary geometric primitive. +typedef CylindricalBoundary* CylindricalBoundaryID; //!< Handle for a cylindrical boundary primitive. +typedef const CylindricalBoundary* ConstCylindricalBoundaryID; //!< Handle for a constant cylindrical boundary primitive. + typedef Plane PlaneType; //!< Type of the plane geometric primitive. typedef Plane* PlaneID; //!< Handle for a plane primitive. typedef const Plane* ConstPlaneID; //!< Handle for a constant plane primitive. diff --git a/src/pe/basic.h b/src/pe/basic.h index 25bf84ca03855d3dce0a22e1099dc0860d6cfce5..70bbdf4426bbaeb38d75c05fb89da6bea1c7bc9a 100644 --- a/src/pe/basic.h +++ b/src/pe/basic.h @@ -40,6 +40,7 @@ #include "pe/rigidbody/BodyIterators.h" #include "pe/rigidbody/BoxFactory.h" #include "pe/rigidbody/CapsuleFactory.h" +#include "pe/rigidbody/CylindricalBoundaryFactory.h" #include "pe/rigidbody/PlaneFactory.h" #include "pe/rigidbody/SphereFactory.h" #include "pe/rigidbody/UnionFactory.h" diff --git a/src/pe/fcd/AnalyticCollisionDetection.h b/src/pe/fcd/AnalyticCollisionDetection.h index 4777794366f6d6cd9eda691b45b187785ed1da01..8b6fa28538b785034d8a1e94bf371f6940aeb403 100644 --- a/src/pe/fcd/AnalyticCollisionDetection.h +++ b/src/pe/fcd/AnalyticCollisionDetection.h @@ -26,6 +26,7 @@ #include "pe/contact/Contact.h" #include "pe/rigidbody/Box.h" #include "pe/rigidbody/Capsule.h" +#include "pe/rigidbody/CylindricalBoundary.h" #include "pe/rigidbody/Plane.h" #include "pe/rigidbody/Sphere.h" #include "pe/rigidbody/Union.h" @@ -54,34 +55,42 @@ bool collide( SphereID s1, SphereID s2, Container& container ); template <typename Container> inline bool collide( SphereID s, PlaneID p, Container& container ); - template <typename Container> inline bool collide( PlaneID p, SphereID s, Container& container ); + template <typename Container> inline -bool collide( SphereID s, BoxID b, Container& container ); +bool collide( SphereID s, CylindricalBoundaryID cb, Container& container ); +template <typename Container> +inline +bool collide( CylindricalBoundaryID cb, SphereID s, Container& container ); +template <typename Container> +inline +bool collide( SphereID s, BoxID b, Container& container ); template <typename Container> inline bool collide( BoxID b, SphereID s, Container& container ); + template <typename Container> inline bool collide( BoxID b1, BoxID b2, Container& container ); + template <typename Container> inline bool collide( BoxID b, PlaneID p, Container& container ); - template <typename Container> inline bool collide( PlaneID p, BoxID b, Container& container ); + template <typename Container> inline bool collide( CapsuleID c1, CapsuleID c2, Container& container ); + template <typename Container> inline bool collide( CapsuleID c, PlaneID p, Container& container ); - template <typename Container> inline bool collide( PlaneID p, CapsuleID c, Container& container ); @@ -89,7 +98,6 @@ bool collide( PlaneID p, CapsuleID c, Container& container ); template <typename Container> inline bool collide( SphereID s, CapsuleID c, Container& container ); - template <typename Container> inline bool collide( CapsuleID c, SphereID s, Container& container ); @@ -98,7 +106,6 @@ bool collide( CapsuleID c, SphereID s, Container& container ); template <typename Container> inline bool collide( BoxID b, CapsuleID c, Container& container ); - template <typename Container> inline bool collide( CapsuleID c, BoxID b, Container& container ); @@ -219,6 +226,35 @@ bool collide( PlaneID p, SphereID s, Container& container ) return collide(s, p, container); } +template <typename Container> +inline +bool collide( SphereID s, CylindricalBoundaryID cb, Container& container ) +{ + WALBERLA_ASSERT_GREATER( cb->getRadius(), s->getRadius() ); + + const Vec3 dist = (s->getPosition() - cb->getPosition()) - ((s->getPosition() - cb->getPosition()) * cb->getAxis()) * cb->getAxis(); + const real_t effRadius = cb->getRadius() - s->getRadius(); + if( effRadius * effRadius - dist.sqrLength() < contactThreshold ) { + const Vec3 contactNormal = -dist.getNormalized(); + const real_t penetrationDepth = effRadius - dist.length(); + const Vec3 contactPoint = ( s->getPosition() - ( s->getRadius() + penetrationDepth ) * contactNormal ); + + WALBERLA_LOG_DETAIL( " Contact created between sphere " << s->getID() + << " and cylindrical boundary " << cb->getID() << " (dist=" << penetrationDepth << ")"); + + container.push_back( Contact(s, cb, contactPoint, contactNormal, penetrationDepth) ); + return true; + } + return false; +} + +template <typename Container> +inline +bool collide( CylindricalBoundaryID cb, SphereID s, Container& container ) +{ + return collide(s, cb, container); +} + //************************************************************************************************* /*!\brief Contact generation between a Sphere and a Box. * diff --git a/src/pe/rigidbody/CylindricalBoundary.cpp b/src/pe/rigidbody/CylindricalBoundary.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1a96385517c916ce10aafe20132d840269aa5178 --- /dev/null +++ b/src/pe/rigidbody/CylindricalBoundary.cpp @@ -0,0 +1,239 @@ +//====================================================================================================================== +// +// 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 CylindricalBoundary.cpp +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +#include "CylindricalBoundary.h" + +//************************************************************************************************* +// Includes +//************************************************************************************************* + +#include <iomanip> +#include <iostream> +#include <stdexcept> +#include "core/math/Shims.h" +#include <pe/Materials.h> +#include <core/math/Matrix3.h> +#include <core/math/Limits.h> +#include <core/debug/Debug.h> + +namespace walberla { +namespace pe { + +//================================================================================================= +// +// CONSTRUCTOR +// +//================================================================================================= + +//************************************************************************************************* +/*!\brief Constructor for the CylindricalBoundary class. + * + * \param sid Unique system-specific ID for the cylindrical boundary. + * \param uid User-specific ID for the cylindrical boundary. + * \param gpos Global geometric center of the cylindrical boundary. + * \param radius The radius of the cylinder. + * \param material The material of the cylindrical boundary. + * + * The cylindrical boundary is created lying along the x-axis. + */ +CylindricalBoundary::CylindricalBoundary( id_t sid, id_t uid, const Vec3& gpos, const real_t radius, + MaterialID material ) + : GeomPrimitive( getStaticTypeID(), sid, uid, material ) // Initializing the base object + , radius_(radius) // Radius of the cylinder // Length of the capsule +{ + //boundaries are always considered locally and have infinite mass + setGlobal( true ); + setCommunicating( false ); + setMass( true ); + setFinite( false ); + + // Checking the radius + // Since the constructor is never directly called but only used in a small number + // of functions that already check the capsule arguments, only asserts are used here to + // double check the arguments. + WALBERLA_ASSERT_GREATER( radius, real_t(0), "Invalid capsule radius" ); + + // Initializing the instantiated capsule + gpos_ = gpos; + q_ = Quat(); // Setting the orientation + R_ = q_.toRotationMatrix(); // Setting the rotation matrix + + // Setting the axis-aligned bounding box + CylindricalBoundary::calcBoundingBox(); +} +//************************************************************************************************* + + + + +//************************************************************************************************* +/*!\brief Destructor for the cylindrical boundary class. + */ +CylindricalBoundary::~CylindricalBoundary() +{ + WALBERLA_LOG_DETAIL( "Destroyed CylindricalBoundary " << sid_ ); +} +//************************************************************************************************* + + + + +//================================================================================================= +// +// UTILITY FUNCTIONS +// +//================================================================================================= + +//************************************************************************************************* +/*!\brief Checks, whether a point in body relative coordinates lies inside the cylindrical boundary. + * + * \param px The x-component of the relative coordinate. + * \param py The y-component of the relative coordinate. + * \param pz The z-component of the relative coordinate. + * \return \a true if the point lies inside the cylindrical boundary, \a false if not. + */ +bool CylindricalBoundary::containsRelPointImpl( real_t /*px*/, real_t py, real_t pz ) const +{ + return ( math::sq(py) + math::sq(pz) ) >= ( radius_ * radius_ ); +} +//************************************************************************************************* + + +//************************************************************************************************* +/*!\brief Checks, whether a point in body relative coordinates lies on the surface of the cylindrical boundary. + * + * \param px The x-component of the relative coordinate. + * \param py The y-component of the relative coordinate. + * \param pz The z-component of the relative coordinate. + * \return \a true if the point lies on the surface of the cylindrical boundary, \a false if not. + * + * The tolerance level of the check is pe::surfaceThreshold. + */ +bool CylindricalBoundary::isSurfaceRelPointImpl( real_t /*px*/, real_t py, real_t pz ) const +{ + return ( std::fabs( ( math::sq(py) + math::sq(pz) ) - ( radius_ * radius_ ) ) <= surfaceThreshold*surfaceThreshold ); +} +//************************************************************************************************* + +//************************************************************************************************* +/*!\brief Calculation of the bounding box of the cylindrical boundary. + * + * \return (-inf, -inf, -inf, +inf, +inf, +inf ) + */ +void CylindricalBoundary::calcBoundingBox() +{ + aabb_ = math::AABB( + -math::Limits<real_t>::inf(), + -math::Limits<real_t>::inf(), + -math::Limits<real_t>::inf(), + +math::Limits<real_t>::inf(), + +math::Limits<real_t>::inf(), + +math::Limits<real_t>::inf()); + + WALBERLA_ASSERT( aabb_.contains( gpos_ ), "Invalid bounding box detected" ); +} +//************************************************************************************************* + + +//================================================================================================= +// +// OUTPUT FUNCTIONS +// +//================================================================================================= + +//************************************************************************************************* +/*!\brief Output of the current state of a cylindrical boundary. + * + * \param os Reference to the output stream. + * \param tab Indentation in front of every line of the cylindrical boundary output. + * \return void + */ +void CylindricalBoundary::print( std::ostream& os, const char* tab ) const +{ + using std::setw; + + os << tab << " Cylindrical Boundary " << getID() << " with radius " << getRadius() << "\n"; + + os << tab << " Fixed: " << isFixed() << " , sleeping: " << !isAwake() << "\n"; + + os << tab << " System ID = " << getSystemID() << "\n" + << tab << " Total mass = " << getMass() << "\n" + << tab << " Material = " << Material::getName( material_ ) << "\n" + << tab << " Global position = " << getPosition() << "\n" + << tab << " Linear velocity = " << getLinearVel() << "\n" + << tab << " Angular velocity = " << getAngularVel() << "\n"; + + os << tab << " Bounding box = " << getAABB() << "\n" + << tab << " Quaternion = " << getQuaternion() << "\n" + << tab << " Rotation matrix = ( " << setw(9) << R_[0] << " , " << setw(9) << R_[1] << " , " << setw(9) << R_[2] << " )\n" + << tab << " ( " << setw(9) << R_[3] << " , " << setw(9) << R_[4] << " , " << setw(9) << R_[5] << " )\n" + << tab << " ( " << setw(9) << R_[6] << " , " << setw(9) << R_[7] << " , " << setw(9) << R_[8] << " )\n"; +} +//************************************************************************************************* + +//================================================================================================= +// +// GLOBAL OPERATORS +// +//================================================================================================= + +//************************************************************************************************* +/*!\brief Global output operator for cylindrical boundaryies. + * + * \param os Reference to the output stream. + * \param c Reference to a cylindrical boundary object. + * \return Reference to the output stream. + */ +std::ostream& operator<<( std::ostream& os, const CylindricalBoundary& c ) +{ + os << "--" << "CYLINDRICAL BOUNDARY PARAMETERS" + << "------------------------------------------------------------\n"; + c.print( os, "" ); + os << "--------------------------------------------------------------------------------\n" + << std::endl; + return os; +} +//************************************************************************************************* + + +//************************************************************************************************* +/*!\brief Global output operator for cylindrical boundaryies handles. + * + * \param os Reference to the output stream. + * \param c Constant cylindrical boundary handle. + * \return Reference to the output stream. + */ +std::ostream& operator<<( std::ostream& os, CylindricalBoundaryID c ) +{ + os << "--" << "CYLINDRICAL BOUNDARY PARAMETERS" + << "------------------------------------------------------------\n"; + c->print( os, "" ); + os << "--------------------------------------------------------------------------------\n" + << std::endl; + return os; +} +//************************************************************************************************* + +id_t CylindricalBoundary::staticTypeID_ = std::numeric_limits<id_t>::max(); + +} // namespace pe +} // namespace walberla + + diff --git a/src/pe/rigidbody/CylindricalBoundary.h b/src/pe/rigidbody/CylindricalBoundary.h new file mode 100644 index 0000000000000000000000000000000000000000..4c095c1a20bae85959c756a856500ec7b4332b93 --- /dev/null +++ b/src/pe/rigidbody/CylindricalBoundary.h @@ -0,0 +1,186 @@ +//====================================================================================================================== +// +// 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 CylindricalBoundary.h +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +#pragma once + + +//************************************************************************************************* +// Includes +//************************************************************************************************* + +#include <pe/rigidbody/GeomPrimitive.h> +#include <pe/Types.h> +#include <core/math/Constants.h> +#include <core/math/Matrix3.h> +#include <core/math/Vector3.h> +#include <core/DataTypes.h> +#include <core/logging/Logging.h> +#include <pe/Config.h> + + +namespace walberla { +namespace pe { + +//================================================================================================= +// +// CLASS DEFINITION +// +//================================================================================================= + +//************************************************************************************************* +/** + * \ingroup pe + */ +class CylindricalBoundary : public GeomPrimitive +{ +public: + //**Constructors******************************************************************************** + /*!\name Constructors */ + //@{ + explicit CylindricalBoundary( id_t sid, id_t uid, const Vec3& gpos, const real_t radius, + MaterialID material ); + //@} + //********************************************************************************************** + + //**Destructor********************************************************************************** + /*!\name Destructor */ + //@{ + virtual ~CylindricalBoundary(); + //@} + //********************************************************************************************** + +public: + //**Get functions******************************************************************************* + /*!\name Get functions */ + //@{ + inline real_t getRadius() const; + inline Vec3 getAxis() const; + //@} + //********************************************************************************************** + + //**Utility functions*************************************************************************** + /*!\name Utility functions */ + //@{ + static inline id_t getStaticTypeID(); + //@} + //********************************************************************************************** + + //**Output functions**************************************************************************** + /*!\name Output functions */ + //@{ + virtual void print( std::ostream& os, const char* tab ) const; + //@} + //********************************************************************************************** + +protected: + //**Utility functions*************************************************************************** + /*!\name Utility functions */ + //@{ + virtual bool containsRelPointImpl ( real_t px, real_t py, real_t pz ) const; + virtual bool isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const; + + virtual void calcBoundingBox(); // Calculation of the axis-aligned bounding box + //@} + //********************************************************************************************** + + //**Member variables**************************************************************************** + /*!\name Member variables */ + //@{ + real_t radius_; //!< The radius of the cylinder. + //@} + //********************************************************************************************** + +private: + static id_t staticTypeID_; //< type id of sphere, will be set by SetBodyTypeIDs + static void setStaticTypeID(id_t typeID) {staticTypeID_ = typeID;} + + //** friend declaration + /// needed to be able to set static type ids with setStaticTypeID + template <class T> + friend struct SetBodyTypeIDs; +}; +//************************************************************************************************* + + + +//================================================================================================= +// +// GET FUNCTIONS +// +//================================================================================================= + +//************************************************************************************************* +/*!\brief Returns the radius of the cylinder. + * + * \return The radius of the cylinder. + */ +inline real_t CylindricalBoundary::getRadius() const +{ + return radius_; +} +//************************************************************************************************* + +//************************************************************************************************* +/*!\brief Returns the main axis of the cylinder. + * + * \return The main axis of the cylinder. + */ +inline Vec3 CylindricalBoundary::getAxis() const +{ + return getRotation() * Vec3(1,0,0); +} +//************************************************************************************************* + + +//================================================================================================= +// +// UTILITY FUNCTIONS +// +//================================================================================================= + +//************************************************************************************************* +/*!\brief Returns unique type id of this type + * + * \return geometry specific type id + */ +inline id_t CylindricalBoundary::getStaticTypeID() +{ + return staticTypeID_; +} + + + + +//================================================================================================= +// +// GLOBAL OPERATORS +// +//================================================================================================= + +//************************************************************************************************* +/*!\name Capsule operators */ +//@{ +std::ostream& operator<<( std::ostream& os, const CylindricalBoundary& cb ); +std::ostream& operator<<( std::ostream& os, CylindricalBoundaryID cb ); +//@} +//************************************************************************************************* + +} // namespace pe +} // namespace walberla diff --git a/src/pe/rigidbody/CylindricalBoundaryFactory.cpp b/src/pe/rigidbody/CylindricalBoundaryFactory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a82ea0c684ad7dbc7f60c2d02b99fb7eb6ded2f2 --- /dev/null +++ b/src/pe/rigidbody/CylindricalBoundaryFactory.cpp @@ -0,0 +1,51 @@ +//====================================================================================================================== +// +// 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 CylindricalBoundaryFactory.cpp +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +#include "CylindricalBoundaryFactory.h" + +#include "pe/Materials.h" +#include "pe/rigidbody/BodyStorage.h" +#include "pe/rigidbody/CylindricalBoundary.h" + +#include "core/logging/Logging.h" + +namespace walberla { +namespace pe { + +CylindricalBoundaryID createCylindricalBoundary( BodyStorage& globalStorage, + id_t uid, const Vec3& gpos, const real_t radius, + MaterialID material) +{ + WALBERLA_ASSERT_UNEQUAL( CylindricalBoundary::getStaticTypeID(), std::numeric_limits<id_t>::max(), "CylindricalBoundary TypeID not initalized!"); + + const id_t sid( UniqueID<RigidBody>::createGlobal() ); + + CylindricalBoundaryID cb = new CylindricalBoundary( sid, uid, gpos, radius, material ); + + globalStorage.add(cb); + + // Logging the successful creation of the plane + WALBERLA_LOG_DETAIL( "Created cylindrical boundary " << sid << "\n" << cb); + + return cb; +} + +} // namespace pe +} // namespace walberla diff --git a/src/pe/rigidbody/CylindricalBoundaryFactory.h b/src/pe/rigidbody/CylindricalBoundaryFactory.h new file mode 100644 index 0000000000000000000000000000000000000000..9d516f4789cd09dbaf292b21d2856be7de993bba --- /dev/null +++ b/src/pe/rigidbody/CylindricalBoundaryFactory.h @@ -0,0 +1,56 @@ +//====================================================================================================================== +// +// 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 CylindricalBoundaryFactory.h +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +#pragma once + +#include "pe/rigidbody/BodyStorage.h" +#include "pe/Materials.h" +#include "pe/Types.h" + +#include "core/debug/Debug.h" + +namespace walberla { +namespace pe { + +//================================================================================================= +// +// CYLINDRICAL BOUNDARY SETUP FUNCTIONS +// +//================================================================================================= + +//************************************************************************************************* +/** + * \ingroup pe + * \brief Setup of a new Cylindrical Boundary. + * + * \param globalStorage process local global storage + * \param uid The user-specific ID. + * \param gpos One point located on the central axis. + * \param radius radius of the cylinder + * \param material The material of the boundary. + * \return Handle for the new boundary. + */ +CylindricalBoundaryID createCylindricalBoundary( BodyStorage& globalStorage, + id_t uid, const Vec3& gpos, const real_t radius, + MaterialID material = Material::find("iron")); +//************************************************************************************************* + +} // namespace pe +} // namespace walberla diff --git a/src/pe_coupling/mapping/BodyBBMapping.cpp b/src/pe_coupling/mapping/BodyBBMapping.cpp index 070434f31e6dfc97476ecaa7d4bdc79e197f46d5..9defb26ecd2fc688ff0432903e38dda314691a16 100644 --- a/src/pe_coupling/mapping/BodyBBMapping.cpp +++ b/src/pe_coupling/mapping/BodyBBMapping.cpp @@ -33,8 +33,13 @@ CellInterval getCellBB( const pe::ConstBodyID body, const IBlock & block, const { CellInterval cellBB; - const auto & aabb = body->getAABB(); - blockStorage->getCellBBFromAABB( cellBB, aabb, blockStorage->getLevel(block) ); + if (body->isFinite()) + { + blockStorage->getCellBBFromAABB( cellBB, body->getAABB(), blockStorage->getLevel(block) ); + } else + { + blockStorage->getCellBBFromAABB( cellBB, body->getAABB().getIntersection( blockStorage->getDomain() ), blockStorage->getLevel(block) ); + } cellBB.xMin() -= cell_idx_c(1); cellBB.yMin() -= cell_idx_c(1); cellBB.zMin() -= cell_idx_c(1); cellBB.xMax() += cell_idx_c(1); cellBB.yMax() += cell_idx_c(1); cellBB.zMax() += cell_idx_c(1); diff --git a/src/pe_coupling/mapping/BodyMapping.h b/src/pe_coupling/mapping/BodyMapping.h index 8d6bfc09a6f4137bb6595dc2d0440b51d1b986b8..6df7a1341381f868b6c6d46e6134fa48f7fc1f81 100644 --- a/src/pe_coupling/mapping/BodyMapping.h +++ b/src/pe_coupling/mapping/BodyMapping.h @@ -48,7 +48,7 @@ void mapBody( const pe::BodyID & body, IBlock & block, const shared_ptr<Structur WALBERLA_UNUSED(moBodiesOnly); // undo when other coupling algorithms are available - if( ( fixedBodiesOnly && !body->isFixed() ) || !body->isFinite() /*|| ( moBodiesOnly && !isMOBody( body ) )*/ ) + if( ( fixedBodiesOnly && !body->isFixed() ) /*|| ( moBodiesOnly && !isMOBody( body ) )*/ ) return; BoundaryHandling_T * boundaryHandling = block.getData< BoundaryHandling_T >( boundaryHandlingID ); @@ -108,7 +108,24 @@ void mapGlobalBodies( const shared_ptr<StructuredBlockStorage> & blockStorage, c for( auto blockIt = blockStorage->begin(); blockIt != blockStorage->end(); ++blockIt ) { for( auto bodyIt = globalBodyStorage.begin(); bodyIt != globalBodyStorage.end(); ++bodyIt) + { + mapBody< BoundaryHandling_T >( *bodyIt, *blockIt, blockStorage, boundaryHandlingID, obstacle, fixedBodiesOnly, moBodiesOnly ); + } + } +} + +template< typename BoundaryHandling_T > +void mapGlobalBody( const id_t globalBodySystemID, + const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & boundaryHandlingID, pe::BodyStorage & globalBodyStorage, const FlagUID & obstacle, + const bool fixedBodiesOnly = true, const bool moBodiesOnly = true ) +{ + for( auto blockIt = blockStorage->begin(); blockIt != blockStorage->end(); ++blockIt ) + { + auto bodyIt = globalBodyStorage.find( globalBodySystemID ); + if( bodyIt != globalBodyStorage.end() ) + { mapBody< BoundaryHandling_T >( *bodyIt, *blockIt, blockStorage, boundaryHandlingID, obstacle, fixedBodiesOnly, moBodiesOnly ); + } } } diff --git a/src/pe_coupling/momentum_exchange_method/BodyMapping.h b/src/pe_coupling/momentum_exchange_method/BodyMapping.h index 4170551cb8a985d286006df571f30c521b927184..732b31fe67bc483c3a6e52ad6daf85560c3fc726 100644 --- a/src/pe_coupling/momentum_exchange_method/BodyMapping.h +++ b/src/pe_coupling/momentum_exchange_method/BodyMapping.h @@ -181,7 +181,7 @@ void mapMovingBody( const pe::BodyID body, IBlock & block, const shared_ptr<Stru WALBERLA_ASSERT_EQUAL( &block.getBlockStorage(), &(blockStorage->getBlockStorage()) ); - if( body->isFixed() || !body->isFinite() ) + if( body->isFixed() /*|| !body->isFinite()*/ ) return; BoundaryHandling_T * boundaryHandling = block.getData< BoundaryHandling_T >( boundaryHandlingID ); @@ -240,6 +240,33 @@ void mapMovingBodies( const shared_ptr<StructuredBlockStorage> & blockStorage, c } } +template< typename BoundaryHandling_T > +void mapMovingGlobalBodies( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & boundaryHandlingID, pe::BodyStorage & globalBodyStorage, + const BlockDataID & bodyFieldID, const FlagUID & obstacle ) +{ + for( auto blockIt = blockStorage->begin(); blockIt != blockStorage->end(); ++blockIt ) + { + for( auto bodyIt = globalBodyStorage.begin(); bodyIt != globalBodyStorage.end(); ++bodyIt) + { + mapMovingBody< BoundaryHandling_T >( *bodyIt, *blockIt, blockStorage, boundaryHandlingID, bodyFieldID, obstacle ); + } + } +} + +template< typename BoundaryHandling_T > +void mapMovingGlobalBody( const id_t globalBodySystemID, + const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & boundaryHandlingID, pe::BodyStorage & globalBodyStorage, + const BlockDataID & bodyFieldID, const FlagUID & obstacle ) +{ + for( auto blockIt = blockStorage->begin(); blockIt != blockStorage->end(); ++blockIt ) + { + auto bodyIt = globalBodyStorage.find( globalBodySystemID ); + if( bodyIt != globalBodyStorage.end() ) + { + mapMovingBody< BoundaryHandling_T >( *bodyIt, *blockIt, blockStorage, boundaryHandlingID, bodyFieldID, obstacle ); + } + } +} } // namespace pe_coupling diff --git a/src/pe_coupling/momentum_exchange_method/boundary/SimpleBB.h b/src/pe_coupling/momentum_exchange_method/boundary/SimpleBB.h index 161dfc872e1094df917d2394048790afdf52da92..0c7d3aae8c5b7352eb6426ef87d54e0986f54e1b 100644 --- a/src/pe_coupling/momentum_exchange_method/boundary/SimpleBB.h +++ b/src/pe_coupling/momentum_exchange_method/boundary/SimpleBB.h @@ -142,7 +142,7 @@ inline void SimpleBB< LatticeModel_T, FlagField_T >::treatDirection( const cell_ WALBERLA_ASSERT_UNEQUAL( mask & this->mask_, numeric_cast<flag_t>(0) ); WALBERLA_ASSERT_EQUAL ( mask & this->mask_, this->mask_ ); // only true if "this->mask_" only contains one single flag, which is the case for the // current implementation of this boundary condition - WALBERLA_ASSERT_NOT_NULLPTR( bodyField_->get(nx,ny,nz) ); + WALBERLA_ASSERT_NOT_NULLPTR( bodyField_->get(nx,ny,nz), "(" << nx << ", " << ny << ", " << nz << ")" ); const real_t pdf_old = pdfField_->get( x, y, z, Stencil_T::idx[dir] ); diff --git a/tests/pe/Collision.cpp b/tests/pe/Collision.cpp index 9abc2fb2e7080262dda47b15ca7b48c8f18dbd6c..98ce1fc4a43f00854911c54a7373f0c86e50cbf8 100644 --- a/tests/pe/Collision.cpp +++ b/tests/pe/Collision.cpp @@ -58,7 +58,9 @@ void SphereTest() Sphere sp1(123, 1, Vec3(0,0,0), Vec3(0,0,0), Quat(), 1, iron, false, true, false); Sphere sp2(124, 2, Vec3(real_t(1.5),0,0), Vec3(0,0,0), Quat(), 1, iron, false, true, false); Sphere sp3(125, 3, Vec3(real_t(3.0),0,0), Vec3(0,0,0), Quat(), 1, iron, false, true, false); + Sphere sp4(124, 2, Vec3(0,real_t(1.5),0), Vec3(0,0,0), Quat(), 1, iron, false, true, false); Plane pl1(223, 1, Vec3(0,0,0), Vec3(1,1,1).getNormalized(), 0, iron); + CylindricalBoundary cb1(333, 0, Vec3(-100,0,0), 2, iron); std::vector<Contact> contacts; fcd::AnalyticCollideFunctor< std::vector<Contact> > collideFunc(contacts); @@ -85,6 +87,26 @@ void SphereTest() Contact( &sp2, &pl1, Vec3(1,real_t(-0.5),real_t(-0.5)), Vec3(1, 1, 1).getNormalized(), real_t(-0.133974596215561181)) ); WALBERLA_CHECK( !collideFunc(&sp3, &pl1) ); + + // SPHERE <-> CYLINDRICAL BOUNDARY + WALBERLA_LOG_INFO("SPHERE <-> CYLINDRICAL BOUNDARY"); + contacts.clear(); + WALBERLA_CHECK( !collideFunc(&sp1, &cb1) ); + WALBERLA_CHECK( !collideFunc(&sp2, &cb1) ); + WALBERLA_CHECK( collideFunc(&sp4, &cb1) ); + checkContact( contacts.at(0), + Contact( &sp4, &cb1, Vec3(0,real_t(2),real_t(0)), Vec3(0, -1, 0).getNormalized(), real_t(-0.5)) ); + cb1.rotateAroundOrigin( Vec3( 0,0,1), math::M_PI * real_t(0.25) ); + WALBERLA_CHECK( !collideFunc(&sp1, &cb1) ); + WALBERLA_CHECK( collideFunc(&sp2, &cb1) ); + WALBERLA_CHECK( collideFunc(&sp4, &cb1) ); + const real_t xPos = real_t(3) / real_t(4) + real_t(2) / real_c(sqrt(real_t(2))); + const real_t yPos = xPos - real_t(4) / real_c(sqrt(real_t(2))); + const real_t dist = real_c(sqrt((xPos - real_t(1.5)) * (xPos - real_t(1.5)) + yPos * yPos)) - sp4.getRadius(); + checkContact( contacts.at(1), + Contact( &sp2, &cb1, Vec3(xPos, yPos, 0), Vec3(-1, +1, 0).getNormalized(), dist) ); + checkContact( contacts.at(2), + Contact( &sp4, &cb1, Vec3(yPos, xPos, 0), Vec3(+1, -1, 0).getNormalized(), dist) ); } void BoxTest() diff --git a/tests/pe_coupling/CMakeLists.txt b/tests/pe_coupling/CMakeLists.txt index e31ded9e826492cb2926ada9d7272dd4647bccd6..b956c82da8a313411a476bb030f0de629de0955a 100644 --- a/tests/pe_coupling/CMakeLists.txt +++ b/tests/pe_coupling/CMakeLists.txt @@ -99,6 +99,10 @@ waLBerla_execute_test( NAME LubricationCorrectionMEMSphereWallTest COMMAND $<T waLBerla_compile_test( FILES momentum_exchange_method/PeriodicParticleChannelMEM.cpp DEPENDS blockforest pe timeloop ) waLBerla_execute_test( NAME PeriodicParticleChannelMEMTest COMMAND $<TARGET_FILE:PeriodicParticleChannelMEM> --shortrun PROCESSES 4 LABELS longrun ) +waLBerla_compile_test( FILES momentum_exchange_method/TaylorCouetteFlowMEM.cpp DEPENDS blockforest pe timeloop ) +waLBerla_execute_test( NAME TaylorCouetteFlowMEMTest COMMAND $<TARGET_FILE:TaylorCouetteFlowMEM> --shortrun ) +waLBerla_execute_test( NAME TaylorCouetteFlowMEMTest2 COMMAND $<TARGET_FILE:TaylorCouetteFlowMEM> PROCESSES 4 LABELS longrun) + waLBerla_compile_test( FILES momentum_exchange_method/SegreSilberbergMEM.cpp DEPENDS blockforest pe timeloop ) waLBerla_execute_test( NAME SegreSilberbergMEMSyncNextNeighbor COMMAND $<TARGET_FILE:SegreSilberbergMEM> --shortrun PROCESSES 9 ) waLBerla_execute_test( NAME SegreSilberbergMEMSyncShadowOwner COMMAND $<TARGET_FILE:SegreSilberbergMEM> --shortrun --syncShadowOwners PROCESSES 9 ) diff --git a/tests/pe_coupling/momentum_exchange_method/PeriodicParticleChannelMEM.cpp b/tests/pe_coupling/momentum_exchange_method/PeriodicParticleChannelMEM.cpp index 51ae2623cf6413f54c9efa451cebcc94b7f64e18..46e97a1129656e65c26e5383cf9c471fa71f3b59 100644 --- a/tests/pe_coupling/momentum_exchange_method/PeriodicParticleChannelMEM.cpp +++ b/tests/pe_coupling/momentum_exchange_method/PeriodicParticleChannelMEM.cpp @@ -13,7 +13,7 @@ // You should have received a copy of the GNU General Public License along // with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. // -//! \file PeriodicParticleChannelMEMPe.cpp +//! \file PeriodicParticleChannelMEM.cpp //! \ingroup pe_coupling //! \author Florian Schornbaum <florian.schornbaum@fau.de> //! \author Christoph Rettinger <christoph.rettinger@fau.de> @@ -65,8 +65,9 @@ #include "vtk/all.h" +#include <vector> -namespace periodic_particle_channel_mem_pe +namespace periodic_particle_channel_mem { /////////// @@ -423,7 +424,7 @@ int main( int argc, char **argv ) // create pe bodies const auto material = pe::createMaterial( "granular", real_c(1.2), real_c(0.25), real_c(0.4), real_c(0.4), real_c(0.35), real_c(1.39e11), real_c(5.18e7), real_c(1.07e2), real_c(1.07e2) ); - // global (fixed) bodies + // global bodies // bounding planes pe::createPlane( *globalBodyStorage, 0, Vector3<real_t>(0,0,1), Vector3<real_t>(0,0,0), material ); pe::createPlane( *globalBodyStorage, 0, Vector3<real_t>(0,0,-1), Vector3<real_t>(0,0,real_c(width)), material ); @@ -431,14 +432,18 @@ int main( int argc, char **argv ) pe::createPlane( *globalBodyStorage, 0, Vector3<real_t>(0,-1,0), Vector3<real_t>(0,real_c(width),0), material ); // spheres as obstacles - pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, Vector3<real_t>(real_c(length) / real_t(2), real_t(50), real_t(110)), real_t(60), material, false, true, true ); - pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, Vector3<real_t>( real_t(0), real_t(50), -real_t(60)), real_t(80), material, false, true, true ); - pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, Vector3<real_t>( real_c(length), real_t(50), -real_t(60)), real_t(80), material, false, true, true ); + std::vector<walberla::id_t> globalBodiesToBeMapped; + auto globalSphere1 = pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, Vector3<real_t>(real_c(length) / real_t(2), real_t(50), real_t(110)), real_t(60), material, true, false, true ); + globalBodiesToBeMapped.push_back(globalSphere1->getSystemID() ); + auto globalSphere2 = pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, Vector3<real_t>( real_t(0), real_t(50), -real_t(60)), real_t(80), material, true, false, true ); + globalBodiesToBeMapped.push_back(globalSphere2->getSystemID() ); + auto globalSphere3 = pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, Vector3<real_t>( real_c(length), real_t(50), -real_t(60)), real_t(80), material, true, false, true ); + globalBodiesToBeMapped.push_back(globalSphere3->getSystemID() ); // local bodies: moving spheres const real_t radius = real_t(10); - auto sphere = pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, Vector3<real_t>( real_t(15), real_t(50), real_t(35) ), radius, material ); //TODO change + auto sphere = pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, Vector3<real_t>( real_t(15), real_t(50), real_t(35) ), radius, material ); if( sphere != NULL ) sphere->setLinearVel( velocity, real_t(0), real_t(0) ); sphere = pe::createSphere( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, Vector3<real_t>( real_t(15), real_t(35), real_t(50) ), radius, material ); @@ -494,7 +499,11 @@ int main( int argc, char **argv ) // map pe bodies into the LBM simulation // global bodies act as no-slip obstacles and are not treated by the fluid-solid coupling - pe_coupling::mapGlobalBodies< BoundaryHandling_T >( blocks, boundaryHandlingID, *globalBodyStorage, NoSlip_Flag ); + // special care has to be taken here that only the global spheres (not the planes) are mapped since the planes would overwrite the already set boundary flags + for( auto globalBodyIt = globalBodiesToBeMapped.begin(); globalBodyIt != globalBodiesToBeMapped.end(); ++globalBodyIt ) + { + pe_coupling::mapGlobalBody< BoundaryHandling_T >( *globalBodyIt, blocks, boundaryHandlingID, *globalBodyStorage, NoSlip_Flag, false ); + } // moving bodies are handled by the momentum exchange method pe_coupling::mapMovingBodies< BoundaryHandling_T >( blocks, boundaryHandlingID, bodyStorageID, bodyFieldID, MO_Flag ); @@ -608,8 +617,8 @@ int main( int argc, char **argv ) return 0; } -} //namespace periodic_particle_channel_mem_pe +} //namespace periodic_particle_channel_mem int main( int argc, char **argv ){ - periodic_particle_channel_mem_pe::main(argc, argv); + periodic_particle_channel_mem::main(argc, argv); } diff --git a/tests/pe_coupling/momentum_exchange_method/TaylorCouetteFlowMEM.cpp b/tests/pe_coupling/momentum_exchange_method/TaylorCouetteFlowMEM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b7d4fc43c277766caaacf7f03a129577dbc79bd1 --- /dev/null +++ b/tests/pe_coupling/momentum_exchange_method/TaylorCouetteFlowMEM.cpp @@ -0,0 +1,421 @@ +//====================================================================================================================== +// +// 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 TaylorCouetteFlowMEM.cpp +//! \ingroup pe_coupling +//! \author Christoph Rettinger <christoph.rettinger@fau.de> +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +#include "blockforest/Initialization.h" +#include "blockforest/communication/UniformBufferedScheme.h" + +#include "boundary/all.h" + +#include "core/DataTypes.h" +#include "core/Environment.h" +#include "core/SharedFunctor.h" +#include "core/debug/Debug.h" +#include "core/debug/TestSubsystem.h" +#include "core/math/all.h" +#include "core/timing/RemainingTimeLogger.h" + +#include "domain_decomposition/SharedSweep.h" + +#include "field/AddToStorage.h" +#include "field/communication/PackInfo.h" +#include "field/vtk/all.h" + +#include "lbm/boundary/all.h" +#include "lbm/communication/PdfFieldPackInfo.h" +#include "lbm/field/AddToStorage.h" +#include "lbm/field/PdfField.h" +#include "lbm/lattice_model/D3Q19.h" +#include "lbm/sweeps/CellwiseSweep.h" +#include "lbm/vtk/all.h" + +#include "timeloop/SweepTimeloop.h" + +#include "pe/basic.h" +#include "pe/Types.h" + +#include "pe_coupling/mapping/all.h" +#include "pe_coupling/momentum_exchange_method/all.h" + +#include "vtk/all.h" + +namespace taylor_coette_flow_mem +{ + +/////////// +// USING // +/////////// + +using namespace walberla; +using walberla::uint_t; + + + +////////////// +// TYPEDEFS // +////////////// + +// pdf field & flag field +typedef lbm::D3Q19< lbm::collision_model::TRT, false > LatticeModel_T; +typedef LatticeModel_T::Stencil Stencil_T; +typedef lbm::PdfField< LatticeModel_T > PdfField_T; + +typedef walberla::uint8_t flag_t; +typedef FlagField< flag_t > FlagField_T; +typedef GhostLayerField< pe::BodyID, 1 > BodyField_T; + +const uint_t FieldGhostLayers = 1; + +typedef pe_coupling::CurvedLinear< LatticeModel_T, FlagField_T > MO_T; + +typedef boost::tuples::tuple< MO_T > BoundaryConditions_T; +typedef BoundaryHandling< FlagField_T, Stencil_T, BoundaryConditions_T > BoundaryHandling_T; + +typedef boost::tuple< pe::Capsule, pe::CylindricalBoundary > BodyTypeTuple; + +/////////// +// FLAGS // +/////////// + +const FlagUID Fluid_Flag( "fluid" ); +const FlagUID MO_Flag( "moving obstacle" ); +const FlagUID FormerMO_Flag( "former moving obstacle" ); + + + +///////////////////// +// CHECK FUNCTIONS // +///////////////////// + +class VelocityChecker +{ +public: + VelocityChecker( const shared_ptr< StructuredBlockStorage > & blocks, const BlockDataID & pdfFieldID, + real_t radius1, real_t radius2, real_t angularVel1, real_t angularVel2, real_t domainLength, real_t domainWidth ) : + blocks_( blocks ), pdfFieldID_( pdfFieldID ), radius1_( radius1 ), radius2_( radius2 ), angularVel1_( angularVel1 ), angularVel2_( angularVel2 ), + domainLength_( domainLength ), domainWidth_( domainWidth ) + { } + real_t getMaximumRelativeError() + { + Vector3<real_t> midPoint( domainLength_ * real_t(0.5), domainWidth_ * real_t(0.5), domainWidth_ * real_t(0.5) ); + Cell midCell = blocks_->getCell( midPoint ); + CellInterval evaluationCells( midCell.x(), midCell.y(), midCell.z(), midCell.x(), blocks_->getDomainCellBB().yMax(), midCell.z() ); + + real_t maxError = real_t(0); + + for( auto blockIt = blocks_->begin(); blockIt != blocks_->end(); ++blockIt ) + { + auto pdfField = blockIt->getData< PdfField_T > ( pdfFieldID_ ); + CellInterval intersection = blocks_->getBlockCellBB( *blockIt ); + intersection.intersect( evaluationCells ); + blocks_->transformGlobalToBlockLocalCellInterval( intersection, *blockIt ); + for( auto fieldIt = pdfField->beginSliceXYZ(intersection); fieldIt != pdfField->end(); ++fieldIt ) + { + const Vector3< real_t > cellCenter = blocks_->getBlockLocalCellCenter( *blockIt, fieldIt.cell() ); + auto diff = cellCenter - midPoint; + real_t radius = diff.length(); + if( radius > radius1_ && radius < radius2_ ) + { + real_t velSimulation = pdfField->getVelocity( fieldIt.cell() )[2]; + real_t velAnalytical = getAnalyticalVelocity( radius ); + real_t relVelDiff = std::fabs(velSimulation - velAnalytical) / std::fabs(velAnalytical); + maxError = std::max( maxError, relVelDiff ); + } + } + } + WALBERLA_MPI_SECTION() + { + mpi::allReduceInplace( maxError, mpi::MAX ); + } + return maxError; + } +private: + real_t getAnalyticalVelocity( real_t r ) + { + real_t radius1sqr = radius1_ * radius1_; + real_t radius2sqr = radius2_ * radius2_; + real_t a = ( angularVel2_ * radius2sqr - angularVel1_ * radius1sqr ) / ( radius2sqr - radius1sqr ); + real_t b = ( angularVel1_ - angularVel2_ ) * radius1sqr * radius2sqr / ( radius2sqr - radius1sqr ); + real_t velInAgularDir = a * r + b / r; + return velInAgularDir; + } + + shared_ptr< StructuredBlockStorage > blocks_; + const BlockDataID pdfFieldID_; + real_t radius1_, radius2_, angularVel1_, angularVel2_, domainLength_, domainWidth_; +}; + +///////////////////////////////////// +// BOUNDARY HANDLING CUSTOMIZATION // +///////////////////////////////////// + +// class MyBoundaryHandling is responsible for creating the boundary handling and setting up the geometry at the outer domain boundaries + +class MyBoundaryHandling +{ +public: + + MyBoundaryHandling( const BlockDataID & flagFieldID, const BlockDataID & pdfFieldID, const BlockDataID & bodyFieldID ) : + flagFieldID_( flagFieldID ), pdfFieldID_( pdfFieldID ), bodyFieldID_( bodyFieldID ) {} + + BoundaryHandling_T * operator()( IBlock* const block, const StructuredBlockStorage* const storage ) const; + +private: + + const BlockDataID flagFieldID_; + const BlockDataID pdfFieldID_; + const BlockDataID bodyFieldID_; + +}; // class MyBoundaryHandling + + + +BoundaryHandling_T * MyBoundaryHandling::operator()( IBlock * const block, const StructuredBlockStorage * const storage ) const +{ + WALBERLA_ASSERT_NOT_NULLPTR( block ); + WALBERLA_ASSERT_NOT_NULLPTR( storage ); + + FlagField_T * flagField = block->getData< FlagField_T >( flagFieldID_ ); + PdfField_T * pdfField = block->getData< PdfField_T > ( pdfFieldID_ ); + BodyField_T * bodyField = block->getData< BodyField_T >( bodyFieldID_ ); + + const auto fluid = flagField->flagExists( Fluid_Flag ) ? flagField->getFlag( Fluid_Flag ) : flagField->registerFlag( Fluid_Flag ); + + BoundaryHandling_T * handling = new BoundaryHandling_T( "cf boundary handling", flagField, fluid, + boost::tuples::make_tuple( MO_T( "MO", MO_Flag, pdfField, flagField, bodyField, fluid, *storage, *block ) ) ); + + CellInterval domainBB = storage->getDomainCellBB(); + domainBB.xMin() -= cell_idx_c( FieldGhostLayers ); + domainBB.xMax() += cell_idx_c( FieldGhostLayers ); + + domainBB.yMin() -= cell_idx_c( FieldGhostLayers ); + domainBB.yMax() += cell_idx_c( FieldGhostLayers ); + + domainBB.zMin() -= cell_idx_c( FieldGhostLayers ); + domainBB.zMax() += cell_idx_c( FieldGhostLayers ); + + handling->fillWithDomain( FieldGhostLayers ); + + return handling; +} + + +////////// +// MAIN // +////////// +/* + * This test features a so-called Taylor-Couette flow, i.e. a Couette flow between two rotating, coaxial cylinders + * For such a setup, the velocity in angular direction can be calculated analytically. + */ +int main( int argc, char **argv ) +{ + debug::enterTestMode(); + + mpi::Environment env( argc, argv ); + + auto processes = MPIManager::instance()->numProcesses(); + if( processes != 1 && processes % 4 != 0 ) + { + std::cerr << "Number of processes must be equal to either 1 or a multiple of 4!" << std::endl; + return EXIT_FAILURE; + } + + bool shortrun = false; + uint_t vtkIOFreq = 0; + + for( int i = 1; i < argc; ++i ) + { + if( std::strcmp( argv[i], "--shortrun" ) == 0 ) { shortrun = true; continue; } + if( std::strcmp( argv[i], "--vtkIOFreq" ) == 0 ) { vtkIOFreq = uint_c( std::atof( argv[++i] ) ); continue; } + WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]); + } + + bool vtkIO = (vtkIOFreq == 0 ) ? false : true; + + /////////////////////////// + // SIMULATION PROPERTIES // + /////////////////////////// + + const uint_t length = uint_c(20); + const uint_t width = uint_c(101); + if( width % 2 == 0 ) WALBERLA_ABORT("Width has to be odd, since otherwise the current implementation of the velocity evaluation does not work correctly!"); + + const real_t omega = real_c(1.33); + + const uint_t timesteps = shortrun ? uint_c(10) : uint_c(5000); + + const real_t radius1 = real_c(width) * real_t(0.25); // radius of internal cylinder + const real_t radius2 = real_c(width) * real_t(0.45); // radius of external cylinder + const real_t angularVel1 = real_t(0.001); // angular velocity of internal cylinder + const real_t angularVel2 = real_t(-0.001); // angular velocity of external cylinder + + /////////////////////////// + // BLOCK STRUCTURE SETUP // + /////////////////////////// + + // (length x width x width) - periodic in x-direction! + Vector3<uint_t> blockDist = getFactors3D( uint_c(processes), Vector3<real_t>( real_c(length), real_c(width), real_c(width) ) ); + if ( processes == 1 ) + { + // due to periodicity, the pe needs at least three blocks in the periodic direction + blockDist[0] = uint_t(4); + } + + const uint_t xCells = length / blockDist[0]; + const uint_t yCells = width / blockDist[1]; + const uint_t zCells = width / blockDist[2]; + + const real_t dx = real_t(1); + + auto blocks = blockforest::createUniformBlockGrid( blockDist[0], blockDist[1], blockDist[2], xCells, yCells, zCells, dx, + ( processes != 1 ), + true, false, false ); + if( vtkIO ) + { + vtk::writeDomainDecomposition( blocks ); + } + ///////////////// + // PE COUPLING // + ///////////////// + + // set up pe functionality + shared_ptr<pe::BodyStorage> globalBodyStorage = make_shared<pe::BodyStorage>(); + pe::SetBodyTypeIDs<BodyTypeTuple>::execute(); + auto bodyStorageID = blocks->addBlockData(pe::createStorageDataHandling<BodyTypeTuple>(), "pe Body Storage"); + + // set up synchronization procedure + const real_t overlap = real_t( 1.5 ) * dx; + boost::function<void(void)> syncCall = boost::bind( pe::syncShadowOwners<BodyTypeTuple>, boost::ref(blocks->getBlockForest()), bodyStorageID, static_cast<WcTimingTree*>(NULL), overlap, false ); + + // create pe bodies + const auto material = pe::createMaterial( "granular", real_t(1.2), real_t(0.25), real_t(0.4), real_t(0.4), real_t(0.35), real_t(1.39e11), real_t(5.18e7), real_t(1.07e2), real_t(1.07e2) ); + + // instead of a cylinder, we use the capsule and make sure it extends the computational domain in x-direction to effectively get a cylinder + auto cap = pe::createCapsule( *globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, pe::Vec3(real_c(length),real_c(width),real_c(width)) * real_t(0.5), radius1, real_c(length)*real_t(2), material, true, false, true ); + cap->setAngularVel( pe::Vec3(1,0,0) * angularVel1 ); + + auto cb = pe::createCylindricalBoundary( *globalBodyStorage, 0, pe::Vec3(real_c(length),real_c(width),real_c(width))*real_t(0.5), radius2 ); + cb->setAngularVel( pe::Vec3(1,0,0) * angularVel2 ); + + //synchronize the pe set up on all processes + syncCall(); + + //////////////////////// + // ADD DATA TO BLOCKS // + //////////////////////// + + // add pdf field + LatticeModel_T latticeModel = LatticeModel_T( lbm::collision_model::TRT::constructWithMagicNumber( omega ) ); + + BlockDataID pdfFieldID = lbm::addPdfFieldToStorage( blocks, "pdf field (zyxf)", latticeModel, Vector3< real_t >( real_t(0) ), real_t(1), uint_t(1), field::zyxf ); + + // add flag field + BlockDataID flagFieldID = field::addFlagFieldToStorage<FlagField_T>( blocks, "flag field" ); + + // add body field + BlockDataID bodyFieldID = field::addToStorage<BodyField_T>( blocks, "body field", NULL, field::zyxf ); + + // add boundary handling & initialize outer domain boundaries (moving walls on the front, back, top, and bottom plane) + BlockDataID boundaryHandlingID = blocks->addStructuredBlockData< BoundaryHandling_T >( + MyBoundaryHandling( flagFieldID, pdfFieldID, bodyFieldID ), "boundary handling" ); + + // map pe bodies into the LBM simulation + // moving bodies are handled by the momentum exchange method, thus act here as velocity boundary condition + pe_coupling::mapMovingGlobalBodies< BoundaryHandling_T >( blocks, boundaryHandlingID, *globalBodyStorage, bodyFieldID, MO_Flag ); + + /////////////// + // TIME LOOP // + /////////////// + + SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps ); + + // setup of the LBM communication for synchronizing the pdf field between neighboring blocks + boost::function< void () > commFunction; + blockforest::communication::UniformBufferedScheme< Stencil_T > scheme( blocks ); + scheme.addPackInfo( make_shared< lbm::PdfFieldPackInfo< LatticeModel_T > >( pdfFieldID ) ); + commFunction = scheme; + + // add LBM communication function and boundary handling sweep + timeloop.add() + << BeforeFunction( commFunction, "LBM Communication" ) + << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" ); + + // LBM stream collide sweep + timeloop.add() << Sweep( makeSharedSweep( lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldID, flagFieldID, Fluid_Flag ) ), "LBM DEFAULT" ); + + //////////////// + // VTK OUTPUT // + //////////////// + + if( vtkIO ) + { + const uint_t writeFrequency = 10; //vtk::determineWriteFrequency( dt_SI, uint_c(30) ); + + // flag field (written only once in the first time step, ghost layers are also written) + auto flagFieldVTK = vtk::createVTKOutput_BlockData( blocks, "flag_field", timesteps, FieldGhostLayers ); + flagFieldVTK->addCellDataWriter( make_shared< field::VTKWriter< FlagField_T > >( flagFieldID, "FlagField" ) ); + timeloop.addFuncAfterTimeStep( vtk::writeFiles( flagFieldVTK ), "VTK (flag field data)" ); + + // pdf field (ghost layers cannot be written because re-sampling/coarsening is applied) + auto pdfFieldVTK = vtk::createVTKOutput_BlockData( blocks, "fluid_field", writeFrequency ); + pdfFieldVTK->setSamplingResolution( real_c(1) ); + + blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > pdfGhostLayerSync( blocks ); + pdfGhostLayerSync.addPackInfo( make_shared< field::communication::PackInfo< PdfField_T > >( pdfFieldID ) ); + pdfFieldVTK->addBeforeFunction( pdfGhostLayerSync ); + + field::FlagFieldCellFilter< FlagField_T > fluidFilter( flagFieldID ); + fluidFilter.addFlag( Fluid_Flag ); + pdfFieldVTK->addCellInclusionFilter( fluidFilter ); + + pdfFieldVTK->addCellDataWriter( make_shared< lbm::VelocityVTKWriter< LatticeModel_T, float > >( pdfFieldID, "VelocityFromPDF" ) ); + pdfFieldVTK->addCellDataWriter( make_shared< lbm::DensityVTKWriter < LatticeModel_T, float > >( pdfFieldID, "DensityFromPDF" ) ); + + timeloop.addFuncAfterTimeStep( vtk::writeFiles( pdfFieldVTK ), "VTK (fluid field data)" ); + } + + timeloop.addFuncAfterTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" ); + + //////////////////////// + // EXECUTE SIMULATION // + //////////////////////// + + WcTimingPool timeloopTiming; + timeloop.run( timeloopTiming ); + + VelocityChecker velChecker(blocks, pdfFieldID, radius1, radius2, angularVel1, angularVel2, real_c(length), real_c(width) ); + real_t maxError = velChecker.getMaximumRelativeError(); + if( !shortrun ) + { + timeloopTiming.logResultOnRoot(); + WALBERLA_LOG_RESULT_ON_ROOT("Maximum relative error in velocity in angular direction: " << maxError ); + // error has to be below 10% + WALBERLA_CHECK_LESS( maxError, real_t(0.1) ); + } + + return 0; +} + +} //namespace taylor_coette_flow_mem + +int main( int argc, char **argv ){ + taylor_coette_flow_mem::main(argc, argv); +}