diff --git a/apps/tutorials/lbm/01_BasicLBM.cpp b/apps/tutorials/lbm/01_BasicLBM.cpp index 1380a1f10e052d8f09032cda1e943698a193d668..ada14422b7ea0d2ac4b73e93ec206621ad97b19b 100644 --- a/apps/tutorials/lbm/01_BasicLBM.cpp +++ b/apps/tutorials/lbm/01_BasicLBM.cpp @@ -28,12 +28,20 @@ #include "gui/all.h" #include "lbm/all.h" #include "timeloop/all.h" +#include "field/interpolators/TrilinearFieldInterpolator.h" +#include "field/interpolators/FieldInterpolatorCreators.h" +#include "field/distributors/DistributorCreators.h" +#include "field/distributors/KernelDistributor.h" +#include "CustomLatticeModel.h" +#include "TurbineModel.h" namespace walberla { -using LatticeModel_T = lbm::D2Q9<lbm::collision_model::SRT>; +//using LatticeModel_T = lbm::D3Q19<lbm::collision_model::SRT>; +using LatticeModel_T = lbm::CustomLatticeModel; + using Stencil_T = LatticeModel_T::Stencil; using CommunicationStencil_T = LatticeModel_T::CommunicationStencil; @@ -42,7 +50,10 @@ using PdfField_T = lbm::PdfField<LatticeModel_T>; using flag_t = walberla::uint8_t; using FlagField_T = FlagField<flag_t>; +using VectorField_T = GhostLayerField<real_t, 3>; +using Vec3Interpolator_T = field::TrilinearFieldInterpolator<VectorField_T, FlagField_T>; +using Vec3Distributor_T = field::KernelDistributor<VectorField_T, FlagField_T>; int main( int argc, char ** argv ) { @@ -52,6 +63,7 @@ int main( int argc, char ** argv ) // read parameters auto parameters = walberlaEnv.config()->getOneBlock( "Parameters" ); + auto turbineCfg = walberlaEnv.config()->getOneBlock( "Turbine" ); const real_t omega = parameters.getParameter< real_t > ( "omega", real_c( 1.4 ) ); const Vector3<real_t> initialVelocity = parameters.getParameter< Vector3<real_t> >( "initialVelocity", Vector3<real_t>() ); @@ -60,10 +72,15 @@ int main( int argc, char ** argv ) const double remainingTimeLoggerFrequency = parameters.getParameter< double >( "remainingTimeLoggerFrequency", 3.0 ); // in seconds // create fields - LatticeModel_T latticeModel = LatticeModel_T( lbm::collision_model::SRT( omega ) ); + //LatticeModel_T latticeModel = LatticeModel_T( lbm::collision_model::SRT( omega ) ); + BlockDataID velocityFieldId = field::addToStorage<VectorField_T>( blocks, "vel field"); + BlockDataID forceFieldId = field::addToStorage<VectorField_T>( blocks, "force field"); + + LatticeModel_T latticeModel( forceFieldId, velocityFieldId, omega ); BlockDataID pdfFieldId = lbm::addPdfFieldToStorage( blocks, "pdf field", latticeModel, initialVelocity, real_t(1) ); BlockDataID flagFieldId = field::addFlagFieldToStorage< FlagField_T >( blocks, "flag field" ); + // create and initialize boundary handling const FlagUID fluidFlagUID( "Fluid" ); @@ -80,7 +97,25 @@ int main( int argc, char ** argv ) geometry::initBoundaryHandling<BHFactory::BoundaryHandling>( *blocks, boundaryHandlingId, boundariesConfig ); geometry::setNonBoundaryCellsToDomain<BHFactory::BoundaryHandling> ( *blocks, boundaryHandlingId ); - // create time loop + + BlockDataID velocityFieldInterpolator = field::addFieldInterpolator<Vec3Interpolator_T, FlagField_T>( + blocks, velocityFieldId, flagFieldId, fluidFlagUID, "Velocity Interpolator" ); + BlockDataID forceFieldDistributor = field::addDistributor<Vec3Distributor_T, FlagField_T>( + blocks, forceFieldId, flagFieldId, fluidFlagUID, "Force distributor" + ); + + + Vector3<real_t> midPoint = turbineCfg.getParameter< Vector3<real_t> >("midPoint"); + const real_t innerRadius = turbineCfg.getParameter<real_t>("innerRadius"); + const real_t outerRadius = turbineCfg.getParameter<real_t>("outerRadius"); + const real_t spacing = turbineCfg.getParameter<real_t>("spacing"); + const real_t drag = turbineCfg.getParameter<real_t>("drag", 1e-2); + const real_t lift = turbineCfg.getParameter<real_t>("lift", 1.0); + TurbineModelLine<Vec3Interpolator_T, Vec3Distributor_T> turbineModel(blocks, midPoint, + innerRadius, outerRadius, spacing, + drag, lift); + + // create time loop SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps ); // create communication for PdfField @@ -90,7 +125,17 @@ int main( int argc, char ** argv ) // add LBM sweep and communication to time loop timeloop.add() << BeforeFunction( communication, "communication" ) << Sweep( BHFactory::BoundaryHandling::getBlockSweep( boundaryHandlingId ), "boundary handling" ); - timeloop.add() << Sweep( makeSharedSweep( lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldId, flagFieldId, fluidFlagUID ) ), "LB stream & collide" ); + timeloop.add() << Sweep( LatticeModel_T::Sweep(pdfFieldId ) ) + << AfterFunction( [&]() { + + for( auto & block : *blocks ) { + auto forceField = block.getData<VectorField_T>( forceFieldId ); + forceField->set( 0.0 ); + } + turbineModel.rotate( 2 * math::M_PI / 360.0 ); + turbineModel.evaluate(velocityFieldInterpolator, forceFieldDistributor); + } ); + //timeloop.add() << Sweep( makeSharedSweep( lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldId, flagFieldId, fluidFlagUID ) ), "LB stream & collide" ); // LBM stability check timeloop.addFuncAfterTimeStep( makeSharedFunctor( field::makeStabilityChecker< PdfField_T, FlagField_T >( walberlaEnv.config(), blocks, pdfFieldId, diff --git a/apps/tutorials/lbm/01_BasicLBM.prm b/apps/tutorials/lbm/01_BasicLBM.prm index 15efce2094b505f5063d99fe84e62847161c9db7..7f9d0094569b72b81823cd123dd95a2de76b4808 100644 --- a/apps/tutorials/lbm/01_BasicLBM.prm +++ b/apps/tutorials/lbm/01_BasicLBM.prm @@ -1,24 +1,37 @@ +DomainSetup +{ + blocks < 1, 1, 1 >; + cellsPerBlock < 120, 30, 30 >; + periodic < 0, 0, 0 >; +} + + +Turbine +{ + midpoint < 20, 15, 15>; + innerRadius 1; + outerRadius 8; + spacing 1; + drag 1e-2; + lift 10; +} + Parameters { omega 1.8; - initialVelocity < 0.1, 0, 0 >; + initialVelocity < 0.05, 0, 0 >; timesteps 10000; - useGui 1; - remainingTimeLoggerFrequency 3; // in seconds + useGui 0; + remainingTimeLoggerFrequency 5; // in seconds } -DomainSetup -{ - blocks < 1, 1, 1 >; - cellsPerBlock < 300, 80, 1 >; - periodic < 0, 0, 1 >; -} + StabilityChecker { - checkFrequency 1; + checkFrequency 100; streamOutput false; vtkOutput true; } @@ -39,23 +52,10 @@ Boundaries - GrayScaleImage BoundaryFromImage.h - RGBAImage BoundaryFromImage.h */ - - Border { direction W; walldistance -1; Velocity0 {} } - Border { direction E; walldistance -1; Pressure1 {} } - Border { direction S,N; walldistance -1; NoSlip {} } - - // load obstacle geometry from file - GrayScaleImage - { - file waLBerla.png; - extrusionCoordinate 2; - rescaleToDomain true; - - BoundaryValueMapping { - NoSlip {} - value 0; - } - } + + Border { direction S,N,T,B; walldistance -1; FreeSlip {} } + Border { direction W; walldistance -1; Velocity0 {} } + Border { direction E; walldistance -1; Pressure1 {} } } diff --git a/apps/tutorials/lbm/CMakeLists.txt b/apps/tutorials/lbm/CMakeLists.txt index 6a36280444bc9db6ba2e96606a4d594f12fe52f2..78ea6b299ce998eff260556699fde21f3a6241da 100644 --- a/apps/tutorials/lbm/CMakeLists.txt +++ b/apps/tutorials/lbm/CMakeLists.txt @@ -1,15 +1,19 @@ waLBerla_link_files_to_builddir( *.prm ) waLBerla_link_files_to_builddir( *.png ) +waLBerla_python_file_generates(CustomLatticeModel.py + CustomLatticeModel.cpp CustomLatticeModel.h) + + waLBerla_add_executable ( NAME 01_BasicLBM - FILES 01_BasicLBM.cpp + FILES 01_BasicLBM.cpp CustomLatticeModel.py DEPENDS blockforest core field lbm geometry timeloop gui ) waLBerla_add_executable ( NAME 02_BasicLBM_ExemplaryExtensions FILES 02_BasicLBM_ExemplaryExtensions.cpp DEPENDS blockforest core field lbm geometry timeloop gui ) - -waLBerla_add_executable ( NAME 03_LBLidDrivenCavity + +waLBerla_add_executable ( NAME 03_LBLidDrivenCavity FILES 03_LBLidDrivenCavity.cpp DEPENDS blockforest boundary core field lbm stencil timeloop vtk ) diff --git a/apps/tutorials/lbm/CustomLatticeModel.py b/apps/tutorials/lbm/CustomLatticeModel.py new file mode 100644 index 0000000000000000000000000000000000000000..01a858c8c9efa13aadf7e7f34dca700d657e2fa3 --- /dev/null +++ b/apps/tutorials/lbm/CustomLatticeModel.py @@ -0,0 +1,25 @@ +import sympy as sp +import pystencils as ps +from lbmpy.creationfunctions import create_lb_method +from pystencils_walberla import CodeGeneration +from lbmpy_walberla import generate_lattice_model, RefinementScaling, generate_boundary + + +with CodeGeneration() as ctx: + omega = sp.Symbol("omega") + velocity_field = ps.fields("velocity(3) : [3D]", layout='fzyx') + force_field = ps.fields("force(3): [3D]", layout='fzyx') + + # lattice Boltzmann method + params = { + 'stencil': 'D3Q19', + 'method': 'srt', + 'relaxation_rates': [omega], + 'smagorinsky': True, + 'output': {'velocity': velocity_field}, + 'force_model': 'guo', + 'force': force_field.center_vector, + } + lb_method = create_lb_method(**params) + generate_lattice_model(ctx, 'CustomLatticeModel', lb_method, + update_rule_params=params) diff --git a/apps/tutorials/lbm/TurbineModel.h b/apps/tutorials/lbm/TurbineModel.h new file mode 100644 index 0000000000000000000000000000000000000000..a70b1c6ccade7efa96ace5cca69f46dad117e9b4 --- /dev/null +++ b/apps/tutorials/lbm/TurbineModel.h @@ -0,0 +1,113 @@ +//====================================================================================================================== +// +// 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 TurbineModel.h +// +//====================================================================================================================== + +#pragma once +#include "core/math/Matrix3.h" +#include "core/math/Vector3.h" +#include "core/math/Rot3.h" + + +namespace walberla { + +template<typename VelocityInterpolator_T, typename ForceDistributor_T> +class TurbineModelLine +{ +public: + TurbineModelLine(const shared_ptr <StructuredBlockStorage> &blocks, + const Vector3 <real_t> midPoint, real_t innerRadius, real_t outerRadius, + real_t pointSpacing, real_t drag, real_t lift) + : blocks_(blocks), midPoint_(midPoint), pointSpacing_(pointSpacing), + drag_(drag), lift_(lift) { + // initialize in z-direction + for (real_t curZ = -outerRadius; curZ <= -innerRadius; curZ += pointSpacing) { + points_.push_back(Vector3<real_t>(real_t(0), real_t(0), curZ) + midPoint_); + } + for (real_t curZ = innerRadius; curZ <= outerRadius; curZ += pointSpacing) { + points_.push_back(Vector3<real_t>(real_t(0), real_t(0), curZ) + midPoint_); + } + } + + // compute forces on fluid, sets them into forceField + void evaluate(ConstBlockDataID velocityInterpolatorId, BlockDataID forceDistributorId) + { + for(auto & block : *blocks_) + { + auto velocity = block.getData<VelocityInterpolator_T>(velocityInterpolatorId); + auto force = block.getData<ForceDistributor_T>(forceDistributorId); + auto blockBB = block.getAABB(); + for( const auto & point : points_ ) + { + if( blockBB.contains(point) ) + { + Vector3<real_t> freeStreamVelocity; + velocity->get( point, freeStreamVelocity.data() ); + auto velMagnitude = freeStreamVelocity.length(); + auto angleOfAttack = real_t(0.0); + + auto drag = real_t(0); + auto lift = real_t(0); + airfoilTable(velMagnitude, angleOfAttack, drag, lift); + + auto wingVec = (point - midPoint_).getNormalized(); + + auto area = pointSpacing_; + + auto forceToDistribute = Vector3<real_t>( + - 0.5 * drag * area * freeStreamVelocity[0] * freeStreamVelocity[0], + - 0.5 * (lift * wingVec[2]) * area * freeStreamVelocity[0] * freeStreamVelocity[0], + - 0.5 * (-lift * wingVec[1]) * area * freeStreamVelocity[0] * freeStreamVelocity[0] + ); + force->distribute( point, forceToDistribute.data() ); + } + //TODO communicate force field with "+=" communication + } + } + } + + void airfoilTable(real_t velocityMagnitude, real_t angleOfAttack, + real_t &drag, real_t &lift) + { + drag = drag_; + lift = lift_; + } + + void rotate(real_t angle) + { + const auto rotationAxis = Vector3<real_t>(real_t(1.0), real_t(0.0), real_t(0.0)); + auto rotationMatrix = Matrix3<real_t>(rotationAxis, angle); + for (auto &point : points_) { + point -= midPoint_; + point = rotationMatrix * point; + point += midPoint_; + } + absoluteAngle_ += angle; + } + +private: + std::vector<Vector3 < real_t> > points_; + shared_ptr <StructuredBlockStorage> blocks_; + Vector3 <real_t> midPoint_; + real_t absoluteAngle_; + real_t pointSpacing_; + real_t drag_; + real_t lift_; +}; + + +} // namespace walberla \ No newline at end of file