diff --git a/apps/showcases/FreeSurface/CMakeLists.txt b/apps/showcases/FreeSurface/CMakeLists.txt index ad5828ac44a34cbe280895b2b88cb7b5b59bfbd8..4f5f1c389c010a50518d91b7ea467e10ea8e138f 100644 --- a/apps/showcases/FreeSurface/CMakeLists.txt +++ b/apps/showcases/FreeSurface/CMakeLists.txt @@ -1,5 +1,9 @@ waLBerla_link_files_to_builddir( *.prm ) +waLBerla_add_executable(NAME DropInPool + FILES DropInPool.cpp + DEPENDS blockforest boundary core domain_decomposition field lbm timeloop vtk) + waLBerla_add_executable(NAME RisingBubble FILES RisingBubble.cpp DEPENDS blockforest boundary core domain_decomposition field lbm timeloop vtk) diff --git a/apps/showcases/FreeSurface/DropInPool.cpp b/apps/showcases/FreeSurface/DropInPool.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f2dbd21c1ad3b48e590939dbcda4c07547beaf0c --- /dev/null +++ b/apps/showcases/FreeSurface/DropInPool.cpp @@ -0,0 +1,284 @@ +//====================================================================================================================== +// +// 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 DropInPool.cpp +//! \author Christoph Schwarzmeier <christoph.schwarzmeier@fau.de> +// +//====================================================================================================================== + +#include "blockforest/Initialization.h" + +#include "core/Environment.h" + +#include "field/adaptors/AdaptorCreators.h" +#include "field/vtk/FlagFieldCellFilter.h" +#include "field/vtk/FlagFieldMapping.h" +#include "field/vtk/VTKWriter.h" + +#include "lbm/PerformanceLogger.h" +#include "lbm/field/Adaptors.h" +#include "lbm/field/AddToStorage.h" +#include "lbm/free_surface/bubble_model/Geometry.h" +#include "lbm/free_surface/dynamics/SurfaceDynamicsHandler.h" +#include "lbm/free_surface/surface_geometry/SurfaceGeometryHandler.h" +#include "lbm/lattice_model/D3Q19.h" + +#include "vtk/Initialization.h" + +using namespace walberla; + +using CollisionModel_T = lbm::collision_model::SRT; +using LatticeModel_T = lbm::D3Q19< CollisionModel_T, true, lbm::force_model::GuoConstant, 2 >; +using Stencil_T = LatticeModel_T::Stencil; +using PdfField_T = lbm::PdfField< LatticeModel_T >; + +using Comm = blockforest::SimpleCommunication< LatticeModel_T::CommunicationStencil >; + +using flag_t = uint16_t; +using FlagField_T = FlagField< flag_t >; +using FsBoundary = free_surface::FreeSurfaceBoundary< LatticeModel_T, FlagField_T >; + +using ScalarField_T = GhostLayerField< real_t, 1 >; +using VectorField_T = GhostLayerField< Vector3< real_t >, 1 >; + +int main(int argc, char** argv) +{ + Environment walberlaEnv(argc, argv); + + if (argc < 2) { WALBERLA_ABORT("Please specify a parameter file as input argument.") } + + // read domain parameters from parameter file + auto domParam = walberlaEnv.config()->getOneBlock("DomainParameters"); + const Vector3< uint_t > numBlocks = domParam.getParameter< Vector3< uint_t > >("blocks"); + const uint_t dropSize = domParam.getParameter< uint_t >("dropSize"); + const Vector3< uint_t > domainSizeFactor = domParam.getParameter< Vector3< uint_t > >("domainSizeFactor"); + const Vector3< uint_t > domainSize = domainSizeFactor * dropSize; + const Vector3< bool > periodic = domParam.getParameter< Vector3< bool > >("periodic"); + const Vector3< uint_t > cellsPerBlock{ uint_c(ceil(real_c(domainSize[0]) / real_c(numBlocks[0]))), + uint_c(ceil(real_c(domainSize[1]) / real_c(numBlocks[1]))), + uint_c(ceil(real_c(domainSize[2]) / real_c(numBlocks[2]))) }; + + WALBERLA_LOG_DEVEL_VAR_ON_ROOT(numBlocks); + WALBERLA_LOG_DEVEL_VAR_ON_ROOT(dropSize); + WALBERLA_LOG_DEVEL_VAR_ON_ROOT(domainSize); + WALBERLA_LOG_DEVEL_VAR_ON_ROOT(periodic); + WALBERLA_LOG_DEVEL_VAR_ON_ROOT(cellsPerBlock); + + // create uniform block grid + auto blocks = blockforest::createUniformBlockGrid(numBlocks[0], numBlocks[1], numBlocks[2], cellsPerBlock[0], + cellsPerBlock[1], cellsPerBlock[2], real_c(1), true, periodic[0], + periodic[1], periodic[2], true); + + // read simulation parameters from parameter file + auto simParam = walberlaEnv.config()->getOneBlock("SimulationParameters"); + const real_t omega = simParam.getParameter< real_t >("omega"); + const Vector3< real_t > force = simParam.getParameter< Vector3< real_t > >("force"); + + const uint_t timesteps = simParam.getParameter< uint_t >("timesteps"); + const uint_t perfLogInterval = simParam.getParameter< uint_t >("perfLogInterval"); + + const CollisionModel_T collisionModel = lbm::collision_model::SRT(omega); + const real_t viscosity = collisionModel.viscosity(); + + WALBERLA_LOG_DEVEL_VAR_ON_ROOT(omega); + WALBERLA_LOG_DEVEL_VAR_ON_ROOT(force); + WALBERLA_LOG_DEVEL_VAR_ON_ROOT(viscosity); + + // create lattice model + LatticeModel_T latticeModel = LatticeModel_T(collisionModel, lbm::force_model::GuoConstant(force)); + + // add pdf field + const BlockDataID pdfFieldID = lbm::addPdfFieldToStorage(blocks, "PDF field", latticeModel); + + // add fill level field (initialized with 0, i.e., gas everywhere) + const BlockDataID fillFieldID = field::addToStorage< ScalarField_T >(blocks, "Fill level field", real_c(0.0)); + + // add boundary handling + FsBoundary boundary(blocks, pdfFieldID, fillFieldID); + const BlockDataID flagFieldID = boundary.getFlagFieldID(); + const typename FsBoundary::Flags& flags = boundary.getFlagInfo(); + + // add various adaptors (for simplified access to macroscopic quantities) + const BlockDataID densityAdaptorID = + field::addFieldAdaptor< typename lbm::Adaptor< LatticeModel_T >::Density >(blocks, pdfFieldID, "DensityAdaptor"); + const BlockDataID velocityAdaptorID = + field::addFieldAdaptor< typename lbm::Adaptor< LatticeModel_T >::VelocityVector >(blocks, pdfFieldID, + "VelocityVectorAdaptor"); + field::addFieldAdaptor< typename lbm::Adaptor< LatticeModel_T >::Velocity >(blocks, pdfFieldID, "VelocityAdaptor"); + + // initialize a pool of liquid at the bottom of the domain in z-direction + const AABB poolAABB(real_c(0), real_c(0), real_c(0), real_c(domainSize[0]), real_c(domainSize[1]), + real_c(domainSize[2]) * real_c(0.3)); + + for (auto blockIterator = blocks->begin(); blockIterator != blocks->end(); ++blockIterator) + { + ScalarField_T* fillField = blockIterator->getData< ScalarField_T >(fillFieldID); + + // determine the cells that are relevant for a block (still in global coordinates) + const CellInterval relevantCellBB = blocks->getCellBBFromAABB(poolAABB.getIntersection(blockIterator->getAABB())); + + // transform the global coordinates of relevant cells to block local coordinates + CellInterval blockLocalCellBB; + blocks->transformGlobalToBlockLocalCellInterval(blockLocalCellBB, *blockIterator, relevantCellBB); + + // clang-format off + WALBERLA_FOR_ALL_CELLS_IN_INTERVAL_XYZ(blockLocalCellBB, + fillField->get(x,y,z) = real_c(1); + ) // WALBERLA_FOR_ALL_CELLS_IN_INTERVAL_XYZ + // clang-format on + } + + // initialize a cubical drop of liquid in the center of the domain + const real_t dropSizeHalf = real_c(dropSize) * real_c(0.5); + const AABB dropAABB( + real_c(domainSize[0]) * real_c(0.5) - dropSizeHalf, real_c(domainSize[1]) * real_c(0.5) - dropSizeHalf, + real_c(domainSize[2]) * real_c(0.75) - dropSizeHalf, real_c(domainSize[0]) * real_c(0.5) + dropSizeHalf, + real_c(domainSize[1]) * real_c(0.5) + dropSizeHalf, real_c(domainSize[2]) * real_c(0.75) + dropSizeHalf); + + for (auto blockIterator = blocks->begin(); blockIterator != blocks->end(); ++blockIterator) + { + ScalarField_T* fillField = blockIterator->getData< ScalarField_T >(fillFieldID); + + // determine the cells that are relevant for a block (still in global coordinates) + CellInterval relevantCellBB = blocks->getCellBBFromAABB(dropAABB.getIntersection(blockIterator->getAABB())); + + // transform the global coordinates of relevant cells to block local coordinates + CellInterval blockLocalCellBB; + blocks->transformGlobalToBlockLocalCellInterval(blockLocalCellBB, *blockIterator, relevantCellBB); + + // clang-format off + WALBERLA_FOR_ALL_CELLS_IN_INTERVAL_XYZ(blockLocalCellBB, + fillField->get(x,y,z) = real_c(1); + ) // WALBERLA_FOR_ALL_CELLS_IN_INTERVAL_XYZ + // clang-format on + } + + // initialize boundary conditions from config file + auto boundaryParameters = walberlaEnv.config()->getOneBlock("BoundaryParameters"); + boundary.initFromConfig(boundaryParameters); + boundary.initFlagsFromFillLevel(); + + // initial communication + Comm(blocks, pdfFieldID, fillFieldID, flagFieldID)(); + + // add bubble model + const bool disableSplits = true; // necessary if a gas bubble could split + auto bubbleModel = make_shared< bubble_model::BubbleModel >(blocks, disableSplits); + bubbleModel->initFromFillLevelField(fillFieldID); + + // create timeloop + SweepTimeloop tl(blocks, simParam.getParameter< uint_t >("timesteps")); + + // add surface geometry handler + free_surface::SurfaceGeometryHandler< FlagField_T, ScalarField_T, VectorField_T > sgh(blocks, flagFieldID, + fillFieldID); + sgh.setBoundaryFlagUIDs(FsBoundary::noSlipFlagID + FsBoundary::pressureFlag1ID + FsBoundary::pressureFlag2ID + + FsBoundary::outletFlagID + FsBoundary::ubbFlagID + FsBoundary::freeSlipFlagID); + sgh.setContactAngle(simParam.getParameter< real_t >("contactAngle", 90)); + + ConstBlockDataID curvatureField = sgh.getCurvatureFieldID(); + ConstBlockDataID normalField = sgh.getNormalFieldID(); + + sgh.addSweeps(tl); + + // add boundary handling for standard boundaries and free surface boundaries + free_surface::SurfaceDynamicsHandler< LatticeModel_T, FlagField_T > dynamicsHandler( + blocks, pdfFieldID, flagFieldID, fillFieldID, normalField, curvatureField, boundary, bubbleModel, real_c(0)); + + dynamicsHandler.addSweeps(tl); + + // add VTK output + auto vtkConfigFunc = [&](std::vector< shared_ptr< vtk::BlockCellDataWriterInterface > >& writers, + std::map< std::string, vtk::VTKOutput::CellFilter >& filters, + std::map< std::string, vtk::VTKOutput::BeforeFunction >& beforeFuncs) { + using field::VTKWriter; + writers.push_back( + make_shared< VTKWriter< typename lbm::Adaptor< LatticeModel_T >::Density > >(densityAdaptorID, "density")); + writers.push_back(make_shared< VTKWriter< typename lbm::Adaptor< LatticeModel_T >::VelocityVector > >( + velocityAdaptorID, "velocity")); + writers.push_back(make_shared< VTKWriter< PdfField_T, float > >(pdfFieldID, "PDFs")); + writers.push_back(make_shared< VTKWriter< ScalarField_T, float > >(fillFieldID, "fill_levels")); + writers.push_back(make_shared< VTKWriter< ScalarField_T, float > >(curvatureField, "curvature")); + writers.push_back(make_shared< VTKWriter< VectorField_T, float > >(normalField, "normals")); + writers.push_back(make_shared< VTKWriter< FlagField_T, float > >(flagFieldID, "flags")); + + auto flagMapper = + make_shared< field::FlagFieldMapping< FlagField_T, walberla::uint8_t > >(flagFieldID, "mapped_flag_field"); + flagMapper->addMapping(FsBoundary::Flags::interfaceID, 1); + flagMapper->addMapping(FsBoundary::Flags::liquidID, 2); + flagMapper->addMapping(FsBoundary::Flags::gasID, 3); + flagMapper->addMapping(FsBoundary::noSlipFlagID, 4); + flagMapper->addMapping(FsBoundary::pressureFlag1ID, 5); + flagMapper->addMapping(FsBoundary::pressureFlag2ID, 6); + flagMapper->addMapping(FsBoundary::ubbFlagID, 7); + + writers.push_back(flagMapper); + + auto liquidInterfaceFilter = field::FlagFieldCellFilter< FlagField_T >(flagFieldID); + liquidInterfaceFilter.addFlag(FsBoundary::Flags::liquidID); + liquidInterfaceFilter.addFlag(FsBoundary::Flags::interfaceID); + filters["liquidInterfaceFilter"] = liquidInterfaceFilter; + + auto preVTKComm = blockforest::communication::UniformBufferedScheme< stencil::D3Q27 >(blocks); + preVTKComm.addPackInfo(make_shared< field::communication::PackInfo< PdfField_T > >(pdfFieldID)); + preVTKComm.addPackInfo(make_shared< field::communication::PackInfo< FlagField_T > >(flagFieldID)); + preVTKComm.addPackInfo(make_shared< field::communication::PackInfo< ScalarField_T > >(fillFieldID)); + + beforeFuncs["sync"] = preVTKComm; + }; + std::map< std::string, vtk::SelectableOutputFunction > vtkOutputFunctions; + + vtk::initializeVTKOutput(vtkOutputFunctions, vtkConfigFunc, blocks, walberlaEnv.config()); + + for (auto output = vtkOutputFunctions.begin(); output != vtkOutputFunctions.end(); ++output) + { + tl.addFuncBeforeTimeStep(output->second.outputFunction, std::string("VTK: ") + output->first, + output->second.requiredGlobalStates, output->second.incompatibleGlobalStates); + } + + // set velocity and density to zero in obstacle and gas cells (only for visualization purposes) + // the PDF values in these fields are not important and thus not automatically set + bool enableZeroSetter = true; + if (enableZeroSetter) + { + auto function = [&](IBlock* block) { + using namespace free_surface; + PdfField_T* pdfFieldPtr = block->getData< PdfField_T >(pdfFieldID); + FlagField_T* flagFieldPtr = block->getData< FlagField_T >(flagFieldID); + WALBERLA_FOR_ALL_CELLS( + pdfIter, pdfFieldPtr, flagIter, flagFieldPtr, + if (flags.isGas(flagIter) || flags.isObstacle(flagIter)) + pdfFieldPtr->setDensityAndVelocity(pdfIter.cell(), Vector3< real_t >(0), real_c(1.0));) + }; + tl.add() << Sweep(function, "Zerosetter"); + } + + // add logging for computational performance + lbm::PerformanceLogger< FlagField_T > perfLogger(blocks, flagFieldID, FsBoundary::Flags::liquidInterfaceIDs, + perfLogInterval); + tl.addFuncAfterTimeStep(perfLogger, "PerformanceLogger"); + + WcTimingPool timingPool; + + for (uint_t t = 0; t != timesteps; ++t) + { + if (t % uint_c(100) == uint_c(0)) { WALBERLA_LOG_DEVEL_ON_ROOT("Performing timestep=" << t); } + tl.singleStep(timingPool, true); + + if (t % perfLogInterval == uint_c(0) && t > uint_c(0)) { timingPool.logResultOnRoot(); } + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/apps/showcases/FreeSurface/DropInPool.prm b/apps/showcases/FreeSurface/DropInPool.prm new file mode 100644 index 0000000000000000000000000000000000000000..1b28076e1a0b58e75a4b8034576394f669075de8 --- /dev/null +++ b/apps/showcases/FreeSurface/DropInPool.prm @@ -0,0 +1,37 @@ +DomainParameters +{ + blocks < 1, 1, 4 >; + dropSize 10; + domainSizeFactor < 4, 4, 4 >; // values multiplied with dropDiameter + periodic < 0, 0, 0 >; +} + +SimulationParameters +{ + omega 1.8; + force <0, 0, -1e-4>; + initialVelocity < 0, 0, 0 >; + timesteps 4000; + perfLogInterval 1000; +} + +BoundaryParameters +{ + Border { direction all; walldistance -1; NoSlip{} } +} + +VTK { + fluid_field { + writeFrequency 50; + samplingResolution 1; + writers { + velocity; + density; + fill_levels; + curvature; + normals; + flags; + mapped_flag_field; + } + } +} diff --git a/src/lbm/free_surface/surface_geometry/SurfaceGeometryHandler.h b/src/lbm/free_surface/surface_geometry/SurfaceGeometryHandler.h index e07f890fb362fa2b92471c4c4081bc43c194b80a..3e3bbdb48b88b04abb5df306d5b9a397dc092df2 100644 --- a/src/lbm/free_surface/surface_geometry/SurfaceGeometryHandler.h +++ b/src/lbm/free_surface/surface_geometry/SurfaceGeometryHandler.h @@ -93,26 +93,27 @@ class SurfaceGeometryHandler << Sweep(emptySweep, "Deactivated normals sweep") << AfterFunction(Comm(bf_, normalFieldID_), "Communication after normals sweep"); - // curvature computation using local triangulation - CurvatureSweepTR< FlagField_T, ScalarField_T, VectorField_T > curvSweep( - curvatureFieldID_, normalFieldID_, fillFieldID_, flagFieldID_, interfaceUIDs_, boundaryUIDs_, contactAngle_); - tl.add() << Sweep(curvSweep, "Curvature sweep", StateSweep::fullFreeSurface) - << Sweep(emptySweep, "Deactivated curvature sweep") - << AfterFunction(Comm(bf_, curvatureFieldID_), "Communication after curvature sweep"); - - // // extrapolation of normals to interface neighboring cells (required for computing the curvature) - // ExtrapolateNormalsSweep< FlagField_T, VectorField_T > extNormalsSweep(normalFieldID_, flagFieldID_, - // interfaceUIDs_); - // tl.add() << Sweep(extNormalsSweep, "Extrapolate normals sweep", StateSweep::fullFreeSurface) - // << Sweep(emptySweep, "Deactivated extrapolate normals sweep") - // << AfterFunction(Comm(bf_, normalFieldID_), "Communication after extrapolate normals sweep"); - - // // curvature computation using finite differences - // CurvatureSweepFD< FlagField_T, ScalarField_T, VectorField_T > curvSweep( - // curvatureFieldID_, normalFieldID_, flagFieldID_, interfaceUIDs_, boundaryUIDs_, contactAngle_); + // // curvature computation using local triangulation + // CurvatureSweepTR< FlagField_T, ScalarField_T, VectorField_T > curvSweep( + // curvatureFieldID_, normalFieldID_, fillFieldID_, flagFieldID_, interfaceUIDs_, boundaryUIDs_, + // contactAngle_); // tl.add() << Sweep(curvSweep, "Curvature sweep", StateSweep::fullFreeSurface) // << Sweep(emptySweep, "Deactivated curvature sweep") // << AfterFunction(Comm(bf_, curvatureFieldID_), "Communication after curvature sweep"); + + // extrapolation of normals to interface neighboring cells (required for computing the curvature) + ExtrapolateNormalsSweep< FlagField_T, VectorField_T > extNormalsSweep(normalFieldID_, flagFieldID_, + interfaceUIDs_); + tl.add() << Sweep(extNormalsSweep, "Extrapolate normals sweep", StateSweep::fullFreeSurface) + << Sweep(emptySweep, "Deactivated extrapolate normals sweep") + << AfterFunction(Comm(bf_, normalFieldID_), "Communication after extrapolate normals sweep"); + + // curvature computation using finite differences + CurvatureSweepFD< FlagField_T, ScalarField_T, VectorField_T > curvSweep( + curvatureFieldID_, normalFieldID_, flagFieldID_, interfaceUIDs_, boundaryUIDs_, contactAngle_); + tl.add() << Sweep(curvSweep, "Curvature sweep", StateSweep::fullFreeSurface) + << Sweep(emptySweep, "Deactivated curvature sweep") + << AfterFunction(Comm(bf_, curvatureFieldID_), "Communication after curvature sweep"); } private: