diff --git a/apps/showcases/CMakeLists.txt b/apps/showcases/CMakeLists.txt index 5770d193a05532c7285993dba466c5aca7873276..536f56ff6acdbdb102faf394361bb2fd661af6e8 100644 --- a/apps/showcases/CMakeLists.txt +++ b/apps/showcases/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory( BidisperseFluidizedBed ) +add_subdirectory( CombinedResolvedUnresolved ) add_subdirectory( Mixer ) diff --git a/apps/showcases/CombinedResolvedUnresolved/CMakeLists.txt b/apps/showcases/CombinedResolvedUnresolved/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a550c3006289f88fa9abdd26e34bdacb4a2c0b23 --- /dev/null +++ b/apps/showcases/CombinedResolvedUnresolved/CMakeLists.txt @@ -0,0 +1,2 @@ +waLBerla_add_executable ( NAME CombinedResolvedUnresolved + DEPENDS blockforest boundary core domain_decomposition field lbm pe pe_coupling postprocessing timeloop vtk ) diff --git a/apps/showcases/CombinedResolvedUnresolved/CombinedResolvedUnresolved.cpp b/apps/showcases/CombinedResolvedUnresolved/CombinedResolvedUnresolved.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8b0779629654e8e34cb856cf81bcd155ffef87f7 --- /dev/null +++ b/apps/showcases/CombinedResolvedUnresolved/CombinedResolvedUnresolved.cpp @@ -0,0 +1,1344 @@ +//====================================================================================================================== +// +// 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 CombinedResolvedUnresolved.cpp +//! \author Matthias König <matthias.k.koenig@fau.de> +// +//====================================================================================================================== + +#include "blockforest/Initialization.h" +#include "blockforest/communication/UniformBufferedScheme.h" + +#include "boundary/all.h" + +#include "core/DataTypes.h" +#include "core/Environment.h" +#include "core/SharedFunctor.h" +#include "core/debug/Debug.h" +#include "core/debug/TestSubsystem.h" +#include "core/grid_generator/SCIterator.h" +#include "core/logging/all.h" +#include "core/math/all.h" +#include "core/timing/RemainingTimeLogger.h" + +#include "domain_decomposition/SharedSweep.h" + +#include "field/AddToStorage.h" +#include "field/StabilityChecker.h" +#include "field/communication/PackInfo.h" +#include "field/vtk/all.h" + +#include "lbm/boundary/all.h" +#include "lbm/communication/PdfFieldPackInfo.h" +#include "lbm/field/AddToStorage.h" +#include "lbm/field/PdfField.h" +#include "lbm/lattice_model/D3Q19.h" +#include "lbm/sweeps/CellwiseSweep.h" +#include "lbm/sweeps/SweepWrappers.h" +#include "lbm/vtk/all.h" + +#include "pe/Types.h" +#include "pe/basic.h" +#include "pe/cr/ICR.h" +#include "pe/vtk/BodyVtkOutput.h" +#include "pe/vtk/SphereVtkOutput.h" + +#include "pe_coupling/discrete_particle_methods/all.h" +#include "pe_coupling/mapping/all.h" +#include "pe_coupling/momentum_exchange_method/all.h" +#include "pe_coupling/utility/all.h" + +#include "timeloop/SweepTimeloop.h" + +#include "vtk/all.h" + +#include <functional> + +namespace combined_resolved_unresolved +{ +/////////// +// USING // +/////////// + +using namespace walberla; +using walberla::uint_t; + +////////////// +// TYPEDEFS // +////////////// + +// PDF field, flag field & body field + +typedef GhostLayerField< pe::BodyID, 1 > BodyField_T; +typedef GhostLayerField< Matrix3< real_t >, 1 > TensorField_T; +typedef GhostLayerField< Vector3< real_t >, 1 > Vec3Field_T; +typedef GhostLayerField< real_t, 1 > ScalarField_T; +using ForceModel_T = lbm::force_model::GuoField< Vec3Field_T >; + +typedef lbm::D3Q19< lbm::collision_model::SRTField< ScalarField_T >, false, ForceModel_T > LatticeModel_T; +using Stencil_T = LatticeModel_T::Stencil; +using PdfField_T = lbm::PdfField< LatticeModel_T >; + +using flag_t = walberla::uint8_t; +using FlagField_T = FlagField< flag_t >; + +const uint_t FieldGhostLayers = 1; + +// boundary handling +typedef lbm::NoSlip< LatticeModel_T, flag_t > NoSlip_T; + +typedef pe_coupling::CurvedLinear< LatticeModel_T, FlagField_T > MO_T; + +typedef BoundaryHandling< FlagField_T, Stencil_T, NoSlip_T, MO_T > BoundaryHandling_T; + +typedef std::tuple< pe::Sphere, pe::Plane > BodyTypeTuple; + +/////////// +// FLAGS // +/////////// + +const FlagUID Fluid_Flag("fluid"); +const FlagUID NoSlip_Flag("no slip"); +const FlagUID MO_Flag("moving obstacle"); +const FlagUID FormerMO_Flag("former moving obstacle"); + +// interpolation (for PDFs, velocity and/or solid volume fraction) +enum Interpolation { INearestNeighbor, IKernel }; + +// force distribution +enum Distribution { DNearestNeighbor, DKernel }; + +// drag correlation +enum DragCorrelation { ErgunWenYu, Tang, Stokes, Felice, Tenneti, NoDrag }; + +// lift correlation +enum LiftCorrelation { NoLift, Saffman }; + +// added mass correlation +enum AddedMassCorrelation { NoAM, Finn }; + +// effective viscosity +enum EffectiveViscosity { None, Rescaled, Eilers }; + +///////////////////////////////////// +// BOUNDARY HANDLING CUSTOMIZATION // +///////////////////////////////////// +class MyBoundaryHandling +{ + public: + MyBoundaryHandling(const BlockDataID& flagFieldID, const BlockDataID& pdfFieldID, const BlockDataID& bodyFieldID) + : flagFieldID_(flagFieldID), pdfFieldID_(pdfFieldID), bodyFieldID_(bodyFieldID) + {} + + BoundaryHandling_T* operator()(IBlock* const block, const StructuredBlockStorage* const storage) const; + + private: + const BlockDataID flagFieldID_; + const BlockDataID pdfFieldID_; + const BlockDataID bodyFieldID_; + +}; // class MyBoundaryHandling + +BoundaryHandling_T* MyBoundaryHandling::operator()(IBlock* const block, + const StructuredBlockStorage* const storage) const +{ + WALBERLA_ASSERT_NOT_NULLPTR(block); + WALBERLA_ASSERT_NOT_NULLPTR(storage); + + FlagField_T* flagField = block->getData< FlagField_T >(flagFieldID_); + PdfField_T* pdfField = block->getData< PdfField_T >(pdfFieldID_); + BodyField_T* bodyField = block->getData< BodyField_T >(bodyFieldID_); + + const auto fluid = + flagField->flagExists(Fluid_Flag) ? flagField->getFlag(Fluid_Flag) : flagField->registerFlag(Fluid_Flag); + + BoundaryHandling_T* handling = new BoundaryHandling_T( + "moving obstacle boundary handling", flagField, fluid, NoSlip_T("NoSlip", NoSlip_Flag, pdfField), + MO_T("MO", MO_Flag, pdfField, flagField, bodyField, fluid, *storage, *block)); + + // boundary conditions (no-slip) are set by mapping the planes into the domain + + handling->fillWithDomain(FieldGhostLayers); + + return handling; +} + +// VTK Output +shared_ptr< vtk::VTKOutput > createFluidFieldVTKWriter(shared_ptr< StructuredBlockForest >& blocks, + const BlockDataID& pdfFieldID, const BlockDataID& flagFieldID, + uint_t writeFrequency, const std::string& vtkBaseFolder) +{ + auto pdfFieldVTKWriter = + vtk::createVTKOutput_BlockData(blocks, "fluid_field", writeFrequency, uint_t(1), false, vtkBaseFolder); + + blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > pdfGhostLayerSync(blocks); + pdfGhostLayerSync.addPackInfo(make_shared< field::communication::PackInfo< PdfField_T > >(pdfFieldID)); + pdfFieldVTKWriter->addBeforeFunction(pdfGhostLayerSync); + + field::FlagFieldCellFilter< FlagField_T > fluidFilter(flagFieldID); + fluidFilter.addFlag(Fluid_Flag); + pdfFieldVTKWriter->addCellInclusionFilter(fluidFilter); + + auto velocityWriter = + make_shared< lbm::VelocityVTKWriter< LatticeModel_T, float > >(pdfFieldID, "Velocity (Lattice)"); + auto densityWriter = make_shared< lbm::DensityVTKWriter< LatticeModel_T, float > >(pdfFieldID, "Density (Lattice)"); + pdfFieldVTKWriter->addCellDataWriter(velocityWriter); + pdfFieldVTKWriter->addCellDataWriter(densityWriter); + + return pdfFieldVTKWriter; +} + +void createSphereLattice(StructuredBlockForest& forest, pe::BodyStorage& globalBodyStorage, + const BlockDataID& bodyStorageID, const AABB& generationDomain, real_t diameter, + real_t solidVolumeFraction, const pe::MaterialID& material, real_t initialZVelocity) +{ + real_t sphereVolume = math::pi * diameter * diameter * diameter / real_t(6); + real_t numSpheresDesired = solidVolumeFraction * generationDomain.volume() / sphereVolume; + uint_t spheresPerDirection = uint_c(std::cbrt(numSpheresDesired)); + + real_t spacing = generationDomain.xSize() / real_c(spheresPerDirection); + + WALBERLA_ASSERT(spacing >= diameter); + + Vector3< real_t > generationOrigin(generationDomain.xMin() + spacing * real_t(0.5), + generationDomain.yMin() + spacing * real_t(0.5), + generationDomain.zMin() + spacing * real_t(0.5)); + + uint_t numSpheres(0); + + for (auto it = grid_generator::SCIterator(generationDomain, generationOrigin, spacing); + it != grid_generator::SCIterator(); ++it) + { + pe::SphereID sp = pe::createSphere(globalBodyStorage, forest.getBlockStorage(), bodyStorageID, 0, *it, + diameter * real_t(0.5), material); + + if (sp != nullptr) + { + sp->setLinearVel(Vector3< real_t >(real_t(0), real_t(0), initialZVelocity)); + ++numSpheres; + } + } + + WALBERLA_MPI_SECTION() { mpi::allReduceInplace(numSpheres, mpi::SUM); } + + WALBERLA_LOG_INFO_ON_ROOT("Created " << numSpheres << " spheres of diameter " << diameter << " in " + << generationDomain << " with a center-to-center spacing of " << spacing + << " ( " << real_t(100) * spacing / diameter << "% of diameter )"); +} + +pe::MaterialID createSphereMaterial(const std::string& name, real_t diameter, real_t densityRatio) +{ + const real_t restitutionCoeff = real_t(0.88); + const real_t frictionCoeff = real_t(0.25); + + real_t sphereVolume = diameter * diameter * diameter * math::pi / real_t(6); + const real_t particleMass = densityRatio * sphereVolume; + const real_t Mij = particleMass * particleMass / (real_t(2) * particleMass); + const real_t lnDryResCoeff = std::log(restitutionCoeff); + const real_t collisionTime = real_t(10.0); + const real_t stiffnessCoeff = + math::pi * math::pi * Mij / + (collisionTime * collisionTime * + (real_t(1) - lnDryResCoeff * lnDryResCoeff / (math::pi * math::pi + lnDryResCoeff * lnDryResCoeff))); + const real_t dampingCoeff = + -real_t(2) * std::sqrt(Mij * stiffnessCoeff) * + (std::log(restitutionCoeff) / + std::sqrt(math::pi * math::pi + (std::log(restitutionCoeff) * std::log(restitutionCoeff)))); + + return pe::createMaterial(name, densityRatio, restitutionCoeff, frictionCoeff, frictionCoeff, real_t(0), real_t(200), + stiffnessCoeff, dampingCoeff, dampingCoeff); +} + +bool selectDPMBodies(pe::BodyID bodyID) +{ + pe::SphereID sphere = static_cast< pe::SphereID >(bodyID); + real_t radius = sphere->getRadius(); + if (radius <= real_t(0.5)) + return true; + else + return false; +} + +bool selectMEMBodies(pe::BodyID bodyID) { return !selectDPMBodies(bodyID); } + +void resetSphereVelocities(const shared_ptr< StructuredBlockForest >& blocks, const BlockDataID& bodyStorageID, + Vector3< real_t > vel) +{ + for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt) + { + for (auto bodyIt = pe::BodyIterator::begin< pe::Sphere >(*blockIt, bodyStorageID); + bodyIt != pe::BodyIterator::end< pe::Sphere >(); ++bodyIt) + { + bodyIt->setAngularVel(real_t(0), real_t(0), real_t(0)); + bodyIt->setLinearVel(vel); + } + } +} + +class DummySweep +{ + public: + DummySweep() = default; + + void operator()(IBlock* const /*block*/) {} +}; + +void emptyFunction() {} + +//******************************************************************************************************************* +/*!\brief Simualtion of a strongly heterogeneous sized particulate flow system using combined resolved and unresolved + * methods. + * + * For the coupling of resolved particles the Momentum Exchange Method (MEM) is used, whereas for the + * unresolved particles the Discrete Particle Method by Rettinger, Ruede - "A Coupled Lattice Boltzmann Method and + * Discrete Element Method for Discrete Particle Simulations of Particulate Flows" is used. + * + * For the default setup of the showcase a 100 × 100 × 100mm box is filled with a viscous fluid. + * The domain is resolved by a 32 × 32 × 32 lattice. One large MEM particle of diameter Dp_MEM = 35mm + * - which equates to a resolution of 12 lattice cells - is dropped from a gap height of 37.5mm. There + * is a layer 4096 DPM spheres of diameter Dp_DPM = 1mm from height 12.5mm to 25mm which + * generates an average solid volume fraction of svf = 0.017 inside the layer. DPM particles thus have + * a diameter of Dp_DPM = 0.32 cells in lattice units. For the evaluation of forces and fluid properties + * by the DPM part of the algorithm 10 interaction subcycles are used. + * This setup can be customized in the customization section. + * + * The algorithm, as well as the setup and the outcome are described in detail in + * Koenig - "Combining fully resolved and unresolved coupling methods for strongly heterogeneous sized particulate + * flow simulations" to be published + * + */ +//******************************************************************************************************************* + +int main(int argc, char** argv) +{ + debug::enterTestMode(); + + mpi::Environment env(argc, argv); + + /////////////////// + // Customization // + /////////////////// + + // simulation control + uint_t vtkIOFreq = 0; + std::string baseFolder_vtk = "vtk_out_Combined"; + + // physical setup + uint_t fluidType = 1; + + uint_t timesteps = uint_t(5000); + uint_t interactionSubcycles = uint_t(10); + + Interpolation interpol = Interpolation::IKernel; + Distribution dist = Distribution::DKernel; + DragCorrelation dragCorr = DragCorrelation::Tenneti; + LiftCorrelation liftCorr = LiftCorrelation::Saffman; + AddedMassCorrelation addedMassCorr = AddedMassCorrelation::Finn; + EffectiveViscosity effVisc = EffectiveViscosity::Eilers; + + // numerical parameters + uint_t numberOfCellsInHorizontalDirection = uint_t(32); + + for (int i = 1; i < argc; ++i) + { + if (std::strcmp(argv[i], "--vtkIOFreq") == 0) + { + vtkIOFreq = uint_c(std::atof(argv[++i])); + continue; + } + if (std::strcmp(argv[i], "--fluidType") == 0) + { + fluidType = uint_c(std::atof(argv[++i])); + continue; + } + if (std::strcmp(argv[i], "--resolution") == 0) + { + numberOfCellsInHorizontalDirection = uint_c(std::atof(argv[++i])); + continue; + } + if (std::strcmp(argv[i], "--baseFolder_vtk") == 0) + { + baseFolder_vtk = argv[++i]; + continue; + } + if (std::strcmp(argv[i], "--timesteps") == 0) + { + timesteps = uint_c(std::atof(argv[++i])); + continue; + } + if (std::strcmp(argv[i], "--interactionSubcycles") == 0) + { + interactionSubcycles = uint_c(std::atof(argv[++i])); + continue; + } + WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]); + } + + ////////////////////////////////////// + // SIMULATION PROPERTIES in SI units// + ////////////////////////////////////// + + real_t densityFluid_SI; + real_t dynamicViscosityFluid_SI; + real_t expectedSettlingVelocity_SI; + switch (fluidType) + { + case 1: + // Re_p around 1.5 + densityFluid_SI = real_t(970); + dynamicViscosityFluid_SI = real_t(373e-3); + expectedSettlingVelocity_SI = real_t(0.035986); + break; + case 2: + // Re_p around 4.1 + densityFluid_SI = real_t(965); + dynamicViscosityFluid_SI = real_t(212e-3); + expectedSettlingVelocity_SI = real_t(0.05718); + break; + case 3: + // Re_p around 11.6 + densityFluid_SI = real_t(962); + dynamicViscosityFluid_SI = real_t(113e-3); + expectedSettlingVelocity_SI = real_t(0.087269); + break; + case 4: + // Re_p around 31.9 + densityFluid_SI = real_t(960); + dynamicViscosityFluid_SI = real_t(58e-3); + expectedSettlingVelocity_SI = real_t(0.12224); + break; + default: + WALBERLA_ABORT("Only four different fluids are supported! Choose type between 1 and 4."); + } + const real_t kinematicViscosityFluid_SI = dynamicViscosityFluid_SI / densityFluid_SI; + + const real_t diameter_MEM_SI = real_t(35e-3); + const real_t densitySphere_MEM_SI = real_t(1120); + const real_t densityRatio_MEM = densitySphere_MEM_SI / densityFluid_SI; + + const real_t diameter_DPM_SI = real_t(1e-3); + const real_t densitySphere_DPM_SI = real_t(1120); + const real_t densityRatio_DPM = densitySphere_DPM_SI / densityFluid_SI; + + const real_t gravitationalAcceleration_SI = real_t(9.81); + Vector3< real_t > domainSize_SI(real_t(100e-3), real_t(100e-3), real_t(100e-3)); + // shift starting gap a bit upwards to match the reported (plotted) values + const real_t startingGapSize_SI = real_t(20e-3) + real_t(0.5) * diameter_MEM_SI; + + WALBERLA_LOG_INFO_ON_ROOT("Setup (in SI units):"); + WALBERLA_LOG_INFO_ON_ROOT(" - domain size = " << domainSize_SI); + WALBERLA_LOG_INFO_ON_ROOT(" - sphere: diameter_MEM = " << diameter_MEM_SI << ", density = " << densitySphere_MEM_SI + << ", starting gap size = " << startingGapSize_SI); + WALBERLA_LOG_INFO_ON_ROOT(" - fluid: density = " << densityFluid_SI << ", dyn. visc = " << dynamicViscosityFluid_SI + << ", kin. visc = " << kinematicViscosityFluid_SI); + WALBERLA_LOG_INFO_ON_ROOT(" - expected settling velocity = " + << expectedSettlingVelocity_SI << " --> Re_p = " + << expectedSettlingVelocity_SI * diameter_MEM_SI / kinematicViscosityFluid_SI); + + ////////////////////////// + // NUMERICAL PARAMETERS // + ////////////////////////// + + const real_t dx_SI = domainSize_SI[0] / real_c(numberOfCellsInHorizontalDirection); + const Vector3< uint_t > domainSize(uint_c(floor(domainSize_SI[0] / dx_SI + real_t(0.5))), + uint_c(floor(domainSize_SI[1] / dx_SI + real_t(0.5))), + uint_c(floor(domainSize_SI[2] / dx_SI + real_t(0.5)))); + const real_t diameter_MEM = diameter_MEM_SI / dx_SI; + const real_t sphereVolume_MEM = math::pi / real_t(6) * diameter_MEM * diameter_MEM * diameter_MEM; + + const real_t diameter_DPM = diameter_DPM_SI / dx_SI; + + const real_t sphereVolume_DPM = math::pi / real_t(6) * diameter_DPM * diameter_DPM * diameter_DPM; + + const real_t expectedSettlingVelocity = real_t(0.01); + const real_t dt_SI = expectedSettlingVelocity / expectedSettlingVelocity_SI * dx_SI; + + const real_t viscosity = kinematicViscosityFluid_SI * dt_SI / (dx_SI * dx_SI); + const real_t relaxationTime = real_t(1) / lbm::collision_model::omegaFromViscosity(viscosity); + + const real_t gravitationalAcceleration = gravitationalAcceleration_SI * dt_SI * dt_SI / dx_SI; + + const real_t dx = real_t(1); + const real_t dt = real_t(1); + const real_t dtInteractionSubCycle = real_t(dt) / real_t(interactionSubcycles); + const real_t dtBodyVelocityTimeDerivativeEvaluation = dtInteractionSubCycle; + + const Vector3< real_t > initialFluidVelocity(real_t(0)); + + const uint_t numPeSubCycles = uint_t(1); + + WALBERLA_LOG_INFO_ON_ROOT(" - dx_SI = " << dx_SI << ", dt_SI = " << dt_SI); + WALBERLA_LOG_INFO_ON_ROOT("Setup (in simulation, i.e. lattice, units):"); + WALBERLA_LOG_INFO_ON_ROOT(" - domain size = " << domainSize); + WALBERLA_LOG_INFO_ON_ROOT(" - sphere: diameter_MEM = " << diameter_MEM); + WALBERLA_LOG_INFO_ON_ROOT(" - relaxation time (tau) = " << relaxationTime << ", kin. visc = " << viscosity); + WALBERLA_LOG_INFO_ON_ROOT(" - gravitational acceleration = " << gravitationalAcceleration); + WALBERLA_LOG_INFO_ON_ROOT(" - expected settling velocity = " << expectedSettlingVelocity << " --> Re_p = " + << expectedSettlingVelocity * diameter_MEM / viscosity); + + if (vtkIOFreq > 0) + { + WALBERLA_LOG_INFO_ON_ROOT(" - writing vtk files to folder \"" << baseFolder_vtk << "\" with frequency " + << vtkIOFreq); + } + + /////////////////////////// + // BLOCK STRUCTURE SETUP // + /////////////////////////// + + Vector3< uint_t > numberOfBlocksPerDirection(uint_t(2), uint_t(2), uint_t(2)); + Vector3< uint_t > cellsPerBlockPerDirection(domainSize[0] / numberOfBlocksPerDirection[0], + domainSize[1] / numberOfBlocksPerDirection[1], + domainSize[2] / numberOfBlocksPerDirection[2]); + for (uint_t i = 0; i < 3; ++i) + { + WALBERLA_CHECK_EQUAL(cellsPerBlockPerDirection[i] * numberOfBlocksPerDirection[i], domainSize[i], + "Unmatching domain decomposition in direction " << i << "!"); + } + + auto blocks = blockforest::createUniformBlockGrid(numberOfBlocksPerDirection[0], numberOfBlocksPerDirection[1], + numberOfBlocksPerDirection[2], cellsPerBlockPerDirection[0], + cellsPerBlockPerDirection[1], cellsPerBlockPerDirection[2], dx, 0, + false, false, false, false, false, // periodicity + false); + + WALBERLA_LOG_INFO_ON_ROOT("Domain decomposition:"); + WALBERLA_LOG_INFO_ON_ROOT(" - blocks per direction = " << numberOfBlocksPerDirection); + WALBERLA_LOG_INFO_ON_ROOT(" - cells per block = " << cellsPerBlockPerDirection); + + // write domain decomposition to file + if (vtkIOFreq > 0) { vtk::writeDomainDecomposition(blocks, "initial_domain_decomposition", baseFolder_vtk); } + + ///////////////// + // PE COUPLING // + ///////////////// + + // set up pe functionality + shared_ptr< pe::BodyStorage > globalBodyStorage = make_shared< pe::BodyStorage >(); + pe::SetBodyTypeIDs< BodyTypeTuple >::execute(); + + auto bodyStorageID = blocks->addBlockData(pe::createStorageDataHandling< BodyTypeTuple >(), "pe Body Storage"); + auto ccdID = blocks->addBlockData(pe::ccd::createHashGridsDataHandling(globalBodyStorage, bodyStorageID), "CCD"); + auto fcdID = blocks->addBlockData( + pe::fcd::createGenericFCDDataHandling< BodyTypeTuple, pe::fcd::AnalyticCollideFunctor >(), "FCD"); + + // set up collision response, here DEM solver + pe::cr::DEM cr(globalBodyStorage, blocks->getBlockStoragePointer(), bodyStorageID, ccdID, fcdID, nullptr); + + // set up synchronization procedure + const real_t overlap = real_t(1.5) * dx; + std::function< void(void) > syncCall = + std::bind(pe::syncShadowOwners< BodyTypeTuple >, std::ref(blocks->getBlockForest()), bodyStorageID, + static_cast< WcTimingTree* >(nullptr), overlap, false); + + // create pe bodies + + // bounding planes (global) + const auto planeMaterial = pe::createMaterial("planeMaterial", 1000.0, 0.8, 0.1, 0.05, 0.2, 80, 100, 10, 11); + pe::createPlane(*globalBodyStorage, 0, Vector3< real_t >(1, 0, 0), Vector3< real_t >(0, 0, 0), planeMaterial); + pe::createPlane(*globalBodyStorage, 0, Vector3< real_t >(-1, 0, 0), Vector3< real_t >(real_c(domainSize[0]), 0, 0), + planeMaterial); + pe::createPlane(*globalBodyStorage, 0, Vector3< real_t >(0, 1, 0), Vector3< real_t >(0, 0, 0), planeMaterial); + pe::createPlane(*globalBodyStorage, 0, Vector3< real_t >(0, -1, 0), Vector3< real_t >(0, real_c(domainSize[1]), 0), + planeMaterial); + pe::createPlane(*globalBodyStorage, 0, Vector3< real_t >(0, 0, 1), Vector3< real_t >(0, 0, 0), planeMaterial); + pe::createPlane(*globalBodyStorage, 0, Vector3< real_t >(0, 0, -1), Vector3< real_t >(0, 0, real_c(domainSize[2])), + planeMaterial); + + // add the MEM sphere + const auto peMaterial_MEM = createSphereMaterial("peMaterial_MEM", diameter_MEM, densityRatio_MEM); + Vector3< real_t > initialPosition(real_t(0.5) * real_c(domainSize[0]), real_t(0.5) * real_c(domainSize[1]), + startingGapSize_SI / dx_SI + real_t(0.5) * diameter_MEM); + pe::createSphere(*globalBodyStorage, blocks->getBlockStorage(), bodyStorageID, 0, initialPosition, + real_t(0.5) * diameter_MEM, peMaterial_MEM); + + // add DPM spheres + const auto peMaterial_DPM = createSphereMaterial("peMaterial_DPM", diameter_DPM, densityRatio_DPM); + createSphereLattice(*blocks, *globalBodyStorage, bodyStorageID, + AABB(real_t(0), real_t(0), real_t(1.0 * real_t(domainSize[2]) / 8.0), real_t(domainSize[0]), + real_t(domainSize[1]), real_t(2.0 * real_t(domainSize[2]) / 8.0)), + diameter_DPM, real_t(0.15), peMaterial_DPM, real_t(0)); + + syncCall(); + + /////////////////////// + // ADD DATA TO BLOCKS // + //////////////////////// + + // create force field + BlockDataID forceFieldID = field::addToStorage< Vec3Field_T >(blocks, "force field", Vector3< real_t >(real_t(0)), + field::zyxf, FieldGhostLayers); + + // create omega field + BlockDataID omegaFieldID = + field::addToStorage< ScalarField_T >(blocks, "omega field", real_t(0), field::zyxf, FieldGhostLayers); + + // create the lattice model + LatticeModel_T latticeModel = LatticeModel_T(omegaFieldID, ForceModel_T(forceFieldID)); + + // add PDF field + BlockDataID pdfFieldID = lbm::addPdfFieldToStorage< LatticeModel_T >( + blocks, "pdf field (zyxf)", latticeModel, Vector3< real_t >(real_t(0)), real_t(1), uint_t(1), field::zyxf); + // add flag field + BlockDataID flagFieldID = field::addFlagFieldToStorage< FlagField_T >(blocks, "flag field"); + + // add body field + BlockDataID bodyFieldID = field::addToStorage< BodyField_T >(blocks, "body field", nullptr, field::zyxf); + + // add boundary handling + BlockDataID boundaryHandlingID = blocks->addStructuredBlockData< BoundaryHandling_T >( + MyBoundaryHandling(flagFieldID, pdfFieldID, bodyFieldID), "boundary handling"); + + // field to store fluid velolcity + BlockDataID velocityFieldID = + field::addToStorage< Vec3Field_T >(blocks, "velocity field", initialFluidVelocity, field::zyxf, FieldGhostLayers); + + BlockDataID oldVelocityFieldID = field::addToStorage< Vec3Field_T >( + blocks, "old velocity field", initialFluidVelocity, field::zyxf, FieldGhostLayers); + BlockDataID swappedOldVelocityFieldID = field::addToStorage< Vec3Field_T >( + blocks, "swapped old velocity field", initialFluidVelocity, field::zyxf, FieldGhostLayers); + + // field to store curl of fluid velocity + BlockDataID velocityCurlFieldID = field::addToStorage< Vec3Field_T >( + blocks, "velocity curl field", Vector3< real_t >(real_c(0)), field::zyxf, FieldGhostLayers); + + // field to store velocity gradient + BlockDataID velocityGradientFieldID = field::addToStorage< TensorField_T >( + blocks, "velocity gradient field", Matrix3< real_t >(real_c(0)), field::zyxf, FieldGhostLayers); + + // field to store time derivative of fluid velocity + BlockDataID timeDerivativeVelocityFieldID = field::addToStorage< Vec3Field_T >( + blocks, "time derivative velocity field", Vector3< real_t >(real_c(0)), field::zyxf, FieldGhostLayers); + + // create solid volume fraction field + BlockDataID svfFieldID = + field::addToStorage< ScalarField_T >(blocks, "svf field", real_t(0), field::zyxf, FieldGhostLayers); + + // create pressure field + BlockDataID pressureFieldID = + field::addToStorage< ScalarField_T >(blocks, "pressure field", real_t(0), field::zyxf, FieldGhostLayers); + + // field to store pressure gradient + BlockDataID pressureGradientFieldID = field::addToStorage< Vec3Field_T >( + blocks, "pressure gradient field", Vector3< real_t >(real_c(0)), field::zyxf, FieldGhostLayers); + + BlockDataID dragForceFieldID = field::addToStorage< Vec3Field_T >( + blocks, "drag force field", Vector3< real_t >(real_t(0)), field::zyxf, FieldGhostLayers); + + BlockDataID amForceFieldID = field::addToStorage< Vec3Field_T >( + blocks, "am force field", Vector3< real_t >(real_t(0)), field::zyxf, FieldGhostLayers); + BlockDataID liftForceFieldID = field::addToStorage< Vec3Field_T >( + blocks, "lift force field", Vector3< real_t >(real_t(0)), field::zyxf, FieldGhostLayers); + + // map planes into the LBM simulation -> act as no-slip boundaries + pe_coupling::mapBodies< BoundaryHandling_T >(*blocks, boundaryHandlingID, bodyStorageID, *globalBodyStorage, + NoSlip_Flag, pe_coupling::selectGlobalBodies); + + // map pe bodies into the LBM simulation + pe_coupling::mapMovingBodies< BoundaryHandling_T >(*blocks, boundaryHandlingID, bodyStorageID, *globalBodyStorage, + bodyFieldID, MO_Flag, selectMEMBodies); + + // communication for synchronizing the interaction force field + pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication< Vec3Field_T > dragForceComm( + blocks, dragForceFieldID); + + pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication< Vec3Field_T > amForceComm( + blocks, amForceFieldID); + pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication< Vec3Field_T > liftForceComm( + blocks, liftForceFieldID); + + blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > pdfScheme(blocks); + pdfScheme.addPackInfo(make_shared< field::communication::PackInfo< PdfField_T > >(pdfFieldID)); + + shared_ptr< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > > velocityCommunicationScheme = + make_shared< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >(blocks); + velocityCommunicationScheme->addPackInfo( + make_shared< field::communication::PackInfo< Vec3Field_T > >(velocityFieldID)); + + shared_ptr< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > > oldVelocityCommunicationScheme = + make_shared< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >(blocks); + oldVelocityCommunicationScheme->addPackInfo( + make_shared< field::communication::PackInfo< Vec3Field_T > >(oldVelocityFieldID)); + + // setup of the communication for synchronizing the solid volume fraction field between neighboring blocks which + // takes into account the svf values inside the ghost layers + shared_ptr< pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication< ScalarField_T > > + svfCommunicationScheme = + make_shared< pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication< ScalarField_T > >( + blocks, svfFieldID); + + // setup of the communication for synchronizing the pressure field between neighboring blocks + shared_ptr< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > > pressureCommunicationScheme = + make_shared< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >(blocks); + pressureCommunicationScheme->addPackInfo( + make_shared< field::communication::PackInfo< ScalarField_T > >(pressureFieldID)); + + // setup of the communication for synchronizing the pressure gradient field between neighboring blocks + shared_ptr< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > > + pressureGradientCommunicationScheme = + make_shared< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >(blocks); + pressureGradientCommunicationScheme->addPackInfo( + make_shared< field::communication::PackInfo< Vec3Field_T > >(pressureGradientFieldID)); + + // setup of the communication for synchronizing the velocity curl field between neighboring blocks + shared_ptr< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > > velocityCurlCommunicationScheme = + make_shared< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >(blocks); + velocityCurlCommunicationScheme->addPackInfo( + make_shared< field::communication::PackInfo< Vec3Field_T > >(velocityCurlFieldID)); + + // communication for synchronizing the velocity gradient values + shared_ptr< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > > + velocityGradientCommunicationScheme = + make_shared< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >(blocks); + velocityGradientCommunicationScheme->addPackInfo( + make_shared< field::communication::PackInfo< TensorField_T > >(velocityGradientFieldID)); + + shared_ptr< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder > + dragForceFieldToForceFieldAdder = + make_shared< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + blocks, forceFieldID, dragForceFieldID, uint_t(1)); + shared_ptr< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder > + amForceFieldToForceFieldAdder = + make_shared< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + blocks, forceFieldID, amForceFieldID, uint_t(1)); + shared_ptr< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder > + liftForceFieldToForceFieldAdder = + make_shared< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + blocks, forceFieldID, liftForceFieldID, uint_t(1)); + + ///////////////////////////////// + // // + // CORRELATION FUNCTIONS // + // // + ///////////////////////////////// + + // drag correlation function + std::function< Vector3< real_t >(const Vector3< real_t >&, const Vector3< real_t >&, real_t, real_t, real_t, + real_t) > + dragCorrelationFunction; + if (dragCorr == DragCorrelation::ErgunWenYu) + { dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceErgunWenYu; } + else if (dragCorr == DragCorrelation::Tang) + { + dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceTang; + } + else if (dragCorr == DragCorrelation::Stokes) + { + dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceStokes; + } + else if (dragCorr == DragCorrelation::Felice) + { + dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceFelice; + } + else if (dragCorr == DragCorrelation::Tenneti) + { + dragCorrelationFunction = pe_coupling::discrete_particle_methods::dragForceTenneti; + } + else if (dragCorr == DragCorrelation::NoDrag) + { + dragCorrelationFunction = pe_coupling::discrete_particle_methods::noDragForce; + } + else + { + WALBERLA_ABORT("Drag correlation not yet implemented!"); + } + + // lift correlation function + std::function< Vector3< real_t >(const Vector3< real_t >&, const Vector3< real_t >&, const Vector3< real_t >&, + real_t, real_t, real_t) > + liftCorrelationFunction; + if (liftCorr == LiftCorrelation::NoLift) + { liftCorrelationFunction = pe_coupling::discrete_particle_methods::noLiftForce; } + else if (liftCorr == LiftCorrelation::Saffman) + { + liftCorrelationFunction = pe_coupling::discrete_particle_methods::liftForceSaffman; + } + else + { + WALBERLA_ABORT("Lift correlation not yet implemented!"); + } + + // added mass correlation function + std::function< Vector3< real_t >(const Vector3< real_t >&, const Vector3< real_t >&, real_t, real_t) > + addedMassCorrelationFunction; + if (addedMassCorr == AddedMassCorrelation::NoAM) + { addedMassCorrelationFunction = pe_coupling::discrete_particle_methods::noAddedMassForce; } + else if (addedMassCorr == AddedMassCorrelation::Finn) + { + addedMassCorrelationFunction = pe_coupling::discrete_particle_methods::addedMassForceFinn; + } + else + { + WALBERLA_ABORT("Added mass correlation not yet implemented!"); + } + + //////////////// + // EVALUATION // + //////////////// + + // evaluator for bodies' velocity time derivative + shared_ptr< pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator > + bodyVelocityTimeDerivativeEvaluator = + make_shared< pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator >( + blocks, bodyStorageID, dtBodyVelocityTimeDerivativeEvaluation); + (*bodyVelocityTimeDerivativeEvaluator)(); + + // function used to evaluate the interaction force between fluid and particles + std::function< void(void) > dragAndPressureForceEvaluationFunction; + if (interpol == Interpolation::INearestNeighbor) + { + if (dist == Distribution::DNearestNeighbor) + { + typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator< + FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >( + blocks, dragForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, svfFieldID, + pressureGradientFieldID, dragCorrelationFunction, viscosity, selectDPMBodies); + dragAndPressureForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + else if (dist == Distribution::DKernel) + { + typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator< + FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >( + blocks, dragForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, svfFieldID, + pressureGradientFieldID, dragCorrelationFunction, viscosity, selectDPMBodies); + dragAndPressureForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + } + else if (interpol == Interpolation::IKernel) + { + if (dist == Distribution::DNearestNeighbor) + { + typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator< + FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >( + blocks, dragForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, svfFieldID, + pressureGradientFieldID, dragCorrelationFunction, viscosity, selectDPMBodies); + dragAndPressureForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + else if (dist == Distribution::DKernel) + { + typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator< + FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >( + blocks, dragForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, svfFieldID, + pressureGradientFieldID, dragCorrelationFunction, viscosity, selectDPMBodies); + dragAndPressureForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + } + + // function to evaluate the lift force contribution + std::function< void(void) > liftForceEvaluationFunction; + if (interpol == Interpolation::INearestNeighbor) + { + if (dist == Distribution::DNearestNeighbor) + { + typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< + FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = + make_shared< IFE_T >(blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, + velocityCurlFieldID, liftCorrelationFunction, viscosity, selectDPMBodies); + liftForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + else if (dist == Distribution::DKernel) + { + typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< + FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = + make_shared< IFE_T >(blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, + velocityCurlFieldID, liftCorrelationFunction, viscosity, selectDPMBodies); + liftForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + } + else if (interpol == Interpolation::IKernel) + { + if (dist == Distribution::DNearestNeighbor) + { + typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< + FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = + make_shared< IFE_T >(blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, + velocityCurlFieldID, liftCorrelationFunction, viscosity, selectDPMBodies); + liftForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + else if (dist == Distribution::DKernel) + { + typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< + FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = + make_shared< IFE_T >(blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, + velocityCurlFieldID, liftCorrelationFunction, viscosity, selectDPMBodies); + liftForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + } + + // function to evaluate the added mass contribution + std::function< void(void) > addedMassEvaluationFunction; + if (interpol == Interpolation::INearestNeighbor) + { + if (dist == Distribution::DNearestNeighbor) + { + typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< + FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >( + blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, timeDerivativeVelocityFieldID, + addedMassCorrelationFunction, bodyVelocityTimeDerivativeEvaluator, selectDPMBodies); + addedMassEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + else if (dist == Distribution::DKernel) + { + typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< + FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >( + blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, timeDerivativeVelocityFieldID, + addedMassCorrelationFunction, bodyVelocityTimeDerivativeEvaluator, selectDPMBodies); + addedMassEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + } + else if (interpol == Interpolation::IKernel) + { + if (dist == Distribution::DNearestNeighbor) + { + typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< + FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >( + blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, timeDerivativeVelocityFieldID, + addedMassCorrelationFunction, bodyVelocityTimeDerivativeEvaluator, selectDPMBodies); + addedMassEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + else if (dist == Distribution::DKernel) + { + typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< + FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > + IFE_T; + shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >( + blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, timeDerivativeVelocityFieldID, + addedMassCorrelationFunction, bodyVelocityTimeDerivativeEvaluator, selectDPMBodies); + addedMassEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr); + } + } + + // set up effective viscosity calculation + std::function< real_t(real_t, real_t) > effectiveViscosityFunction; + if (effVisc == EffectiveViscosity::None) + { effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateUnchangedEffectiveViscosity; } + else if (effVisc == EffectiveViscosity::Rescaled) + { + effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateRescaledEffectiveViscosity; + } + else if (effVisc == EffectiveViscosity::Eilers) + { + effectiveViscosityFunction = pe_coupling::discrete_particle_methods::calculateEilersEffectiveViscosity; + } + else + { + WALBERLA_ABORT("Effective viscosity not yet implemented!"); + } + shared_ptr< pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator > effectiveViscosityEvaluator = + walberla::make_shared< pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator >( + omegaFieldID, svfFieldID, viscosity, effectiveViscosityFunction); + + /////////////// + // TIME LOOP // + /////////////// + + // create the timeloop + SweepTimeloop timeloop(blocks->getBlockStorage(), timesteps); + + shared_ptr< pe_coupling::BodiesForceTorqueContainer > bodiesFTContainer1 = + make_shared< pe_coupling::BodiesForceTorqueContainer >(blocks, bodyStorageID, selectMEMBodies); + std::function< void(void) > storeForceTorqueInCont1 = + std::bind(&pe_coupling::BodiesForceTorqueContainer::store, bodiesFTContainer1); + shared_ptr< pe_coupling::BodiesForceTorqueContainer > bodiesFTContainer2 = + make_shared< pe_coupling::BodiesForceTorqueContainer >(blocks, bodyStorageID, selectMEMBodies); + std::function< void(void) > setForceTorqueOnBodiesFromCont2 = + std::bind(&pe_coupling::BodiesForceTorqueContainer::setOnBodies, bodiesFTContainer2); + + shared_ptr< pe_coupling::BodiesForceTorqueContainer > bodiesFTContainer3 = + make_shared< pe_coupling::BodiesForceTorqueContainer >(blocks, bodyStorageID, selectMEMBodies); + std::function< void(void) > storeForceTorqueInCont3 = + std::bind(&pe_coupling::BodiesForceTorqueContainer::store, bodiesFTContainer3); + std::function< void(void) > setForceTorqueOnBodiesFromCont3 = + std::bind(&pe_coupling::BodiesForceTorqueContainer::setOnBodies, bodiesFTContainer3); + + shared_ptr< pe_coupling::ForceTorqueOnBodiesScaler > forceScaler = + make_shared< pe_coupling::ForceTorqueOnBodiesScaler >(blocks, bodyStorageID, real_t(1), selectMEMBodies); + + std::function< void(void) > setForceScalingFactorToHalf = + std::bind(&pe_coupling::ForceTorqueOnBodiesScaler::resetScalingFactor, forceScaler, real_t(0.5)); + + // initial svf eval + if (dist == Distribution::DNearestNeighbor) + { + pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator< FlagField_T, + field::NearestNeighborDistributor > + svfEvaluator(blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag, selectDPMBodies); + for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt) + svfEvaluator(&(*blockIt)); + } + else + { + pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator< FlagField_T, field::KernelDistributor > + svfEvaluator(blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag, selectDPMBodies); + for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt) + svfEvaluator(&(*blockIt)); + } + + (*svfCommunicationScheme)(); + + pe_coupling::discrete_particle_methods::ForceFieldResetter forceFieldResetter(forceFieldID); + for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt) + forceFieldResetter(&(*blockIt)); + + // Averaging the force/torque over two time steps is said to damp oscillations of the interaction force/torque. + // See Ladd - " Numerical simulations of particulate suspensions via a discretized Boltzmann equation. Part 1. + // Theoretical foundation", 1994, p. 302 + + timeloop.add() << Sweep(DummySweep(), "Dummy Sweep ") + // set force/torque from previous time step (in container2) + << AfterFunction(setForceTorqueOnBodiesFromCont2, "Force setting") + // average the force/torque by scaling it with factor 1/2 (except in first timestep, there it is 1, + // which it is initially) + << AfterFunction(SharedFunctor< pe_coupling::ForceTorqueOnBodiesScaler >(forceScaler), + "Force averaging") + << AfterFunction(setForceScalingFactorToHalf, "Force scaling adjustment") + // swap containers + << AfterFunction(pe_coupling::BodyContainerSwapper(bodiesFTContainer1, bodiesFTContainer2), + "Swap FT container") + << AfterFunction(storeForceTorqueInCont3, "Force Storing") + << AfterFunction(pe_coupling::ForceTorqueOnBodiesResetter(blocks, bodyStorageID, selectMEMBodies), + "Reset MEM Forces"); + + if (addedMassCorr != AddedMassCorrelation::NoAM) + { + timeloop.add() << Sweep(pe_coupling::discrete_particle_methods::FieldDataSwapper< Vec3Field_T >( + velocityFieldID, swappedOldVelocityFieldID), + "Velocity Field Swap"); + } + + if (addedMassCorr != AddedMassCorrelation::NoAM) + { + timeloop.add() + << Sweep( + pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator< LatticeModel_T, BoundaryHandling_T >( + oldVelocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID), + "Old Velocity Field Evaluation") + << AfterFunction(SharedFunctor< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >( + oldVelocityCommunicationScheme), + "Old Velocity Field Communication"); + timeloop.add() + << Sweep(pe_coupling::discrete_particle_methods::VelocityGradientFieldEvaluator< LatticeModel_T, + BoundaryHandling_T >( + velocityGradientFieldID, oldVelocityFieldID, boundaryHandlingID), + "Velocity Gradient Field Evaluation") + << AfterFunction(SharedFunctor< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >( + velocityGradientCommunicationScheme), + "Velocity Gradient Field Communication"); + timeloop.add() << Sweep( + pe_coupling::discrete_particle_methods::VelocityTotalTimeDerivativeFieldEvaluator( + timeDerivativeVelocityFieldID, oldVelocityFieldID, swappedOldVelocityFieldID, velocityGradientFieldID, dt), + "Velocity Time Derivative Field Evaluation"); + } + + // subcycling loop begin + for (uint_t subcycle = 1; subcycle <= interactionSubcycles; ++subcycle) + { + timeloop.add() << Sweep(DummySweep(), "Dummy Sweep ") + << AfterFunction(setForceTorqueOnBodiesFromCont3, "Force setting"); + + timeloop.add() << Sweep(pe_coupling::discrete_particle_methods::ForceFieldResetter(forceFieldID), + "Force Field Reset"); + timeloop.add() + << Sweep(DummySweep(), "Force Field Add") + << AfterFunction( + SharedFunctor< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + dragForceFieldToForceFieldAdder), + "Drag Force Field To Force Field Adder") + << AfterFunction( + SharedFunctor< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + amForceFieldToForceFieldAdder), + "AM Force Field To Force Field Adder") + << AfterFunction( + SharedFunctor< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + liftForceFieldToForceFieldAdder), + "Lift Force Field To Force Field Adder"); + + timeloop.add() + << Sweep( + pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator< LatticeModel_T, BoundaryHandling_T >( + velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID), + "Velocity Field Evaluation") + << AfterFunction(SharedFunctor< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >( + velocityCommunicationScheme), + "Velocity Field Communication"); + + if (liftCorr != LiftCorrelation::NoLift) + { + timeloop.add() + << Sweep(pe_coupling::discrete_particle_methods::VelocityCurlFieldEvaluator< LatticeModel_T, + BoundaryHandling_T >( + velocityCurlFieldID, velocityFieldID, boundaryHandlingID), + "Velocity Curl Field Evaluation") + << AfterFunction(SharedFunctor< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >( + velocityCurlCommunicationScheme), + "Velocity Curl Field Communication"); + } + + timeloop.add() + << Sweep( + pe_coupling::discrete_particle_methods::GNSPressureFieldEvaluator< LatticeModel_T, BoundaryHandling_T >( + pressureFieldID, pdfFieldID, svfFieldID, boundaryHandlingID), + "Pressure Field Evaluation") + << AfterFunction(SharedFunctor< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >( + pressureCommunicationScheme), + "Pressure Field Communication"); + + timeloop.add() + << Sweep(pe_coupling::discrete_particle_methods::PressureGradientFieldEvaluator< LatticeModel_T, + BoundaryHandling_T >( + pressureGradientFieldID, pressureFieldID, boundaryHandlingID), + "Pressure Gradient Field Evaluation") + << AfterFunction(SharedFunctor< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >( + pressureGradientCommunicationScheme), + "Pressure Gradient Field Communication"); + + if (liftCorr != LiftCorrelation::NoLift) + { + // evaluate Flift + timeloop.add() << Sweep(pe_coupling::discrete_particle_methods::ForceFieldResetter(liftForceFieldID), + "Lift Force Field Reset") + << AfterFunction(liftForceEvaluationFunction, "Lift Force Evaluation") + << AfterFunction(liftForceComm, "Lift Force Field Communication"); + } + + if (addedMassCorr != AddedMassCorrelation::NoAM) + { + // evaluate Fam + timeloop.add() + << Sweep(pe_coupling::discrete_particle_methods::ForceFieldResetter(amForceFieldID), "AM Force Field Reset") + << AfterFunction(addedMassEvaluationFunction, "Added Mass Force Evaluation") + << AfterFunction(amForceComm, "Force Field Communication") + << AfterFunction( + SharedFunctor< pe_coupling::discrete_particle_methods::BodyVelocityTimeDerivativeEvaluator >( + bodyVelocityTimeDerivativeEvaluator), + "Body Velocity Time Derivative Evaluation"); + } + + if (liftCorr != LiftCorrelation::NoLift || addedMassCorr != AddedMassCorrelation::NoAM) + { + timeloop.add() + << Sweep(pe_coupling::discrete_particle_methods::ForceFieldResetter(forceFieldID), "Force Field Reset") + << AfterFunction( + SharedFunctor< + pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + liftForceFieldToForceFieldAdder), + "Lift Force Field To Force Field Adder") + << AfterFunction( + SharedFunctor< + pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + amForceFieldToForceFieldAdder), + "AM Force Field To Force Field Adder") + << AfterFunction( + SharedFunctor< + pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + dragForceFieldToForceFieldAdder), + "Drag Force Field To Force Field Adder"); + + timeloop.add() + << Sweep(pe_coupling::discrete_particle_methods::GNSVelocityFieldEvaluator< LatticeModel_T, + BoundaryHandling_T >( + velocityFieldID, pdfFieldID, svfFieldID, boundaryHandlingID), + "Velocity Field Evaluation") + << AfterFunction(SharedFunctor< blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > >( + velocityCommunicationScheme), + "Velocity Field Communication"); + } + + // evaluate Fdrag + timeloop.add() << Sweep(pe_coupling::discrete_particle_methods::ForceFieldResetter(dragForceFieldID), + "Drag Force Field Reset") + << AfterFunction(dragAndPressureForceEvaluationFunction, + "Fluid-Particle Interaction Force Evaluation") + << AfterFunction(dragForceComm, "Drag Force Field Communication"); + + Vector3< real_t > gravitationalForce_MEM(0, 0, -gravitationalAcceleration * densityRatio_MEM * sphereVolume_MEM); + Vector3< real_t > gravitationalForce_DPM(0, 0, -gravitationalAcceleration * densityRatio_DPM * sphereVolume_DPM); + + Vector3< real_t > buoyancyForce_MEM(0, 0, gravitationalAcceleration * real_t(1) * sphereVolume_MEM); + Vector3< real_t > buoyancyForce_DPM(0, 0, gravitationalAcceleration * real_t(1) * sphereVolume_DPM); + + // ext forces on bodies + timeloop.add() + << Sweep(DummySweep(), "Dummy Sweep ") + << AfterFunction( + pe_coupling::ForceOnBodiesAdder(blocks, bodyStorageID, gravitationalForce_DPM, selectDPMBodies), + "DPM Gravitational Force Add") + + << AfterFunction( + pe_coupling::ForceOnBodiesAdder(blocks, bodyStorageID, gravitationalForce_MEM, selectMEMBodies), + "MEM Gravitational Force Add") + << AfterFunction(pe_coupling::ForceOnBodiesAdder(blocks, bodyStorageID, buoyancyForce_DPM, selectDPMBodies), + "DPM Buoyancy Force (due to gravity) Add") + + << AfterFunction(pe_coupling::ForceOnBodiesAdder(blocks, bodyStorageID, buoyancyForce_MEM, selectMEMBodies), + "MEM Buoyancy Force (due to gravity) Add") + + << AfterFunction(pe_coupling::TimeStep(blocks, bodyStorageID, cr, syncCall, dtInteractionSubCycle, + numPeSubCycles, emptyFunction), + "Pe Time Step"); + + // sweep for updating the pe body mapping into the LBM simulation + timeloop.add() << Sweep(pe_coupling::BodyMapping< LatticeModel_T, BoundaryHandling_T >( + blocks, pdfFieldID, boundaryHandlingID, bodyStorageID, globalBodyStorage, bodyFieldID, + MO_Flag, FormerMO_Flag, selectMEMBodies), + "Body Mapping"); + + // sweep for restoring PDFs in cells previously occupied by pe bodies + pe_coupling::SphereNormalExtrapolationDirectionFinder extrapolationFinder(blocks, bodyFieldID); + typedef pe_coupling::ExtrapolationReconstructor< LatticeModel_T, BoundaryHandling_T, + pe_coupling::SphereNormalExtrapolationDirectionFinder > + Reconstructor_T; + Reconstructor_T reconstructor(blocks, boundaryHandlingID, bodyFieldID, extrapolationFinder, true); + + timeloop.add() << Sweep(pe_coupling::PDFReconstruction< LatticeModel_T, BoundaryHandling_T, Reconstructor_T >( + blocks, pdfFieldID, boundaryHandlingID, bodyStorageID, globalBodyStorage, bodyFieldID, + reconstructor, FormerMO_Flag, Fluid_Flag, selectMEMBodies), + "PDF Restore"); + + if (dist == Distribution::DNearestNeighbor) + { + timeloop.add() + << Sweep(pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator< + FlagField_T, field::NearestNeighborDistributor >(blocks, svfFieldID, bodyStorageID, flagFieldID, + Fluid_Flag, selectDPMBodies), + "Solid Volume Fraction Field Evaluation") + << AfterFunction( + SharedFunctor< + pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication< ScalarField_T > >( + svfCommunicationScheme), + "Solid Volume Fraction Field Communication"); + } + else + { + timeloop.add() + << Sweep( + pe_coupling::discrete_particle_methods::SolidVolumeFractionFieldEvaluator< FlagField_T, + field::KernelDistributor >( + blocks, svfFieldID, bodyStorageID, flagFieldID, Fluid_Flag, selectDPMBodies), + "Solid Volume Fraction Field Evaluation") + << AfterFunction( + SharedFunctor< + pe_coupling::discrete_particle_methods::CombinedReductionFieldCommunication< ScalarField_T > >( + svfCommunicationScheme), + "Solid Volume Fraction Field Communication"); + } + } + + timeloop.add() << Sweep(pe_coupling::discrete_particle_methods::ForceFieldResetter(forceFieldID), + "Force Field Reset"); + timeloop.add() + << Sweep(DummySweep(), "Force Field Add") + << AfterFunction( + SharedFunctor< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + dragForceFieldToForceFieldAdder), + "Drag Force Field To Force Field Adder") + << AfterFunction( + SharedFunctor< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + amForceFieldToForceFieldAdder), + "AM Force Field To Force Field Adder") + << AfterFunction( + SharedFunctor< pe_coupling::discrete_particle_methods::AveragedInteractionForceFieldToForceFieldAdder >( + liftForceFieldToForceFieldAdder), + "Lift Force Field To Force Field Adder"); + + timeloop.add() << Sweep(makeSharedSweep< pe_coupling::discrete_particle_methods::EffectiveViscosityFieldEvaluator >( + effectiveViscosityEvaluator), + "Effective Viscosity Evaluation"); + + bodiesFTContainer2->store(); + + // execute GNS-LBM sweep, boundary handling, PDF communication + auto sweep = pe_coupling::discrete_particle_methods::makeGNSSweep< LatticeModel_T, FlagField_T >( + pdfFieldID, svfFieldID, flagFieldID, Fluid_Flag); + + timeloop.add() << Sweep(lbm::makeCollideSweep(sweep), "GNS-LBM sweep (collide)"); + + timeloop.add() << BeforeFunction(pdfScheme, "LBM Communication") + << Sweep(BoundaryHandling_T::getBlockSweep(boundaryHandlingID), "Boundary Handling"); + + timeloop.add() << Sweep(lbm::makeStreamSweep(sweep), "GNS-LBM sweep (stream)"); + + // store force/torque from hydrodynamic interactions in container1 + timeloop.addFuncAfterTimeStep(storeForceTorqueInCont1, "Force Storing"); + + if (vtkIOFreq != uint_t(0)) + { + shared_ptr< vtk::VTKOutput > pdfFieldVTKWriter = + createFluidFieldVTKWriter(blocks, pdfFieldID, flagFieldID, vtkIOFreq, baseFolder_vtk); + timeloop.addFuncAfterTimeStep(vtk::writeFiles(pdfFieldVTKWriter), "VTK (fluid field data)"); + + auto bodyVtkOutput = make_shared< pe::SphereVtkOutput >(bodyStorageID, blocks->getBlockStorage()); + auto bodyVTK = vtk::createVTKOutput_PointData(bodyVtkOutput, "bodies", vtkIOFreq, baseFolder_vtk); + timeloop.addFuncAfterTimeStep(vtk::writeFiles(bodyVTK), "VTK (sphere data)"); + + timeloop.addFuncAfterTimeStep(field::createVTKOutput< Vec3Field_T, float >(forceFieldID, *blocks, "force_field", + vtkIOFreq, uint_t(0), false, + baseFolder_vtk), + "VTK (force field)"); + timeloop.addFuncAfterTimeStep(field::createVTKOutput< ScalarField_T, float >( + svfFieldID, *blocks, "svf_field", vtkIOFreq, uint_t(0), false, baseFolder_vtk), + "VTK (svf field)"); + + timeloop.addFuncAfterTimeStep(field::createVTKOutput< ScalarField_T, float >(omegaFieldID, *blocks, "omega_field", + vtkIOFreq, uint_t(0), false, + baseFolder_vtk), + "VTK (omega field)"); + } + + timeloop.addFuncAfterTimeStep(RemainingTimeLogger(timeloop.getNrOfTimeSteps()), "Remaining Time Logger"); + + //////////////////////// + // EXECUTE SIMULATION // + //////////////////////// + + WcTimingPool timeloopTiming; + + // time loop + for (uint_t i = 0; i < timesteps; ++i) + { + // perform a single simulation step + timeloop.singleStep(timeloopTiming); + } + + timeloopTiming.logResultOnRoot(); + + return EXIT_SUCCESS; +} + +} // namespace combined_resolved_unresolved + +int main(int argc, char** argv) { combined_resolved_unresolved::main(argc, argv); } diff --git a/src/pe_coupling/utility/BodiesForceTorqueContainer.cpp b/src/pe_coupling/utility/BodiesForceTorqueContainer.cpp index bed1c267c35770e232638d6b423e283cf3bc9172..6b2639ece2b4a0504fe456b9fc5f551dfef28fdc 100644 --- a/src/pe_coupling/utility/BodiesForceTorqueContainer.cpp +++ b/src/pe_coupling/utility/BodiesForceTorqueContainer.cpp @@ -42,6 +42,7 @@ void BodiesForceTorqueContainer::store() for( auto bodyIt = pe::BodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt ) { + if( !bodySelectorFct_(bodyIt.getBodyID()) ) continue; auto & f = (*bodyForceTorqueStorage)[ bodyIt->getSystemID() ]; const auto & force = bodyIt->getForce(); @@ -60,6 +61,7 @@ void BodiesForceTorqueContainer::setOnBodies() for( auto bodyIt = pe::BodyIterator::begin(*blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt ) { + if( !bodySelectorFct_(bodyIt.getBodyID()) ) continue; const auto f = bodyForceTorqueStorage->find( bodyIt->getSystemID() ); if( f != bodyForceTorqueStorage->end() ) diff --git a/src/pe_coupling/utility/BodiesForceTorqueContainer.h b/src/pe_coupling/utility/BodiesForceTorqueContainer.h index fe367b5e095075f7882c19f9cf213d793be40351..b18d4ec0dd655a6c85648e4676a677850a5249ee 100644 --- a/src/pe_coupling/utility/BodiesForceTorqueContainer.h +++ b/src/pe_coupling/utility/BodiesForceTorqueContainer.h @@ -22,6 +22,7 @@ #pragma once #include "blockforest/StructuredBlockForest.h" +#include "BodySelectorFunctions.h" #include <map> #include <array> @@ -35,8 +36,9 @@ public: typedef std::map< walberla::id_t, std::array<real_t,6> > ForceTorqueStorage_T; - BodiesForceTorqueContainer( const shared_ptr<StructuredBlockForest> & blockForest, const BlockDataID & bodyStorageID ) - : blockForest_( blockForest ), bodyStorageID_( bodyStorageID ) + BodiesForceTorqueContainer( const shared_ptr<StructuredBlockForest> & blockForest, const BlockDataID & bodyStorageID, const std::function<bool( + pe::BodyID)> &bodySelectorFct = selectRegularBodies) + : blockForest_( blockForest ), bodyStorageID_( bodyStorageID ), bodySelectorFct_( bodySelectorFct ) { // has to be added to the forest (not the storage) to register correctly bodyForceTorqueStorageID_ = blockForest->addBlockData(make_shared<blockforest::AlwaysCreateBlockDataHandling<ForceTorqueStorage_T> >(), "BodiesForceTorqueContainer"); @@ -60,6 +62,7 @@ private: shared_ptr<StructuredBlockStorage> blockForest_; const BlockDataID bodyStorageID_; BlockDataID bodyForceTorqueStorageID_; + std::function<bool(pe::BodyID)> bodySelectorFct_; }; diff --git a/src/pe_coupling/utility/ForceOnBodiesAdder.cpp b/src/pe_coupling/utility/ForceOnBodiesAdder.cpp index 4c7c17f8376763af77004bedb8cd83186515460e..e5e4ecb6e67edc34f8e601ac1c0ed8d4b8932d15 100644 --- a/src/pe_coupling/utility/ForceOnBodiesAdder.cpp +++ b/src/pe_coupling/utility/ForceOnBodiesAdder.cpp @@ -35,6 +35,7 @@ void ForceOnBodiesAdder::operator()() { for( auto bodyIt = pe::LocalBodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::LocalBodyIterator::end(); ++bodyIt ) { + if( !bodySelectorFct_(bodyIt.getBodyID()) ) continue; bodyIt->addForce ( force_ ); } } diff --git a/src/pe_coupling/utility/ForceOnBodiesAdder.h b/src/pe_coupling/utility/ForceOnBodiesAdder.h index b0b7056d52aa54498197317eb48f4fdf294c4b0b..e5b82f299afbfa63e3749a1769acdda2ecb2e72a 100644 --- a/src/pe_coupling/utility/ForceOnBodiesAdder.h +++ b/src/pe_coupling/utility/ForceOnBodiesAdder.h @@ -23,6 +23,7 @@ #include "core/math/Vector3.h" #include "domain_decomposition/StructuredBlockStorage.h" +#include "BodySelectorFunctions.h" namespace walberla { namespace pe_coupling { @@ -32,8 +33,9 @@ class ForceOnBodiesAdder public: ForceOnBodiesAdder( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID, - const Vector3<real_t> & force ) - : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), force_( force ) + const Vector3<real_t> & force, const std::function<bool( + pe::BodyID)> &bodySelectorFct = selectRegularBodies ) + : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), force_( force ), bodySelectorFct_( bodySelectorFct ) { } // set a constant force on all (only local, to avoid force duplication) bodies @@ -46,6 +48,7 @@ private: shared_ptr<StructuredBlockStorage> blockStorage_; const BlockDataID bodyStorageID_; Vector3<real_t> force_; + const std::function<bool(pe::BodyID)> bodySelectorFct_; }; } // namespace pe_coupling diff --git a/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.cpp b/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.cpp index e3301650a01ce5b2a5a6cfc7f517576891ddfaf4..b3ae6d309805164c67c0a36587e9dbaee53b3fc0 100644 --- a/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.cpp +++ b/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.cpp @@ -32,6 +32,7 @@ void ForceTorqueOnBodiesResetter::operator()() { for( auto bodyIt = pe::BodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt ) { + if( !bodySelectorFct_(bodyIt.getBodyID()) ) continue; bodyIt->resetForceAndTorque(); } } diff --git a/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.h b/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.h index a86368f83fdaf26b4f2c586ac90f178042d567f6..71861e2bbbee3c61148d1068b1b904f08f6a1299 100644 --- a/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.h +++ b/src/pe_coupling/utility/ForceTorqueOnBodiesResetter.h @@ -22,6 +22,7 @@ #pragma once #include "domain_decomposition/StructuredBlockStorage.h" +#include "BodySelectorFunctions.h" namespace walberla { namespace pe_coupling { @@ -30,8 +31,9 @@ class ForceTorqueOnBodiesResetter { public: - ForceTorqueOnBodiesResetter( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID ) - : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ) + ForceTorqueOnBodiesResetter( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID, const std::function<bool( + pe::BodyID)> &bodySelectorFct = selectRegularBodies ) + : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), bodySelectorFct_( bodySelectorFct ) { } // resets forces and torques on all (local and remote) bodies @@ -41,6 +43,7 @@ private: shared_ptr<StructuredBlockStorage> blockStorage_; const BlockDataID bodyStorageID_; + const std::function<bool(pe::BodyID)> bodySelectorFct_; }; } // namespace pe_coupling diff --git a/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.cpp b/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.cpp index 677c5788d7dea105e6952d911d9d8942deb0cbce..773ae67fb08e1e8bb9161c8c40144e5789681e6c 100644 --- a/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.cpp +++ b/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.cpp @@ -35,6 +35,7 @@ void ForceTorqueOnBodiesScaler::operator()() { for( auto bodyIt = pe::BodyIterator::begin( *blockIt, bodyStorageID_); bodyIt != pe::BodyIterator::end(); ++bodyIt ) { + if( !bodySelectorFct_(bodyIt.getBodyID()) ) continue; force = scalingFactor_ * bodyIt->getForce(); torque = scalingFactor_ * bodyIt->getTorque(); diff --git a/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.h b/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.h index f903e8ddc1606112f60dbf19a872379077a4c325..56ae4fd9c536f2522903a92e48e16e0b623068ac 100644 --- a/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.h +++ b/src/pe_coupling/utility/ForceTorqueOnBodiesScaler.h @@ -22,6 +22,7 @@ #pragma once #include "domain_decomposition/StructuredBlockStorage.h" +#include "BodySelectorFunctions.h" namespace walberla { namespace pe_coupling { @@ -33,8 +34,9 @@ class ForceTorqueOnBodiesScaler public: ForceTorqueOnBodiesScaler( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyStorageID, - const real_t & scalingFactor ) - : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), scalingFactor_( scalingFactor ) + const real_t & scalingFactor, const std::function<bool( + pe::BodyID)> &bodySelectorFct = selectRegularBodies ) + : blockStorage_( blockStorage ), bodyStorageID_( bodyStorageID ), scalingFactor_( scalingFactor ), bodySelectorFct_( bodySelectorFct ) { } // resets forces and torques on all (local and remote) bodies @@ -47,6 +49,7 @@ private: shared_ptr<StructuredBlockStorage> blockStorage_; const BlockDataID bodyStorageID_; real_t scalingFactor_; + const std::function<bool(pe::BodyID)> bodySelectorFct_; }; } // namespace pe_coupling