From 0a5b7370db31d9f39f3595933b717d1fdc457896 Mon Sep 17 00:00:00 2001 From: Michael Kuron <mkuron@icp.uni-stuttgart.de> Date: Tue, 26 Mar 2019 09:58:39 +0100 Subject: [PATCH] Added functionality to LBM boundaries to optionally obtain the hydrodynamic force on those boundaries. Also added tests. --- src/lbm/boundary/DynamicUBB.h | 28 ++- src/lbm/boundary/NoSlip.h | 37 ++- src/lbm/boundary/ParserUBB.h | 94 +++++--- src/lbm/boundary/SimpleUBB.h | 45 +++- src/lbm/boundary/UBB.h | 70 ++++-- tests/lbm/CMakeLists.txt | 7 + tests/lbm/boundary/BoundaryForce.cpp | 228 +++++++++++++++++++ tests/lbm/boundary/BoundaryForceCouette.cpp | 237 ++++++++++++++++++++ 8 files changed, 685 insertions(+), 61 deletions(-) create mode 100644 tests/lbm/boundary/BoundaryForce.cpp create mode 100644 tests/lbm/boundary/BoundaryForceCouette.cpp diff --git a/src/lbm/boundary/DynamicUBB.h b/src/lbm/boundary/DynamicUBB.h index 67425aa05..945cc36f3 100644 --- a/src/lbm/boundary/DynamicUBB.h +++ b/src/lbm/boundary/DynamicUBB.h @@ -51,12 +51,14 @@ namespace lbm { // 2. A member function "Vector3< real_t > operator()( const Vector3< real_t > & x, const real_t t )" that is called for every // boundary link treated by "treatDirection". The arguments are the position 'x' of the boudnary cell in the simulation space and the current time 't'. // The functon is supposed to return the velocity used by the boundary treatment. -template< typename LatticeModel_T, typename flag_t, typename VelocityFunctor_T, bool AdaptVelocityToExternalForce = false > +template< typename LatticeModel_T, typename flag_t, typename VelocityFunctor_T, bool AdaptVelocityToExternalForce = false, bool StoreForce = false > class DynamicUBB : public Boundary<flag_t> { typedef lbm::PdfField< LatticeModel_T > PDFField; typedef typename LatticeModel_T::Stencil Stencil; + typedef GhostLayerField< Vector3<real_t>, 1 > ForceField; + public: static const bool threadsafe = true; @@ -83,6 +85,9 @@ public: { velocity_( time_ ); } + + if (StoreForce) + force_ = make_shared<ForceField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), pdfField_->nrOfGhostLayers(), field::zyxf ); } DynamicUBB( const BoundaryUID & boundaryUID, const FlagUID & uid, PDFField * const pdfField, const uint_t level, const VelocityFunctor_T & velocity, const AABB & aabb ) : @@ -100,6 +105,9 @@ public: time_ = timeTracker_->getTime( level_ ); velocity_( time_ ); } + + if (StoreForce) + force_->setWithGhostLayer( Vector3<real_t>() ); } void afterBoundaryTreatment() const {} @@ -131,6 +139,8 @@ public: 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 (DynamicUBB) + const real_t pdf_old = pdfField_->get( x, y, z, Stencil::idx[dir] ); + const Vector3< real_t > pos( origin_[0] + real_c(nx) * dx_[0], origin_[1] + real_c(ny) * dx_[1], origin_[2] + real_c(nz) * dx_[2] ); @@ -160,6 +170,21 @@ public: real_c(stencil::cy[ dir ]) * vel[1] + real_c(stencil::cz[ dir ]) * vel[2] ) ); } + + if (StoreForce && pdfField_->isInInnerPart( Cell(x,y,z) )) + { + const real_t forceMEM = pdf_old + pdfField_->get( nx, ny, nz, Stencil::invDirIdx(dir) ); + Vector3<real_t> force( real_c( stencil::cx[dir] ) * forceMEM, + real_c( stencil::cy[dir] ) * forceMEM, + real_c( stencil::cz[dir] ) * forceMEM ); + force_->get( nx, ny, nz ) += force; + } + } + + const typename ForceField::value_type & getForce( const cell_idx_t x, cell_idx_t y, cell_idx_t z ) const + { + static_assert(StoreForce, "this member function is only available if the fourth template argument on the class is true"); + return force_->get(x,y,z); } private: @@ -177,6 +202,7 @@ private: uint_t level_; VelocityFunctor_T velocity_; + shared_ptr<ForceField> force_; }; // class DynamicUBB diff --git a/src/lbm/boundary/NoSlip.h b/src/lbm/boundary/NoSlip.h index 0b9d3e32a..dc769d345 100644 --- a/src/lbm/boundary/NoSlip.h +++ b/src/lbm/boundary/NoSlip.h @@ -42,7 +42,7 @@ namespace lbm { -template< typename LatticeModel_T, typename flag_t > +template< typename LatticeModel_T, typename flag_t, bool StoreForce = false > class NoSlip : public Boundary<flag_t> { protected: @@ -50,6 +50,8 @@ protected: typedef PdfField< LatticeModel_T > PDFField; typedef typename LatticeModel_T::Stencil Stencil; + typedef GhostLayerField< Vector3<real_t>, 1 > ForceField; + public: static const bool threadsafe = true; @@ -60,11 +62,21 @@ public: NoSlip( const BoundaryUID& boundaryUID, const FlagUID& uid, PDFField* const pdfField ) : - Boundary<flag_t>( boundaryUID ), uid_( uid ), pdfField_( pdfField ) { WALBERLA_ASSERT_NOT_NULLPTR( pdfField_ ); } + Boundary<flag_t>( boundaryUID ), uid_( uid ), pdfField_( pdfField ) + { + WALBERLA_ASSERT_NOT_NULLPTR( pdfField_ ); + + if (StoreForce) + force_ = make_shared<ForceField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), pdfField_->nrOfGhostLayers(), field::zyxf ); + } void pushFlags( std::vector< FlagUID >& uids ) const { uids.push_back( uid_ ); } - void beforeBoundaryTreatment() const {} + void beforeBoundaryTreatment() const + { + if (StoreForce) + force_->setWithGhostLayer( Vector3<real_t>() ); + } void afterBoundaryTreatment() const {} template< typename Buffer_T > @@ -95,7 +107,24 @@ public: 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 (NoSlip) + const real_t pdf_old = pdfField_->get( x, y, z, Stencil::idx[dir] ); + pdfField_->get( nx, ny, nz, Stencil::invDirIdx(dir) ) = pdfField_->get( x, y, z, Stencil::idx[dir] ); + + if (StoreForce && pdfField_->isInInnerPart( Cell(x,y,z) )) + { + const real_t forceMEM = pdf_old + pdfField_->get( nx, ny, nz, Stencil::invDirIdx(dir) ); + Vector3<real_t> force( real_c( stencil::cx[dir] ) * forceMEM, + real_c( stencil::cy[dir] ) * forceMEM, + real_c( stencil::cz[dir] ) * forceMEM ); + force_->get( nx, ny, nz ) += force; + } + } + + const typename ForceField::value_type & getForce( const cell_idx_t x, cell_idx_t y, cell_idx_t z ) const + { + static_assert(StoreForce, "this member function is only available if the third template argument on the class is true"); + return force_->get(x,y,z); } private: @@ -104,6 +133,8 @@ private: PDFField* const pdfField_; + shared_ptr<ForceField> force_; + }; // class NoSlip diff --git a/src/lbm/boundary/ParserUBB.h b/src/lbm/boundary/ParserUBB.h index 82bfff5e2..415f36bc4 100644 --- a/src/lbm/boundary/ParserUBB.h +++ b/src/lbm/boundary/ParserUBB.h @@ -47,12 +47,14 @@ namespace lbm { -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce = false > +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce = false, bool StoreForce = false > class ParserUBB : public Boundary<flag_t> { typedef lbm::PdfField< LatticeModel_T > PDFField; typedef typename LatticeModel_T::Stencil Stencil; + typedef GhostLayerField< Vector3<real_t>, 1 > ForceField; + public: static const bool threadsafe = true; @@ -118,6 +120,9 @@ public: { if( timeTracker_ ) time_ = timeTracker_->getTime( level_ ); + + if (StoreForce) + force_->setWithGhostLayer( Vector3<real_t>() ); } void afterBoundaryTreatment() const {} @@ -145,6 +150,12 @@ public: inline Vector3<real_t> getValue( const cell_idx_t x, cell_idx_t y, cell_idx_t z ) const; inline Vector3<real_t> getValue( const cell_idx_t x, cell_idx_t y, cell_idx_t z, real_t t ) const; + const typename ForceField::value_type & getForce( const cell_idx_t x, cell_idx_t y, cell_idx_t z ) const + { + static_assert(StoreForce, "this member function is only available if the fourth template argument on the class is true"); + return force_->get(x,y,z); + } + private: const FlagUID uid_; @@ -161,11 +172,12 @@ private: shared_ptr<ParserField> parserField_; shared_ptr<VelocityField> velocityField_; + shared_ptr<ForceField> force_; }; // class ParserUBB -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce> -inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::Parser::Parser( const Config::BlockHandle & config ) +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce> +inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce>::Parser::Parser( const Config::BlockHandle & config ) : parsers_(), equations_(), timeDependent_( false ) { if( !config ) @@ -194,8 +206,8 @@ inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::Parser:: } } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce> -inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::Parser::Parser( std::array< std::string, 3 > & equations ) +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce> +inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce>::Parser::Parser( std::array< std::string, 3 > & equations ) : parsers_(), equations_( equations ), timeDependent_( false ) { if( equations_[0].length() > 0 ) @@ -218,16 +230,16 @@ inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::Parser:: } } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > -inline ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::Velocity::Velocity( const Config::BlockHandle & config ) +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::Velocity::Velocity( const Config::BlockHandle & config ) { velocity_[0] = ( config && config.isDefined( "x" ) ) ? config.getParameter<real_t>( "x" ) : real_c(0.0); velocity_[1] = ( config && config.isDefined( "y" ) ) ? config.getParameter<real_t>( "y" ) : real_c(0.0); velocity_[2] = ( config && config.isDefined( "z" ) ) ? config.getParameter<real_t>( "z" ) : real_c(0.0); } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce> -Vector3< real_t > ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::Parser::operator()( const Vector3< real_t > & x, const real_t t ) const +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce> +Vector3< real_t > ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce>::Parser::operator()( const Vector3< real_t > & x, const real_t t ) const { std::map< std::string, double > symbols; symbols["x"] = x[0]; @@ -242,8 +254,8 @@ Vector3< real_t > ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce return v; } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce> -Vector3< real_t > ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::Parser::operator()( const Vector3< real_t > & x ) const +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce> +Vector3< real_t > ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce>::Parser::operator()( const Vector3< real_t > & x ) const { WALBERLA_ASSERT( !timeDependent_ ); @@ -261,8 +273,8 @@ Vector3< real_t > ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce> -inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::ParserUBB( const BoundaryUID & boundaryUID, const FlagUID & uid, PDFField * const pdfField, +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce> +inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce>::ParserUBB( const BoundaryUID & boundaryUID, const FlagUID & uid, PDFField * const pdfField, FlagField<flag_t> * const flagField, const shared_ptr< TimeTracker > & timeTracker, const uint_t level, const AABB & aabb ) : Boundary<flag_t>( boundaryUID ), uid_( uid ), pdfField_( pdfField ), timeTracker_( timeTracker ), time_( real_t(0) ), level_( level ) @@ -285,13 +297,16 @@ inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::ParserUB parserField_ = make_shared<ParserField> ( pdfField->xSize(), pdfField->ySize(), pdfField->zSize(), pdfField->nrOfGhostLayers(), field::zyxf ); velocityField_ = make_shared<VelocityField>( pdfField->xSize(), pdfField->ySize(), pdfField->zSize(), pdfField->nrOfGhostLayers(), field::zyxf ); } + + if (StoreForce) + force_ = make_shared<ForceField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), pdfField_->nrOfGhostLayers(), field::zyxf ); } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > template< typename Buffer_T > -inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::packCell( Buffer_T & buffer, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) const +inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::packCell( Buffer_T & buffer, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) const { if( parserField_->get( x, y, z ) ) { @@ -306,9 +321,9 @@ inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::p -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > template< typename Buffer_T > -inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registerCell( Buffer_T & buffer, const flag_t, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) +inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::registerCell( Buffer_T & buffer, const flag_t, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) { bool isparser; buffer >> isparser; @@ -329,16 +344,16 @@ inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::r -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce> -inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::ParserUBB( const BoundaryUID & boundaryUID, const FlagUID & uid, PDFField * const pdfField, +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce> +inline ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce>::ParserUBB( const BoundaryUID & boundaryUID, const FlagUID & uid, PDFField * const pdfField, FlagField<flag_t> * const flagField, const uint_t level, const AABB & aabb ) : ParserUBB( boundaryUID, uid, pdfField, flagField, nullptr, level, aabb ) {} -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > -inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registerCell( const flag_t, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::registerCell( const flag_t, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const BoundaryConfiguration & parser ) { WALBERLA_ASSERT_EQUAL( dynamic_cast< const Parser * >( &parser ), &parser ); @@ -369,8 +384,8 @@ inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::r -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > -inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registerCells( const flag_t, const CellInterval & cells, const BoundaryConfiguration & parser ) +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::registerCells( const flag_t, const CellInterval & cells, const BoundaryConfiguration & parser ) { WALBERLA_ASSERT_EQUAL( dynamic_cast< const Parser * >( &parser ), &parser ); WALBERLA_ASSERT_NOT_NULLPTR( parserField_ ); @@ -408,9 +423,9 @@ inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::r -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > template< typename CellIterator > -inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registerCells( const flag_t, const CellIterator & begin, const CellIterator & end, +inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::registerCells( const flag_t, const CellIterator & begin, const CellIterator & end, const BoundaryConfiguration & parser ) { WALBERLA_ASSERT_EQUAL( dynamic_cast< const Parser * >( &parser ), &parser ); @@ -451,20 +466,20 @@ inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::r -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > -inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::unregisterCell( const flag_t, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) const +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline void ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::unregisterCell( const flag_t, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) const { parserField_->get(x,y,z) = nullptr; } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce> +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce> #ifndef NDEBUG -inline void ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::ParserUBB::treatDirection( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const stencil::Direction dir, +inline void ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce>::ParserUBB::treatDirection( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const stencil::Direction dir, const cell_idx_t nx, const cell_idx_t ny, const cell_idx_t nz, const flag_t mask ) #else -inline void ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::ParserUBB::treatDirection( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const stencil::Direction dir, +inline void ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce>::ParserUBB::treatDirection( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const stencil::Direction dir, const cell_idx_t nx, const cell_idx_t ny, const cell_idx_t nz, const flag_t /*mask*/ ) #endif { @@ -475,6 +490,8 @@ inline void ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::Par 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 (ParserUBB) + const real_t pdf_old = pdfField_->get( x, y, z, Stencil::idx[dir] ); + Vector3<real_t> velocity; if( parserField_->get(nx,ny,nz) ) { @@ -512,18 +529,27 @@ inline void ParserUBB<LatticeModel_T, flag_t, AdaptVelocityToExternalForce>::Par real_c(stencil::cy[ dir ]) * vel[1] + real_c(stencil::cz[ dir ]) * vel[2] ) ); } + + if (StoreForce && pdfField_->isInInnerPart( Cell(x,y,z) )) + { + const real_t forceMEM = pdf_old + pdfField_->get( nx, ny, nz, Stencil::invDirIdx(dir) ); + Vector3<real_t> force( real_c( stencil::cx[dir] ) * forceMEM, + real_c( stencil::cy[dir] ) * forceMEM, + real_c( stencil::cz[dir] ) * forceMEM ); + force_->get( nx, ny, nz ) += force; + } } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > -inline Vector3<real_t> ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::getValue( const cell_idx_t x, cell_idx_t y, cell_idx_t z ) const +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline Vector3<real_t> ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::getValue( const cell_idx_t x, cell_idx_t y, cell_idx_t z ) const { return getValue( x, y, z, time_ ); } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > -inline Vector3<real_t> ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::getValue( const cell_idx_t x, cell_idx_t y, cell_idx_t z, real_t t ) const +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline Vector3<real_t> ParserUBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::getValue( const cell_idx_t x, cell_idx_t y, cell_idx_t z, real_t t ) const { Vector3<real_t> velocity; if( parserField_->get(x,y,z) ) diff --git a/src/lbm/boundary/SimpleUBB.h b/src/lbm/boundary/SimpleUBB.h index da6e7839b..ba07eedc4 100644 --- a/src/lbm/boundary/SimpleUBB.h +++ b/src/lbm/boundary/SimpleUBB.h @@ -45,12 +45,14 @@ namespace lbm { -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce = false > +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce = false, bool StoreForce = false > class SimpleUBB : public Boundary<flag_t> { typedef PdfField< LatticeModel_T > PDFField; typedef typename LatticeModel_T::Stencil Stencil; + typedef GhostLayerField< Vector3<real_t>, 1 > ForceField; + public: static const bool threadsafe = true; @@ -61,14 +63,30 @@ public: SimpleUBB( const BoundaryUID& boundaryUID, const FlagUID& uid, PDFField* const pdfField, const Vector3< real_t > & velocity ) : - Boundary<flag_t>( boundaryUID ), uid_( uid ), pdfField_( pdfField ), velocity_( velocity ) { WALBERLA_ASSERT_NOT_NULLPTR( pdfField_ ); } + Boundary<flag_t>( boundaryUID ), uid_( uid ), pdfField_( pdfField ), velocity_( velocity ) +{ + WALBERLA_ASSERT_NOT_NULLPTR( pdfField_ ); + + if (StoreForce) + force_ = make_shared<ForceField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), pdfField_->nrOfGhostLayers(), field::zyxf ); +} SimpleUBB( const BoundaryUID& boundaryUID, const FlagUID& uid, PDFField* const pdfField, const real_t x, const real_t y, const real_t z ) : - Boundary<flag_t>( boundaryUID ), uid_( uid ), pdfField_( pdfField ), velocity_( x, y, z ) { WALBERLA_ASSERT_NOT_NULLPTR( pdfField_ ); } + Boundary<flag_t>( boundaryUID ), uid_( uid ), pdfField_( pdfField ), velocity_( x, y, z ) +{ + WALBERLA_ASSERT_NOT_NULLPTR( pdfField_ ); + + if (StoreForce) + force_ = make_shared<ForceField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), pdfField_->nrOfGhostLayers(), field::zyxf ); +} void pushFlags( std::vector< FlagUID >& uids ) const { uids.push_back( uid_ ); } - void beforeBoundaryTreatment() const {} + void beforeBoundaryTreatment() const + { + if (StoreForce) + force_->setWithGhostLayer( Vector3<real_t>() ); + } void afterBoundaryTreatment() const {} template< typename Buffer_T > @@ -99,6 +117,8 @@ public: 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 (SimpleUBB) + const real_t pdf_old = pdfField_->get( x, y, z, Stencil::idx[dir] ); + if( LatticeModel_T::compressible ) { const auto density = pdfField_->getDensity(x,y,z); @@ -122,6 +142,21 @@ public: real_c(stencil::cy[ dir ]) * velocity[1] + real_c(stencil::cz[ dir ]) * velocity[2] ) ); } + + if (StoreForce && pdfField_->isInInnerPart( Cell(x,y,z) )) + { + const real_t forceMEM = pdf_old + pdfField_->get( nx, ny, nz, Stencil::invDirIdx(dir) ); + Vector3<real_t> force( real_c( stencil::cx[dir] ) * forceMEM, + real_c( stencil::cy[dir] ) * forceMEM, + real_c( stencil::cz[dir] ) * forceMEM ); + force_->get( nx, ny, nz ) += force; + } + } + + const typename ForceField::value_type & getForce( const cell_idx_t x, cell_idx_t y, cell_idx_t z ) const + { + static_assert(StoreForce, "this member function is only available if the fourth template argument on the class is true"); + return force_->get(x,y,z); } private: @@ -132,6 +167,8 @@ private: const Vector3< real_t > velocity_; + shared_ptr<ForceField> force_; + }; // class SimpleUBB diff --git a/src/lbm/boundary/UBB.h b/src/lbm/boundary/UBB.h index 361322414..5ce5fcc06 100644 --- a/src/lbm/boundary/UBB.h +++ b/src/lbm/boundary/UBB.h @@ -46,7 +46,7 @@ namespace lbm { -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce = false > +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce = false, bool StoreForce = false > class UBB : public Boundary<flag_t> { typedef PdfField< LatticeModel_T > PDFField; @@ -54,6 +54,8 @@ class UBB : public Boundary<flag_t> typedef GhostLayerField< Vector3<real_t>, 1 > VelField; + typedef GhostLayerField< Vector3<real_t>, 1 > ForceField; + public: static const bool threadsafe = true; @@ -89,7 +91,7 @@ public: void pushFlags( std::vector< FlagUID > & uids ) const { uids.push_back( uid_ ); } - void beforeBoundaryTreatment() const {} + inline void beforeBoundaryTreatment() const; void afterBoundaryTreatment() const {} template< typename Buffer_T > @@ -110,19 +112,26 @@ public: inline const Vector3<real_t> & getValue( const cell_idx_t x, cell_idx_t y, cell_idx_t z ) const { return vel_->get(x,y,z); } + const typename ForceField::value_type & getForce( const cell_idx_t x, cell_idx_t y, cell_idx_t z ) const + { + static_assert(StoreForce, "this member function is only available if the fourth template argument on the class is true"); + return force_->get(x,y,z); + } + private: const FlagUID uid_; PDFField* const pdfField_; shared_ptr<VelField> vel_; + shared_ptr<ForceField> force_; }; // class UBB -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > -inline UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::Velocity::Velocity( const Config::BlockHandle & config ) +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::Velocity::Velocity( const Config::BlockHandle & config ) { velocity_[0] = ( config && config.isDefined( "x" ) ) ? config.getParameter<real_t>( "x" ) : real_c(0.0); velocity_[1] = ( config && config.isDefined( "y" ) ) ? config.getParameter<real_t>( "y" ) : real_c(0.0); @@ -131,8 +140,8 @@ inline UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::Velocity::Ve -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > -inline UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::UBB( const BoundaryUID & boundaryUID, const FlagUID & uid, PDFField* const pdfField, FlagField<flag_t> * const flagField ) : +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::UBB( const BoundaryUID & boundaryUID, const FlagUID & uid, PDFField* const pdfField, FlagField<flag_t> * const flagField ) : Boundary<flag_t>( boundaryUID ), uid_( uid ), pdfField_( pdfField ) { @@ -141,30 +150,42 @@ inline UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::UBB( const B vel_ = make_shared<VelField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), flagField->nrOfGhostLayers(), field::zyxf ); else vel_ = make_shared<VelField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), pdfField_->nrOfGhostLayers(), field::zyxf ); + + if (StoreForce) + force_ = make_shared<ForceField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), pdfField_->nrOfGhostLayers(), field::zyxf ); +} + + + +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::beforeBoundaryTreatment() const +{ + if (StoreForce) + force_->setWithGhostLayer( Vector3<real_t>() ); } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > template< typename Buffer_T > -inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::packCell( Buffer_T & buffer, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) const +inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::packCell( Buffer_T & buffer, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) const { buffer << vel_->get( x, y, z ); } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > template< typename Buffer_T > -inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registerCell( Buffer_T & buffer, const flag_t, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) +inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::registerCell( Buffer_T & buffer, const flag_t, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) { buffer >> vel_->get( x, y, z ); } -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > -inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registerCell( const flag_t, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::registerCell( const flag_t, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const BoundaryConfiguration & velocity ) { WALBERLA_ASSERT_EQUAL( dynamic_cast< const Velocity * >( &velocity ), &velocity ); @@ -177,8 +198,8 @@ inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registe -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > -inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registerCells( const flag_t, const CellInterval & cells, const BoundaryConfiguration & velocity ) +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > +inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::registerCells( const flag_t, const CellInterval & cells, const BoundaryConfiguration & velocity ) { WALBERLA_ASSERT_EQUAL( dynamic_cast< const Velocity * >( &velocity ), &velocity ); WALBERLA_ASSERT_NOT_NULLPTR( vel_ ); @@ -191,9 +212,9 @@ inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registe -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > template< typename CellIterator > -inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registerCells( const flag_t, const CellIterator & begin, const CellIterator & end, +inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::registerCells( const flag_t, const CellIterator & begin, const CellIterator & end, const BoundaryConfiguration & velocity ) { WALBERLA_ASSERT_EQUAL( dynamic_cast< const Velocity * >( &velocity ), &velocity ); @@ -207,12 +228,12 @@ inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::registe -template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce > +template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce, bool StoreForce > #ifndef NDEBUG -inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::treatDirection( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const stencil::Direction dir, +inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::treatDirection( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const stencil::Direction dir, const cell_idx_t nx, const cell_idx_t ny, const cell_idx_t nz, const flag_t mask ) #else -inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::treatDirection( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const stencil::Direction dir, +inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::treatDirection( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const stencil::Direction dir, const cell_idx_t nx, const cell_idx_t ny, const cell_idx_t nz, const flag_t /*mask*/ ) #endif { @@ -223,6 +244,8 @@ inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::treatDi 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 (UBB) + const real_t pdf_old = pdfField_->get( x, y, z, Stencil::idx[dir] ); + if( LatticeModel_T::compressible ) { const auto density = pdfField_->getDensity(x,y,z); @@ -246,6 +269,15 @@ inline void UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce >::treatDi real_c(stencil::cy[ dir ]) * velocity[1] + real_c(stencil::cz[ dir ]) * velocity[2] ) ); } + + if (StoreForce && pdfField_->isInInnerPart( Cell(x,y,z) )) + { + const real_t forceMEM = pdf_old + pdfField_->get( nx, ny, nz, Stencil::invDirIdx(dir) ); + Vector3<real_t> force( real_c( stencil::cx[dir] ) * forceMEM, + real_c( stencil::cy[dir] ) * forceMEM, + real_c( stencil::cz[dir] ) * forceMEM ); + force_->get( nx, ny, nz ) += force; + } } diff --git a/tests/lbm/CMakeLists.txt b/tests/lbm/CMakeLists.txt index 7fef30af0..dd9496fce 100644 --- a/tests/lbm/CMakeLists.txt +++ b/tests/lbm/CMakeLists.txt @@ -23,6 +23,13 @@ waLBerla_execute_test( NAME SimpleDiffusionDirichletDef COMMAND $<TARGET_FILE:Si waLBerla_execute_test( NAME SimpleDiffusionDirichletRef COMMAND $<TARGET_FILE:SimpleDiffusionDirichlet> -l 4 -w 4 -o 1.2 -d 3 -t 3 -c 0 -r 1 ) waLBerla_execute_test( NAME SimpleDiffusionDirichletCav COMMAND $<TARGET_FILE:SimpleDiffusionDirichlet> -l 5 -w 3 -o 1.2 -d 3 -t 333 -c 1 ) +waLBerla_compile_test( FILES boundary/BoundaryForce.cpp DEPENDS blockforest timeloop ) +waLBerla_execute_test( NAME BoundaryForceShort COMMAND $<TARGET_FILE:BoundaryForce> 10 PROCESSES 2 CONFIGURATIONS Debug DebugOptimized ) +waLBerla_execute_test( NAME BoundaryForce COMMAND COMMAND $<TARGET_FILE:BoundaryForce> PROCESSES 2 CONFIGURATIONS Release RelWithDbgInfo ) + +waLBerla_compile_test( FILES boundary/BoundaryForceCouette.cpp DEPENDS blockforest timeloop ) +waLBerla_execute_test( NAME BoundaryForceCouetteShort COMMAND $<TARGET_FILE:BoundaryForceCouette> 10 PROCESSES 2 CONFIGURATIONS Debug DebugOptimized ) +waLBerla_execute_test( NAME BoundaryForceCouette COMMAND COMMAND $<TARGET_FILE:BoundaryForceCouette> PROCESSES 2 CONFIGURATIONS Release RelWithDbgInfo ) waLBerla_compile_test( FILES boundary/DiffusionDirichlet.cpp DEPENDS field blockforest geometry timeloop vtk ) waLBerla_execute_test( NAME DiffusionDirichletTest1 COMMAND $<TARGET_FILE:DiffusionDirichlet> -l 16 -w 1 -o 1.2 -v 0.1 -e 0.097711 -t 50 ) diff --git a/tests/lbm/boundary/BoundaryForce.cpp b/tests/lbm/boundary/BoundaryForce.cpp new file mode 100644 index 000000000..08b41401c --- /dev/null +++ b/tests/lbm/boundary/BoundaryForce.cpp @@ -0,0 +1,228 @@ +//====================================================================================================================== +// +// 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 BoundaryForce.cpp +//! \author Michael Kuron <mkuron@icp.uni-stuttgart.de> +//! \brief Calculates the drag force on a spherical no-slip boundary in a flow +//! due to a velocity boundary condition and compares with Stokes' law. +// +//====================================================================================================================== + +#include "blockforest/Initialization.h" +#include "blockforest/communication/UniformBufferedScheme.h" + +#include "boundary/BoundaryHandling.h" + +#include "core/mpi/Environment.h" +#include "core/mpi/MPIManager.h" +#include "core/mpi/Reduce.h" +#include "core/timing/RemainingTimeLogger.h" + +#include "domain_decomposition/SharedSweep.h" + +#include "field/AddToStorage.h" +#include "field/FlagField.h" +#include "field/communication/PackInfo.h" + +#include "geometry/initializer/BoundaryFromBody.h" +#include "geometry/initializer/BoundaryFromDomainBorder.h" +#include "geometry/InitBoundaryHandling.h" + +#include "lbm/boundary/SimpleUBB.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 "timeloop/SweepTimeloop.h" + +using namespace walberla; + + + +////////////// +// TYPEDEFS // +////////////// + +using LatticeModel_T = lbm::D3Q19<lbm::collision_model::TRT>; +using Stencil_T = LatticeModel_T::Stencil; +using CommunicationStencil_T = LatticeModel_T::CommunicationStencil; +using PdfField_T = lbm::PdfField<LatticeModel_T>; + +using flag_t = walberla::uint8_t; +using FlagField_T = FlagField<flag_t>; + +typedef lbm::SimpleUBB< LatticeModel_T, flag_t, false, true > UBB_Sphere_T; +typedef lbm::SimpleUBB< LatticeModel_T, flag_t > UBB_Wall_T; +typedef boost::tuples::tuple< UBB_Sphere_T, UBB_Wall_T > BoundaryConditions_T; +typedef BoundaryHandling< FlagField_T, Stencil_T, BoundaryConditions_T > BoundaryHandling_T; + + + +/////////// +// FLAGS // +/////////// + +const FlagUID Fluid_Flag( "fluid" ); +const FlagUID UBB_Sphere_Flag( "sphere"); +const FlagUID UBB_Wall_Flag( "wall" ); + + + +/////////////////////// +// BOUNDARY HANDLING // +/////////////////////// + +class MyBoundaryHandling +{ +public: + + MyBoundaryHandling( const BlockDataID & flagFieldId, const BlockDataID & pdfFieldId, const Vector3<real_t> & velocity ) : + flagFieldId_( flagFieldId ), pdfFieldId_( pdfFieldId ), velocity_( velocity ) {} + + BoundaryHandling_T * operator()( IBlock* const block ) const; + +private: + + const BlockDataID flagFieldId_; + const BlockDataID pdfFieldId_; + + const Vector3<real_t> velocity_; + +}; // class MyBoundaryHandling + +BoundaryHandling_T * MyBoundaryHandling::operator()( IBlock * const block ) const +{ + FlagField_T * flagField = block->getData< FlagField_T >( flagFieldId_ ); + PdfField_T * pdfField = block->getData< PdfField_T > ( pdfFieldId_ ); + + const flag_t fluid = flagField->registerFlag( Fluid_Flag ); // register the fluid flag at the flag field + + return new BoundaryHandling_T( "boundary handling", flagField, fluid, + boost::tuples::make_tuple( UBB_Sphere_T( "sphere", UBB_Sphere_Flag, pdfField, Vector3<real_t>() ), + UBB_Wall_T( "wall", UBB_Wall_Flag, pdfField, velocity_ ) ) ); +} + + + +////////// +// MAIN // +////////// + +int main( int argc, char ** argv ) +{ + mpi::Environment env( argc, argv ); + + int processes( MPIManager::instance()->numProcesses() ); + if( processes != 1 && processes != 2 && processes != 4 && processes != 8 ) + WALBERLA_ABORT( "The number of processes must be equal to either 1, 2, 4, or 8!" ); + + const uint_t xBlocks = ( processes == 8 ) ? uint_c(2) : uint_c(1); + const uint_t yBlocks = ( processes >= 4 ) ? uint_c(2) : uint_c(1); + const uint_t zBlocks = ( processes >= 2 ) ? uint_c(2) : uint_c(1); + + const uint_t L = uint_t(64); + + auto blocks = blockforest::createUniformBlockGrid( xBlocks, yBlocks, zBlocks, + L / xBlocks, + L / yBlocks, + L / zBlocks, + real_c(1), + true, + false, false, true); + + const real_t omega = real_c(0.06); + LatticeModel_T latticeModel = LatticeModel_T( lbm::collision_model::TRT::constructWithMagicNumber( omega ) ); + + const Vector3<real_t> velocity(real_t(0), real_t(0), real_t(0.01)); + BlockDataID pdfFieldId = lbm::addPdfFieldToStorage( blocks, "pdf field", latticeModel ); + BlockDataID flagFieldId = field::addFlagFieldToStorage< FlagField_T >( blocks, "flag field" ); + BlockDataID boundaryHandlingId = blocks->addBlockData< BoundaryHandling_T >( MyBoundaryHandling( flagFieldId, pdfFieldId, velocity ), + "boundary handling" ); + + // set boundary conditions + geometry::initializer::BoundaryFromBody< BoundaryHandling_T > bodyInitializer( *blocks, boundaryHandlingId ); + const real_t R = real_t(5.4); + geometry::Sphere sphere( Vector3<real_t>( real_t(L/2), real_t(L/2), real_t(L/2) ), R ); + bodyInitializer.template init< geometry::Sphere >( sphere, UBB_Sphere_Flag ); + geometry::initializer::BoundaryFromDomainBorder< BoundaryHandling_T > borderInitializer( *blocks, boundaryHandlingId ); + borderInitializer.init( UBB_Wall_Flag, stencil::N, cell_idx_t(-1) ); + borderInitializer.init( UBB_Wall_Flag, stencil::S, cell_idx_t(-1) ); + borderInitializer.init( UBB_Wall_Flag, stencil::E, cell_idx_t(-1) ); + borderInitializer.init( UBB_Wall_Flag, stencil::W, cell_idx_t(-1) ); + geometry::setNonBoundaryCellsToDomain< BoundaryHandling_T >( *blocks, boundaryHandlingId ); + + uint_t timeSteps = uint_c(200); + if( argc > 1 ) + timeSteps = boost::lexical_cast<uint_t>( argv[1] ); + SweepTimeloop timeloop( blocks->getBlockStorage(), timeSteps ); + + blockforest::communication::UniformBufferedScheme< CommunicationStencil_T > communication( blocks ); + communication.addPackInfo( make_shared< lbm::PdfFieldPackInfo< LatticeModel_T > >( pdfFieldId ) ); + + timeloop.add() << BeforeFunction( communication, "communication" ) + << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingId ), "boundary handling" ); + + timeloop.add() << Sweep( makeSharedSweep( lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldId, flagFieldId, Fluid_Flag ) ), "LB stream & collide" ); + + timeloop.addFuncAfterTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" ); + + timeloop.run(); + + // get the force acting on the sphere + real_t fx = real_t(0), fy = real_t(0), fz = real_t(0); + for( auto block = blocks->begin(); block != blocks->end(); ++block ) + { + FlagField_T * flagField = block->getData< FlagField_T >( flagFieldId ); + BoundaryHandling_T * boundaryHandling = block->getData< BoundaryHandling_T >( boundaryHandlingId ); + auto buid = boundaryHandling->getBoundaryUID( UBB_Sphere_Flag ); + auto & bc = boundaryHandling->getBoundaryCondition<UBB_Sphere_T>( buid ); + auto flag = flagField->getFlag(UBB_Sphere_Flag); + + // need to iterate over ghost cells too because they may contain part of the force + WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ_OMP(flagField, omp parallel for schedule(static) reduction(+:fx) reduction(+:fy) reduction(+:fz), + { + if( flagField->isFlagSet(x, y, z, flag)) + { + const Vector3<real_t> & cellForce = bc.getForce(x, y, z); + fx += cellForce[0]; + fy += cellForce[1]; + fz += cellForce[2]; + } + }); + } + Vector3<real_t> force(fx, fy, fz); + + mpi::allReduceInplace( force[0], mpi::SUM ); + mpi::allReduceInplace( force[1], mpi::SUM ); + mpi::allReduceInplace( force[2], mpi::SUM ); + + real_t visc = lbm::collision_model::viscosityFromOmega( omega ); + Vector3<real_t> stokes = 6 * math::PI * visc * R * velocity; + + WALBERLA_LOG_RESULT_ON_ROOT("Expected force: " << stokes); + WALBERLA_LOG_RESULT_ON_ROOT("Actual force: " << force); + real_t err = (force-stokes).length() / stokes.length(); + WALBERLA_LOG_RESULT_ON_ROOT("Relative error: " << (real_t(100)*err) << " %"); + + if( timeSteps > 100 ) + { + WALBERLA_CHECK_LESS(err, 0.1); + WALBERLA_LOG_PROGRESS_ON_ROOT("Test succeeded"); + } + + return EXIT_SUCCESS; +} diff --git a/tests/lbm/boundary/BoundaryForceCouette.cpp b/tests/lbm/boundary/BoundaryForceCouette.cpp new file mode 100644 index 000000000..1acf8f8c7 --- /dev/null +++ b/tests/lbm/boundary/BoundaryForceCouette.cpp @@ -0,0 +1,237 @@ +//====================================================================================================================== +// +// 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 BoundaryForceCouette.cpp +//! \author Christoph Rettinger <christoph.rettinger@fau.de> +//! \brief Calculates the force on top and bottom boundary in plane Couette flow +//! and checks correctness against analytical force +// +//====================================================================================================================== + +#include "blockforest/Initialization.h" +#include "blockforest/communication/UniformBufferedScheme.h" + +#include "boundary/BoundaryHandling.h" + +#include "core/mpi/Environment.h" +#include "core/mpi/MPIManager.h" +#include "core/mpi/Reduce.h" +#include "core/timing/RemainingTimeLogger.h" + +#include "domain_decomposition/SharedSweep.h" + +#include "field/AddToStorage.h" +#include "field/FlagField.h" +#include "field/communication/PackInfo.h" + +#include "geometry/initializer/BoundaryFromDomainBorder.h" +#include "geometry/InitBoundaryHandling.h" + +#include "lbm/boundary/NoSlip.h" +#include "lbm/boundary/SimpleUBB.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 "timeloop/SweepTimeloop.h" + +using namespace walberla; + + + +////////////// +// TYPEDEFS // +////////////// + +using LatticeModel_T = lbm::D3Q19<lbm::collision_model::TRT>; +using Stencil_T = LatticeModel_T::Stencil; +using CommunicationStencil_T = LatticeModel_T::CommunicationStencil; +using PdfField_T = lbm::PdfField<LatticeModel_T>; + +using flag_t = walberla::uint8_t; +using FlagField_T = FlagField<flag_t>; + +typedef lbm::NoSlip< LatticeModel_T, flag_t, true > BottomWall_T; +typedef lbm::SimpleUBB< LatticeModel_T, flag_t, false, true > TopWall_T; +typedef boost::tuples::tuple< BottomWall_T, TopWall_T > BoundaryConditions_T; +typedef BoundaryHandling< FlagField_T, Stencil_T, BoundaryConditions_T > BoundaryHandling_T; + + + +/////////// +// FLAGS // +/////////// + +const FlagUID Fluid_Flag( "fluid" ); +const FlagUID BottomWall_Flag( "bottom wall" ); +const FlagUID TopWall_Flag( "top wall" ); + + + +/////////////////////// +// BOUNDARY HANDLING // +/////////////////////// + +class MyBoundaryHandling +{ +public: + + MyBoundaryHandling( const BlockDataID & flagFieldId, const BlockDataID & pdfFieldId, const Vector3<real_t> & velocity ) : + flagFieldId_( flagFieldId ), pdfFieldId_( pdfFieldId ), velocity_( velocity ) {} + + BoundaryHandling_T * operator()( IBlock* const block ) const; + +private: + + const BlockDataID flagFieldId_; + const BlockDataID pdfFieldId_; + + const Vector3<real_t> velocity_; + +}; // class MyBoundaryHandling + +BoundaryHandling_T * MyBoundaryHandling::operator()( IBlock * const block ) const +{ + FlagField_T * flagField = block->getData< FlagField_T >( flagFieldId_ ); + PdfField_T * pdfField = block->getData< PdfField_T > ( pdfFieldId_ ); + + const flag_t fluid = flagField->registerFlag( Fluid_Flag ); // register the fluid flag at the flag field + + return new BoundaryHandling_T( "boundary handling", flagField, fluid, + boost::tuples::make_tuple( BottomWall_T( "bottom", BottomWall_Flag, pdfField ), + TopWall_T( "top", TopWall_Flag, pdfField, velocity_ ) ) ); +} + + + +////////// +// MAIN // +////////// + +int main( int argc, char ** argv ) +{ + mpi::Environment env( argc, argv ); + + int processes( MPIManager::instance()->numProcesses() ); + if( processes != 1 && processes != 2 && processes != 4 && processes != 8 ) + WALBERLA_ABORT( "The number of processes must be equal to either 1, 2, 4, or 8!" ); + + const uint_t xBlocks = ( processes == 8 ) ? uint_t(2) : uint_t(1); + const uint_t yBlocks = ( processes >= 4 ) ? uint_t(2) : uint_t(1); + const uint_t zBlocks = ( processes >= 2 ) ? uint_t(2) : uint_t(1); + + const uint_t L = uint_t(16); + + auto blocks = blockforest::createUniformBlockGrid( xBlocks, yBlocks, zBlocks, + L / xBlocks, + L / yBlocks, + L / zBlocks, + real_t(1), + true, + true, true, false); + + const real_t omega = real_c(1); + LatticeModel_T latticeModel = LatticeModel_T( lbm::collision_model::TRT::constructWithMagicNumber( omega ) ); + + const Vector3<real_t> velocity(real_t(0.01), real_t(0), real_t(0)); + BlockDataID pdfFieldId = lbm::addPdfFieldToStorage( blocks, "pdf field", latticeModel ); + BlockDataID flagFieldId = field::addFlagFieldToStorage< FlagField_T >( blocks, "flag field" ); + BlockDataID boundaryHandlingId = blocks->addBlockData< BoundaryHandling_T >( MyBoundaryHandling( flagFieldId, pdfFieldId, velocity ), + "boundary handling" ); + + // set boundary conditions + geometry::initializer::BoundaryFromBody< BoundaryHandling_T > bodyInitializer( *blocks, boundaryHandlingId ); + geometry::initializer::BoundaryFromDomainBorder< BoundaryHandling_T > borderInitializer( *blocks, boundaryHandlingId ); + borderInitializer.init( TopWall_Flag, stencil::T, cell_idx_t(-1) ); + borderInitializer.init( BottomWall_Flag, stencil::B, cell_idx_t(-1) ); + geometry::setNonBoundaryCellsToDomain< BoundaryHandling_T >( *blocks, boundaryHandlingId ); + + uint_t timeSteps = uint_c(2000); + if( argc > 1 ) + timeSteps = boost::lexical_cast<uint_t>( argv[1] ); + SweepTimeloop timeloop( blocks->getBlockStorage(), timeSteps ); + + blockforest::communication::UniformBufferedScheme< CommunicationStencil_T > communication( blocks ); + communication.addPackInfo( make_shared< lbm::PdfFieldPackInfo< LatticeModel_T > >( pdfFieldId ) ); + + timeloop.add() << BeforeFunction( communication, "communication" ) + << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingId ), "boundary handling" ); + + timeloop.add() << Sweep( makeSharedSweep( lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldId, flagFieldId, Fluid_Flag ) ), "LB stream & collide" ); + + timeloop.addFuncAfterTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" ); + + timeloop.run(); + + // get the force acting on the walls + real_t fXBottom = real_t(0), fXTop = real_t(0); + for( auto block = blocks->begin(); block != blocks->end(); ++block ) + { + FlagField_T * flagField = block->getData< FlagField_T >( flagFieldId ); + BoundaryHandling_T * boundaryHandling = block->getData< BoundaryHandling_T >( boundaryHandlingId ); + auto topWallUid = boundaryHandling->getBoundaryUID( TopWall_Flag ); + auto bottomWallUid = boundaryHandling->getBoundaryUID( BottomWall_Flag ); + auto & topBc = boundaryHandling->getBoundaryCondition<TopWall_T>( topWallUid ); + auto & bottomBc = boundaryHandling->getBoundaryCondition<BottomWall_T>( bottomWallUid ); + auto topFlag = flagField->getFlag(TopWall_Flag); + auto bottomFlag = flagField->getFlag(BottomWall_Flag); + + // need to iterate over ghost cells too because they may contain part of the force + WALBERLA_FOR_ALL_CELLS_INCLUDING_GHOST_LAYER_XYZ_OMP(flagField, omp parallel for schedule(static) reduction(+:fXTop) reduction(+:fXBottom), + { + if( flagField->isFlagSet(x, y, z, topFlag)) + { + const Vector3<real_t> & cellForce = topBc.getForce(x, y, z); + fXTop += cellForce[0]; + } + + if( flagField->isFlagSet(x, y, z, bottomFlag)) + { + const Vector3<real_t> & cellForce = bottomBc.getForce(x, y, z); + fXBottom += cellForce[0]; + } + }); + } + + mpi::allReduceInplace( fXTop, mpi::SUM ); + mpi::allReduceInplace( fXBottom, mpi::SUM ); + + real_t viscosity = lbm::collision_model::viscosityFromOmega( omega ); + real_t shearRate = velocity[0] / real_c(L); + real_t wallArea = real_c(L) * real_c(L); + real_t analyticalForce = shearRate * viscosity * wallArea; + + WALBERLA_LOG_RESULT_ON_ROOT("Expected force: " << analyticalForce); + WALBERLA_LOG_RESULT_ON_ROOT("Actual force top: " << fXTop); + WALBERLA_LOG_RESULT_ON_ROOT("Actual force bottom: " << fXBottom); + + real_t errorTop = std::fabs( ( std::fabs(fXTop) - analyticalForce ) / analyticalForce ); + real_t errorBottom = std::fabs( ( std::fabs(fXBottom) - analyticalForce ) / analyticalForce ); + + WALBERLA_LOG_RESULT_ON_ROOT("Relative error top: " << (real_t(100)*errorTop) << " %"); + WALBERLA_LOG_RESULT_ON_ROOT("Relative error bottom: " << (real_t(100)*errorBottom) << " %"); + + + if( timeSteps > 100 ) + { + WALBERLA_CHECK_LESS(errorTop, 0.01, "Error in force at top wall is too high! value = " << fXTop << " vs analytical = " << analyticalForce); + WALBERLA_CHECK_LESS(errorBottom, 0.01, "Error in force at bottom wall is too high! value = " << fXBottom << " vs analytical = " << analyticalForce); + WALBERLA_LOG_PROGRESS_ON_ROOT("Test succeeded"); + } + + return EXIT_SUCCESS; +} -- GitLab