diff --git a/include/sfg/IrregularFreeSlip.hpp b/include/sfg/IrregularFreeSlip.hpp index 54fecce734bfae3701a7c418505f948b8b245c51..3409d630bed0d106c2be551e9ebfbcd403f9a883 100644 --- a/include/sfg/IrregularFreeSlip.hpp +++ b/include/sfg/IrregularFreeSlip.hpp @@ -32,7 +32,7 @@ namespace walberla::sfg source_y{sourceCell.y()}, source_z{sourceCell.z()}, source_dir{int32_t(sourceDir)} {} - bool operator==(const IrregularFreeSlipLinkInfo &other) + bool operator==(const IrregularFreeSlipLinkInfo &other) const { return std::tie(x, y, z, dir, source_x, source_y, source_z, source_dir) == std::tie(other.x, other.y, other.z, other.dir, other.source_x, @@ -47,10 +47,11 @@ namespace walberla::sfg { public: using IndexList = SparseIndexList<IrregularFreeSlipLinkInfo>; + using flag_t = typename FlagField_T::flag_t; FreeSlipLinksFromFlagField( StructuredBlockForest &sbfs, - ConstBlockDataID flagFieldID, + BlockDataID flagFieldID, field::FlagUID boundaryFlagUID, field::FlagUID fluidFlagUID) : sbfs_{sbfs}, flagFieldID_(flagFieldID), boundaryFlagUID_(boundaryFlagUID), fluidFlagUID_(fluidFlagUID) {} @@ -61,12 +62,15 @@ namespace walberla::sfg for (auto &block : sbfs_) { FlagField_T *flagField = block.getData<FlagField_T>(flagFieldID_); + flag_t boundaryFlag { flagField->getFlag(boundaryFlagUID_) }; + flag_t fluidFlag { flagField->getFlag(fluidFlagUID_) }; + auto &idxVector = indexList.get(block); for (auto it = flagField->beginXYZ(); it != flagField->end(); ++it) { Cell c{it.cell()}; - if (!isFlagSet(it, fluidFlagUID_)) + if (!isFlagSet(it, fluidFlag)) { continue; } @@ -75,18 +79,21 @@ namespace walberla::sfg { stencil::Direction dir{*dIt}; Cell neighbor{c + dir}; - if (flagField->isFlagSet(neighbor, boundaryFlagUID_)) + if (flagField->isFlagSet(neighbor, boundaryFlag)) { idxVector.emplace_back(createLink(flagField, c, dir)); } } } } + + return indexList; } private: IrregularFreeSlipLinkInfo createLink(FlagField_T *flagField, const Cell &fluidCell, const stencil::Direction dir) { + const flag_t fluidFlag { flagField->getFlag(fluidFlagUID_) }; const Cell wallCell{fluidCell + dir}; // inverse direction of 'dir' as lattice vector @@ -101,17 +108,17 @@ namespace walberla::sfg cell_idx_t wny = 0; cell_idx_t wnz = 0; - if (flagField->isFlagSet(wallCell.x() + ix, wallCell.y(), wallCell.z(), fluidFlagUID_)) + if (flagField->isFlagSet(wallCell.x() + ix, wallCell.y(), wallCell.z(), fluidFlag)) { wnx = ix; sourceDir = stencil::mirrorX[sourceDir]; } - if (flagField->isFlagSet(wallCell.x(), wallCell.y() + iy, wallCell.z(), fluidFlagUID_)) + if (flagField->isFlagSet(wallCell.x(), wallCell.y() + iy, wallCell.z(), fluidFlag)) { wny = iy; sourceDir = stencil::mirrorY[sourceDir]; } - if (flagField->isFlagSet(wallCell.x(), wallCell.y(), wallCell.z() + iz, fluidFlagUID_)) + if (flagField->isFlagSet(wallCell.x(), wallCell.y(), wallCell.z() + iz, fluidFlag)) { wnz = iz; sourceDir = stencil::mirrorZ[sourceDir]; @@ -139,7 +146,7 @@ namespace walberla::sfg } StructuredBlockForest &sbfs_; - const ConstBlockDataID flagFieldID_; + const BlockDataID flagFieldID_; const field::FlagUID boundaryFlagUID_; const field::FlagUID fluidFlagUID_; }; @@ -148,23 +155,29 @@ namespace walberla::sfg template <typename Impl> class IrregularFreeSlipFactory { - using Stencil = typename Impl::Stencil; - using Sweep = typename Impl::Sweep; - public: IrregularFreeSlipFactory(const shared_ptr<StructuredBlockForest> blocks, BlockDataID pdfFieldID) : blocks_{blocks}, pdfFieldID_{pdfFieldID} {} template <typename FlagField_T> - Sweep fromFlagField(BlockDataID flagFieldID, field::FlagUID boundaryFlagUID, field::FlagUID fluidFlagUID) + auto fromFlagField(BlockDataID flagFieldID, field::FlagUID boundaryFlagUID, field::FlagUID fluidFlagUID) { - detail::FreeSlipLinksFromFlagField< Stencil, FlagField_T > linksFromFlagField{ *blocks_, flagFieldID, boundaryFlagUID, fluidFlagUID}; + detail::FreeSlipLinksFromFlagField< typename Impl::Stencil, FlagField_T > linksFromFlagField{ *blocks_, flagFieldID, boundaryFlagUID, fluidFlagUID}; auto indexVector = linksFromFlagField.collectLinks(); - return Impl::irregularFromIndexVector(indexVector); + return impl().irregularFromIndexVector(indexVector); } protected: shared_ptr<StructuredBlockForest> blocks_; BlockDataID pdfFieldID_; + + private: + Impl & impl() { + return *static_cast< Impl * >(this); + } + + const Impl & impl() const { + return *static_cast< const Impl * >(this); + } }; } diff --git a/src/sfg_walberla/boundaries/freeslip.py b/src/sfg_walberla/boundaries/freeslip.py index 0f0b1b8742ea71c859b92b3d3dd51aba1eaeebe9..2f2b8959a6c9f87c8199a265fe6c67dae113b9d3 100644 --- a/src/sfg_walberla/boundaries/freeslip.py +++ b/src/sfg_walberla/boundaries/freeslip.py @@ -81,16 +81,21 @@ class FreeSlip(CustomGenerator): stencil_name = self._method.stencil.name sfg.include(f"stencil/{stencil_name}.h") - sfg.klass(factory_name, bases=[factory_crtp_base])( - sfg.private( + sfg.klass(factory_name, bases=[f"public {factory_crtp_base}"])( + sfg.public( f"using Base = {factory_crtp_base};", - "friend class Base;", + f"friend class {factory_crtp_base};", f"using Stencil = walberla::stencil::{stencil_name};", f"using Sweep = {sweep_type.get_dtype().c_string()};", - sfg.method("irregularFromIndexVector", returns=sweep_type.get_dtype(), inline=True)( - sfg.expr("return {};", sweep_type.ctor(**sweep_ctor_args)) - ), - ) + "using Base::Base;", + ), + sfg.private( + sfg.method( + "irregularFromIndexVector", + returns=sweep_type.get_dtype(), + inline=True, + )(sfg.expr("return {};", sweep_type.ctor(**sweep_ctor_args))), + ), ) def _generate_regular(self, sfg: SfgComposer): diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 22ae8c4e78cc00f11fb5cfe403ec6e7f73ada970..cff7ae0de942c8d0e49d56ea8d6a80f977818c54 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -19,5 +19,3 @@ add_subdirectory(${CMAKE_SOURCE_DIR}/.. ${CMAKE_BINARY_DIR}/sfg-walberla) # Test Directories include(CTest) - -add_subdirectory( boundary_sweeps ) diff --git a/tests/boundary_sweeps/CMakeLists.txt b/tests/boundary_sweeps/CMakeLists.txt deleted file mode 100644 index 91aa645bd3e208886170f6c9d5e5eb118fbc7afa..0000000000000000000000000000000000000000 --- a/tests/boundary_sweeps/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ - -add_executable( TestBoundarySweeps TestBoundarySweeps.cpp ) - -walberla_generate_sources( TestBoundarySweeps SCRIPTS FreeSlip.py ) -target_link_libraries( TestBoundarySweeps core blockforest field sfg_walberla ) diff --git a/tests/boundary_sweeps/FreeSlip.py b/tests/boundary_sweeps/FreeSlip.py deleted file mode 100644 index abf2f03c718264d9d66f5e30e236631bb9c8dd13..0000000000000000000000000000000000000000 --- a/tests/boundary_sweeps/FreeSlip.py +++ /dev/null @@ -1,16 +0,0 @@ -from pystencilssfg import SourceFileGenerator - -from pystencils import fields -from lbmpy import create_lb_method, Stencil, LBMConfig - -from sfg_walberla.boundaries import FreeSlip, IRREGULAR - -with SourceFileGenerator() as sfg: - sfg.namespace("gen") - - lb_config = LBMConfig(stencil=Stencil.D3Q19) - lb_method = create_lb_method(lbm_config=lb_config) - pdf_field = fields("f(19): double[3D]") - - freeslip = FreeSlip("FreeSlip", lb_method, pdf_field, wall_normal=IRREGULAR) - sfg.generate(freeslip) diff --git a/tests/boundary_sweeps/TestBoundarySweeps.cpp b/tests/boundary_sweeps/TestBoundarySweeps.cpp deleted file mode 100644 index 734d70211e34089c1d63b13c171a2bc777ae469d..0000000000000000000000000000000000000000 --- a/tests/boundary_sweeps/TestBoundarySweeps.cpp +++ /dev/null @@ -1,6 +0,0 @@ - -#include "gen/FreeSlip.hpp" - -int main(void) { - -} diff --git a/user_manual/examples/CMakeLists.txt b/user_manual/examples/CMakeLists.txt index f498cc3603a44d597f611da0530ef297a238f97a..499f1f083bf4419e3f4c8d72c7ccb55123500ad4 100644 --- a/user_manual/examples/CMakeLists.txt +++ b/user_manual/examples/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(${CMAKE_SOURCE_DIR}/../.. ${CMAKE_BINARY_DIR}/sfg-walberla) add_subdirectory( GeneratorScriptBasics ) add_subdirectory( ForceDrivenChannel ) add_subdirectory( SparseSpiral ) +add_subdirectory( FreeSlipBubble ) add_custom_target( Examples ) add_dependencies( @@ -23,4 +24,5 @@ add_dependencies( Ex_GeneratorScriptBasics Ex_ForceDrivenChannel SparseSpiral + FreeSlipBubble ) diff --git a/user_manual/examples/FreeSlipBubble/CMakeLists.txt b/user_manual/examples/FreeSlipBubble/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..cd3e7af346973a31ce122c55531821489e74ce14 --- /dev/null +++ b/user_manual/examples/FreeSlipBubble/CMakeLists.txt @@ -0,0 +1,6 @@ +walberla_link_files_to_builddir( *.prm ) + +add_executable( FreeSlipBubble FreeSlipBubble.cpp ) + +walberla_generate_sources( FreeSlipBubble SCRIPTS LbmAlgorithms.py ) +target_link_libraries( FreeSlipBubble core blockforest field geometry stencil sfg_walberla ) diff --git a/user_manual/examples/FreeSlipBubble/FreeSlipBubble.cpp b/user_manual/examples/FreeSlipBubble/FreeSlipBubble.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b6f6990c89189f4ad6ecab41f4818370ca0b61e --- /dev/null +++ b/user_manual/examples/FreeSlipBubble/FreeSlipBubble.cpp @@ -0,0 +1,157 @@ +#include "blockforest/all.h" +#include "blockforest/communication/UniformBufferedScheme.h" + +#include "core/all.h" + +#include "domain_decomposition/SharedSweep.h" + +#include "field/all.h" +#include "field/communication/PackInfo.h" +#include "field/communication/StencilRestrictedPackInfo.h" + +#include "geometry/InitBoundaryHandling.h" + +#include "stencil/all.h" + +#include "timeloop/all.h" + +#include "vtk/all.h" + +#include "gen/LbmAlgorithms.hpp" + +namespace FreeSlipBubble +{ + using namespace walberla; + using namespace blockforest; + + using std::make_unique; + using std::shared_ptr; + + using ScalarField_T = field::GhostLayerField<real_t, 1>; + using VectorField_T = field::GhostLayerField<real_t, 3>; + + using LbStencil = stencil::D3Q19; + using PdfField_T = field::GhostLayerField<real_t, LbStencil::Q>; + + using CommScheme = blockforest::communication::UniformBufferedScheme<LbStencil>; + using PdfsPackInfo = field::communication::StencilRestrictedPackInfo<PdfField_T, LbStencil>; + + using FlagField_T = FlagField<uint8_t>; + + void run(const shared_ptr<Config> &config) + { + auto blocks = createUniformBlockGridFromConfig(config); + + Config::BlockHandle simParams = config->getBlock("Parameters"); + const real_t omega{simParams.getParameter<real_t>("omega")}; + const real_t density{simParams.getParameter<real_t>("density", 1.0)}; + const Vector3<real_t> velocity = simParams.getParameter<Vector3<real_t>>("velocity", Vector3<real_t>{0.05, 0.0, 0.0}); + + BlockDataID pdfsId = field::addToStorage<PdfField_T>(blocks, "pdfs", real_c(0.0), field::fzyx, 1); + BlockDataID rhoId = field::addToStorage<ScalarField_T>(blocks, "rho", real_c(1.0), field::fzyx, 0); + BlockDataID uId = field::addToStorage<VectorField_T>(blocks, "u", real_c(0.0), field::fzyx, 0); + + gen::LbInit lbInit{pdfsId, density, velocity}; + + for (auto &block : *blocks) + { + lbInit(&block); + } + + // Stream-Collide + + auto streamCollide = make_shared<gen::LbStreamCollide>(pdfsId, rhoId, uId, omega); + + // Communication Setup + + CommScheme comm{blocks}; + auto pdfsPackInfo = std::make_shared<PdfsPackInfo>(pdfsId); + comm.addPackInfo(pdfsPackInfo); + + // Boundary Conditions + + const BlockDataID flagFieldId = field::addFlagFieldToStorage<FlagField_T>(blocks, "flagField"); + const FlagUID fluidFlagUid{"Fluid"}; + const FlagUID freeSlipFlagUID{"FreeSlip"}; + + const CellInterval allCells{{0, 0, 0}, {blocks->getNumberOfXCellsPerBlock() - 1, blocks->getNumberOfYCellsPerBlock() - 1, blocks->getNumberOfZCellsPerBlock() - 1}}; + const Vector3< real_t > sphereCenter { 16., 16., 16. }; + const real_t sphereRadius { 8. }; + + for (auto &block : *blocks) + { + FlagField_T &flagField = *block.getData<FlagField_T>(flagFieldId); + const uint8_t freeSlipFlag{flagField.getOrRegisterFlag(freeSlipFlagUID)}; + + PdfField_T & pdfField = *block.getData< PdfField_T >(pdfsId); + + for (Cell c: allCells){ + Cell globalCell { c }; + blocks->transformBlockLocalToGlobalCell( globalCell, block ); + Vector3< real_t > cellCenter { blocks->getCellCenter(globalCell) }; + + if((cellCenter - sphereCenter).sqrLength() < sphereRadius * sphereRadius){ + flagField.addFlag(c, freeSlipFlag); + for( uint_t q = 0; q < 19; ++q ){ + pdfField.get(c, q) = NAN; + } + } + } + } + + geometry::setNonBoundaryCellsToDomain<FlagField_T>(*blocks, flagFieldId, fluidFlagUid); + + auto flagFieldOutput = field::createVTKOutput<FlagField_T>(flagFieldId, *blocks, "flagField", 1, 0); + flagFieldOutput(); + + auto freeSlip = gen::FreeSlipFactory(blocks, pdfsId).fromFlagField<FlagField_T>(flagFieldId, freeSlipFlagUID, fluidFlagUid); + + // Timeloop + const uint_t numTimesteps{simParams.getParameter<uint_t>("timesteps")}; + SweepTimeloop loop{blocks->getBlockStorage(), numTimesteps}; + + loop.add() << BeforeFunction(comm) << Sweep(makeSharedSweep(streamCollide)); + loop.add() << Sweep(freeSlip); + + RemainingTimeLogger logger{numTimesteps}; + loop.addFuncAfterTimeStep(logger); + + // VTK Output + + Config::BlockHandle outputParams = config->getBlock("Output"); + + const uint_t vtkWriteFrequency = outputParams.getParameter<uint_t>("vtkWriteFrequency", 0); + if (vtkWriteFrequency > 0) + { + auto vtkOutput = vtk::createVTKOutput_BlockData(*blocks, "vtk", vtkWriteFrequency, 0, false, "vtk_out", + "simulation_step", false, true, true, false, 0); + + auto densityWriter = make_shared<field::VTKWriter<ScalarField_T, float32>>(rhoId, "density"); + vtkOutput->addCellDataWriter(densityWriter); + + auto velWriter = make_shared<field::VTKWriter<VectorField_T, float32>>(uId, "velocity"); + vtkOutput->addCellDataWriter(velWriter); + + const cell_idx_t xCells = cell_idx_c(blocks->getNumberOfXCells()); + const cell_idx_t yCells = cell_idx_c(blocks->getNumberOfYCells()); + const cell_idx_t zCells = cell_idx_c(blocks->getNumberOfZCells()); + + loop.addFuncAfterTimeStep(vtk::writeFiles(vtkOutput), "VTK Output"); + } + + // Run the Simulation + + WALBERLA_LOG_INFO_ON_ROOT("Commencing simulation with " << numTimesteps << " timesteps") + + loop.run(); + } +} + +int main(int argc, char **argv) +{ + walberla::Environment env{argc, argv}; + + FreeSlipBubble::run(env.config()); + + return EXIT_SUCCESS; +} diff --git a/user_manual/examples/FreeSlipBubble/LbmAlgorithms.py b/user_manual/examples/FreeSlipBubble/LbmAlgorithms.py new file mode 100644 index 0000000000000000000000000000000000000000..1d6678f3121c38964a0d8b6c314f0cb77c16bcf5 --- /dev/null +++ b/user_manual/examples/FreeSlipBubble/LbmAlgorithms.py @@ -0,0 +1,50 @@ +import sympy as sp +from pystencilssfg import SourceFileGenerator + +from pystencils import fields, Field +from lbmpy import LBStencil, Stencil, LBMConfig, LBMOptimisation, create_lb_update_rule +from lbmpy.macroscopic_value_kernels import macroscopic_values_setter + + +from sfg_walberla import Sweep +from sfg_walberla.boundaries import FreeSlip, IRREGULAR + +with SourceFileGenerator() as sfg: + sfg.namespace("FreeSlipBubble::gen") + + stencil = LBStencil(Stencil.D3Q19) + d, q = stencil.D, stencil.Q + f: Field + f_tmp: Field + f, f_tmp, rho, u = fields( + f"f({q}), f_tmp({q}), rho, u({d}): double[{d}D]", layout="fzyx" + ) # type: ignore + + lbm_config = LBMConfig( + stencil=Stencil.D3Q19, + output={"density": rho, "velocity": u}, + ) + lbm_opt = LBMOptimisation( + symbolic_field=f, + symbolic_temporary_field=f_tmp, + ) + + lb_update = create_lb_update_rule(lbm_config=lbm_config, lbm_optimisation=lbm_opt) + assert lb_update is not None + + lb_update_sweep = Sweep("LbStreamCollide", lb_update) + lb_update_sweep.swap_fields(f, f_tmp) + sfg.generate(lb_update_sweep) + + lb_init = macroscopic_values_setter( + lb_update.method, + density=sp.Symbol("density"), + velocity=sp.symbols(f"velocity_:{d}"), + pdfs=f, + ) + lb_init_sweep = Sweep("LbInit", lb_init) + sfg.generate(lb_init_sweep) + + freeslip = FreeSlip("FreeSlip", lb_update.method, f, wall_normal=IRREGULAR) + sfg.generate(freeslip) + diff --git a/user_manual/examples/FreeSlipBubble/Periodic.prm b/user_manual/examples/FreeSlipBubble/Periodic.prm new file mode 100644 index 0000000000000000000000000000000000000000..8bf1dace34ec190e4b79df77f7daa97927a84c56 --- /dev/null +++ b/user_manual/examples/FreeSlipBubble/Periodic.prm @@ -0,0 +1,17 @@ +DomainSetup +{ + blocks < 1, 1, 1 >; + cellsPerBlock < 32, 32, 32 >; + periodic < 1, 1, 1 >; +} + +Parameters +{ + omega 1.0; + timesteps 200; +} + +Output +{ + vtkWriteFrequency 10; +}