diff --git a/python/mesa_pd.py b/python/mesa_pd.py index 80b5bff0e6b6355b795ed1bb024f55cb75c80d08..599c0e075f0469d5783ed9d499e16cd026477152 100755 --- a/python/mesa_pd.py +++ b/python/mesa_pd.py @@ -98,6 +98,7 @@ if __name__ == '__main__': mpd.add(kernel.NonLinearSpringDashpot()) mpd.add(kernel.SingleCast(ps)) mpd.add(kernel.SpringDashpot()) + mpd.add(kernel.SpringDashpotSpring()) mpd.add(kernel.TemperatureIntegration()) mpd.add(kernel.VelocityVerlet()) mpd.add(kernel.VelocityVerletWithShape()) diff --git a/python/mesa_pd/kernel/SpringDashpotSpring.py b/python/mesa_pd/kernel/SpringDashpotSpring.py new file mode 100644 index 0000000000000000000000000000000000000000..9399e156332b7541dfc6c445a860920c3ba42238 --- /dev/null +++ b/python/mesa_pd/kernel/SpringDashpotSpring.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- + +from mesa_pd.utility import generate_file + + +class SpringDashpotSpring: + def generate(self, module): + ctx = {'module': module} + ctx["parameters"] = ["stiffnessN", "dampingN", "stiffnessT", "coefficientOfFriction"] + + generate_file(module['module_path'], 'kernel/SpringDashpotSpring.templ.h', ctx) diff --git a/python/mesa_pd/kernel/__init__.py b/python/mesa_pd/kernel/__init__.py index eef9907a547ca08e93d35a26eed6d89ef005580b..8e79c416daafd913a50262539f7f0fbe36a335c7 100644 --- a/python/mesa_pd/kernel/__init__.py +++ b/python/mesa_pd/kernel/__init__.py @@ -15,6 +15,7 @@ from .LinearSpringDashpot import LinearSpringDashpot from .NonLinearSpringDashpot import NonLinearSpringDashpot from .SingleCast import SingleCast from .SpringDashpot import SpringDashpot +from .SpringDashpotSpring import SpringDashpotSpring from .TemperatureIntegration import TemperatureIntegration from .VelocityVerlet import VelocityVerlet from .VelocityVerletWithShape import VelocityVerletWithShape @@ -35,6 +36,7 @@ __all__ = ['DoubleCast', 'NonLinearSpringDashpot', 'SingleCast', 'SpringDashpot', + 'SpringDashpotSpring', 'TemperatureIntegration', 'VelocityVerlet', 'VelocityVerletWithShape'] diff --git a/python/mesa_pd/templates/kernel/SpringDashpotSpring.templ.h b/python/mesa_pd/templates/kernel/SpringDashpotSpring.templ.h new file mode 100644 index 0000000000000000000000000000000000000000..78d3d4863175141fc76c657828c9c448804671bd --- /dev/null +++ b/python/mesa_pd/templates/kernel/SpringDashpotSpring.templ.h @@ -0,0 +1,204 @@ +//====================================================================================================================== +// +// 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 SpringDashpotSpring.h +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/common/ParticleFunctions.h> +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/IAccessor.h> + +#include <core/math/Constants.h> +#include <vector> + +namespace walberla { +namespace mesa_pd { +namespace kernel { + +/** + * Basic DEM kernel + * + * DEM kernel with spring-dashpot interaction in normal direction and spring in tangential direction. + * + * \ingroup mesa_pd_kernel + */ +class SpringDashpotSpring +{ +public: + SpringDashpotSpring(const uint_t numParticleTypes); + SpringDashpotSpring(const SpringDashpotSpring& other) = default; + SpringDashpotSpring(SpringDashpotSpring&& other) = default; + SpringDashpotSpring& operator=(const SpringDashpotSpring& other) = default; + SpringDashpotSpring& operator=(SpringDashpotSpring&& other) = default; + + template <typename Accessor> + void operator()(const size_t p_idx1, + const size_t p_idx2, + Accessor& ac, + const Vec3& contactPoint, + const Vec3& contactNormal, + const real_t& penetrationDepth, + const real_t dt) const; + + {% for param in parameters %} + /// assumes this parameter is symmetric + void set{{param | capFirst}}(const size_t type1, const size_t type2, const real_t& val); + {%- endfor %} + + {% for param in parameters %} + real_t get{{param | capFirst}}(const size_t type1, const size_t type2) const; + {%- endfor %} + + inline + real_t calcCoefficientOfRestitution(const size_t type1, + const size_t type2, + const real_t meff) + { + auto a = real_t(0.5) * getDampingN(type1, type2) / meff; + return std::exp(-a * math::pi / std::sqrt(getStiffnessN(type1, type2) / meff - a*a)); + } + + inline + real_t calcCollisionTime(const size_t type1, + const size_t type2, + const real_t meff) + { + auto a = real_t(0.5) * getDampingN(type1, type2) / meff; + return math::pi / std::sqrt( getStiffnessN(type1, type2)/meff - a*a); + } + + inline + void setParametersFromCOR(const size_t type1, + const size_t type2, + const real_t cor, + const real_t collisionTime, + const real_t meff) + { + const real_t lnDryResCoeff = std::log(cor); + setStiffnessN(type1, type2, math::pi * math::pi * meff / ( collisionTime * collisionTime * ( real_t(1) - lnDryResCoeff * lnDryResCoeff / ( math::pi * math::pi + lnDryResCoeff* lnDryResCoeff )) )); + setDampingN( type1, type2, - real_t(2) * std::sqrt( meff * getStiffnessN(type1, type2) ) * ( lnDryResCoeff / std::sqrt( math::pi * math::pi + ( lnDryResCoeff * lnDryResCoeff ) ) )); + } +private: + uint_t numParticleTypes_; + {% for param in parameters %} + std::vector<real_t> {{param}}_ {}; + {%- endfor %} +}; + +SpringDashpotSpring::SpringDashpotSpring(const uint_t numParticleTypes) +{ + numParticleTypes_ = numParticleTypes; + {% for param in parameters %} + {{param}}_.resize(numParticleTypes * numParticleTypes, real_t(0)); + {%- endfor %} +} + +{% for param in parameters %} +inline void SpringDashpotSpring::set{{param | capFirst}}(const size_t type1, const size_t type2, const real_t& val) +{ + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + {{param}}_[numParticleTypes_*type1 + type2] = val; + {{param}}_[numParticleTypes_*type2 + type1] = val; +} +{%- endfor %} + +{% for param in parameters %} +inline real_t SpringDashpotSpring::get{{param | capFirst}}(const size_t type1, const size_t type2) const +{ + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + WALBERLA_ASSERT_FLOAT_EQUAL( {{param}}_[numParticleTypes_*type1 + type2], + {{param}}_[numParticleTypes_*type2 + type1], + "parameter matrix for {{param}} not symmetric!"); + return {{param}}_[numParticleTypes_*type1 + type2]; +} +{%- endfor %} + +template <typename Accessor> +inline void SpringDashpotSpring::operator()(const size_t p_idx1, + const size_t p_idx2, + Accessor& ac, + const Vec3& contactPoint, + const Vec3& contactNormal, + const real_t& penetrationDepth, + const real_t dt) const +{ + static_assert(std::is_base_of<data::IAccessor, Accessor>::value, "please provide a valid accessor"); + + if (p_idx1 != p_idx2) + { + // skip if no penetration is present + real_t delta = -penetrationDepth; + if (delta < real_t(0)) return; + + // calculate relative velocities + const Vec3 relVel ( -(getVelocityAtWFPoint(p_idx1, ac, contactPoint) - getVelocityAtWFPoint(p_idx2, ac, contactPoint)) ); + const real_t relVelN( math::dot(relVel, contactNormal) ); + const Vec3 relVelT( relVel - ( relVelN * contactNormal ) ); + const Vec3 contactTangent = relVelT.getNormalizedOrZero(); + + // Calculating the normal force based on a linear spring-dashpot force model + real_t fNabs = getStiffnessN(ac.getType(p_idx1), ac.getType(p_idx2)) * delta + getDampingN(ac.getType(p_idx1), ac.getType(p_idx2)) * relVelN; + const Vec3 fN = fNabs * contactNormal; + + // get tangential displacement from contact history + auto tangentialDisplacement = Vec3(real_t(0)); + auto contactHistory = ac.getOldContactHistoryRef(p_idx1).find(ac.getUid(p_idx2)); + if(contactHistory != ac.getOldContactHistoryRef(p_idx1).end()) + { + // get infos from the contact history + tangentialDisplacement = dot(contactHistory->second.getTangentialSpringDisplacement(), contactTangent) * contactTangent; + } + + // accumulate tangential displacement + tangentialDisplacement += relVelT * dt; + + // Calculating the tangential force + const auto maxTangentialForce = fNabs * getCoefficientOfFriction(ac.getType(p_idx1), ac.getType(p_idx2)); + Vec3 fT = getStiffnessT(ac.getType(p_idx1), ac.getType(p_idx2)) * tangentialDisplacement; + if (length(fT) > maxTangentialForce) + fT = maxTangentialForce * fT.getNormalizedOrZero(); + + // store new tangential displacements + auto& ch1 = ac.getNewContactHistoryRef(p_idx1)[ac.getUid(p_idx2)]; + ch1.setTangentialSpringDisplacement(tangentialDisplacement); + + auto& ch2 = ac.getNewContactHistoryRef(p_idx2)[ac.getUid(p_idx1)]; + ch2.setTangentialSpringDisplacement(tangentialDisplacement); + + // Add normal force at contact point + addForceAtWFPosAtomic( p_idx1, ac, fN, contactPoint ); + addForceAtWFPosAtomic( p_idx2, ac, -fN, contactPoint ); + + // Add tangential force at contact point + addForceAtWFPosAtomic( p_idx1, ac, fT, contactPoint ); + addForceAtWFPosAtomic( p_idx2, ac, -fT, contactPoint ); + } +} + +} //namespace kernel +} //namespace mesa_pd +} //namespace walberla \ No newline at end of file diff --git a/src/mesa_pd/kernel/SpringDashpotSpring.h b/src/mesa_pd/kernel/SpringDashpotSpring.h new file mode 100644 index 0000000000000000000000000000000000000000..f2f87671a4e9bf08c87f17100f3b47a482f385e3 --- /dev/null +++ b/src/mesa_pd/kernel/SpringDashpotSpring.h @@ -0,0 +1,261 @@ +//====================================================================================================================== +// +// 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 SpringDashpotSpring.h +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +//====================================================================================================================== +// +// THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!! +// +//====================================================================================================================== + +#pragma once + +#include <mesa_pd/common/ParticleFunctions.h> +#include <mesa_pd/data/DataTypes.h> +#include <mesa_pd/data/IAccessor.h> + +#include <core/math/Constants.h> +#include <vector> + +namespace walberla { +namespace mesa_pd { +namespace kernel { + +/** + * Basic DEM kernel + * + * DEM kernel with spring-dashpot interaction in normal direction and spring in tangential direction. + * + * \ingroup mesa_pd_kernel + */ +class SpringDashpotSpring +{ +public: + SpringDashpotSpring(const uint_t numParticleTypes); + SpringDashpotSpring(const SpringDashpotSpring& other) = default; + SpringDashpotSpring(SpringDashpotSpring&& other) = default; + SpringDashpotSpring& operator=(const SpringDashpotSpring& other) = default; + SpringDashpotSpring& operator=(SpringDashpotSpring&& other) = default; + + template <typename Accessor> + void operator()(const size_t p_idx1, + const size_t p_idx2, + Accessor& ac, + const Vec3& contactPoint, + const Vec3& contactNormal, + const real_t& penetrationDepth, + const real_t dt) const; + + + /// assumes this parameter is symmetric + void setStiffnessN(const size_t type1, const size_t type2, const real_t& val); + /// assumes this parameter is symmetric + void setDampingN(const size_t type1, const size_t type2, const real_t& val); + /// assumes this parameter is symmetric + void setStiffnessT(const size_t type1, const size_t type2, const real_t& val); + /// assumes this parameter is symmetric + void setCoefficientOfFriction(const size_t type1, const size_t type2, const real_t& val); + + + real_t getStiffnessN(const size_t type1, const size_t type2) const; + real_t getDampingN(const size_t type1, const size_t type2) const; + real_t getStiffnessT(const size_t type1, const size_t type2) const; + real_t getCoefficientOfFriction(const size_t type1, const size_t type2) const; + + inline + real_t calcCoefficientOfRestitution(const size_t type1, + const size_t type2, + const real_t meff) + { + auto a = real_t(0.5) * getDampingN(type1, type2) / meff; + return std::exp(-a * math::pi / std::sqrt(getStiffnessN(type1, type2) / meff - a*a)); + } + + inline + real_t calcCollisionTime(const size_t type1, + const size_t type2, + const real_t meff) + { + auto a = real_t(0.5) * getDampingN(type1, type2) / meff; + return math::pi / std::sqrt( getStiffnessN(type1, type2)/meff - a*a); + } + + inline + void setParametersFromCOR(const size_t type1, + const size_t type2, + const real_t cor, + const real_t collisionTime, + const real_t meff) + { + const real_t lnDryResCoeff = std::log(cor); + setStiffnessN(type1, type2, math::pi * math::pi * meff / ( collisionTime * collisionTime * ( real_t(1) - lnDryResCoeff * lnDryResCoeff / ( math::pi * math::pi + lnDryResCoeff* lnDryResCoeff )) )); + setDampingN( type1, type2, - real_t(2) * std::sqrt( meff * getStiffnessN(type1, type2) ) * ( lnDryResCoeff / std::sqrt( math::pi * math::pi + ( lnDryResCoeff * lnDryResCoeff ) ) )); + } +private: + uint_t numParticleTypes_; + + std::vector<real_t> stiffnessN_ {}; + std::vector<real_t> dampingN_ {}; + std::vector<real_t> stiffnessT_ {}; + std::vector<real_t> coefficientOfFriction_ {}; +}; + +SpringDashpotSpring::SpringDashpotSpring(const uint_t numParticleTypes) +{ + numParticleTypes_ = numParticleTypes; + + stiffnessN_.resize(numParticleTypes * numParticleTypes, real_t(0)); + dampingN_.resize(numParticleTypes * numParticleTypes, real_t(0)); + stiffnessT_.resize(numParticleTypes * numParticleTypes, real_t(0)); + coefficientOfFriction_.resize(numParticleTypes * numParticleTypes, real_t(0)); +} + + +inline void SpringDashpotSpring::setStiffnessN(const size_t type1, const size_t type2, const real_t& val) +{ + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + stiffnessN_[numParticleTypes_*type1 + type2] = val; + stiffnessN_[numParticleTypes_*type2 + type1] = val; +} +inline void SpringDashpotSpring::setDampingN(const size_t type1, const size_t type2, const real_t& val) +{ + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + dampingN_[numParticleTypes_*type1 + type2] = val; + dampingN_[numParticleTypes_*type2 + type1] = val; +} +inline void SpringDashpotSpring::setStiffnessT(const size_t type1, const size_t type2, const real_t& val) +{ + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + stiffnessT_[numParticleTypes_*type1 + type2] = val; + stiffnessT_[numParticleTypes_*type2 + type1] = val; +} +inline void SpringDashpotSpring::setCoefficientOfFriction(const size_t type1, const size_t type2, const real_t& val) +{ + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + coefficientOfFriction_[numParticleTypes_*type1 + type2] = val; + coefficientOfFriction_[numParticleTypes_*type2 + type1] = val; +} + + +inline real_t SpringDashpotSpring::getStiffnessN(const size_t type1, const size_t type2) const +{ + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + WALBERLA_ASSERT_FLOAT_EQUAL( stiffnessN_[numParticleTypes_*type1 + type2], + stiffnessN_[numParticleTypes_*type2 + type1], + "parameter matrix for stiffnessN not symmetric!"); + return stiffnessN_[numParticleTypes_*type1 + type2]; +} +inline real_t SpringDashpotSpring::getDampingN(const size_t type1, const size_t type2) const +{ + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + WALBERLA_ASSERT_FLOAT_EQUAL( dampingN_[numParticleTypes_*type1 + type2], + dampingN_[numParticleTypes_*type2 + type1], + "parameter matrix for dampingN not symmetric!"); + return dampingN_[numParticleTypes_*type1 + type2]; +} +inline real_t SpringDashpotSpring::getStiffnessT(const size_t type1, const size_t type2) const +{ + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + WALBERLA_ASSERT_FLOAT_EQUAL( stiffnessT_[numParticleTypes_*type1 + type2], + stiffnessT_[numParticleTypes_*type2 + type1], + "parameter matrix for stiffnessT not symmetric!"); + return stiffnessT_[numParticleTypes_*type1 + type2]; +} +inline real_t SpringDashpotSpring::getCoefficientOfFriction(const size_t type1, const size_t type2) const +{ + WALBERLA_ASSERT_LESS( type1, numParticleTypes_ ); + WALBERLA_ASSERT_LESS( type2, numParticleTypes_ ); + WALBERLA_ASSERT_FLOAT_EQUAL( coefficientOfFriction_[numParticleTypes_*type1 + type2], + coefficientOfFriction_[numParticleTypes_*type2 + type1], + "parameter matrix for coefficientOfFriction not symmetric!"); + return coefficientOfFriction_[numParticleTypes_*type1 + type2]; +} + +template <typename Accessor> +inline void SpringDashpotSpring::operator()(const size_t p_idx1, + const size_t p_idx2, + Accessor& ac, + const Vec3& contactPoint, + const Vec3& contactNormal, + const real_t& penetrationDepth, + const real_t dt) const +{ + static_assert(std::is_base_of<data::IAccessor, Accessor>::value, "please provide a valid accessor"); + + if (p_idx1 != p_idx2) + { + // skip if no penetration is present + real_t delta = -penetrationDepth; + if (delta < real_t(0)) return; + + // calculate relative velocities + const Vec3 relVel ( -(getVelocityAtWFPoint(p_idx1, ac, contactPoint) - getVelocityAtWFPoint(p_idx2, ac, contactPoint)) ); + const real_t relVelN( math::dot(relVel, contactNormal) ); + const Vec3 relVelT( relVel - ( relVelN * contactNormal ) ); + const Vec3 contactTangent = relVelT.getNormalizedOrZero(); + + // Calculating the normal force based on a linear spring-dashpot force model + real_t fNabs = getStiffnessN(ac.getType(p_idx1), ac.getType(p_idx2)) * delta + getDampingN(ac.getType(p_idx1), ac.getType(p_idx2)) * relVelN; + const Vec3 fN = fNabs * contactNormal; + + // get tangential displacement from contact history + auto tangentialDisplacement = Vec3(real_t(0)); + auto contactHistory = ac.getOldContactHistoryRef(p_idx1).find(ac.getUid(p_idx2)); + if(contactHistory != ac.getOldContactHistoryRef(p_idx1).end()) + { + // get infos from the contact history + tangentialDisplacement = dot(contactHistory->second.getTangentialSpringDisplacement(), contactTangent) * contactTangent; + } + + // accumulate tangential displacement + tangentialDisplacement += relVelT * dt; + + // Calculating the tangential force + const auto maxTangentialForce = fNabs * getCoefficientOfFriction(ac.getType(p_idx1), ac.getType(p_idx2)); + Vec3 fT = getStiffnessT(ac.getType(p_idx1), ac.getType(p_idx2)) * tangentialDisplacement; + if (length(fT) > maxTangentialForce) + fT = maxTangentialForce * fT.getNormalizedOrZero(); + + // store new tangential displacements + auto& ch1 = ac.getNewContactHistoryRef(p_idx1)[ac.getUid(p_idx2)]; + ch1.setTangentialSpringDisplacement(tangentialDisplacement); + + auto& ch2 = ac.getNewContactHistoryRef(p_idx2)[ac.getUid(p_idx1)]; + ch2.setTangentialSpringDisplacement(tangentialDisplacement); + + // Add normal force at contact point + addForceAtWFPosAtomic( p_idx1, ac, fN, contactPoint ); + addForceAtWFPosAtomic( p_idx2, ac, -fN, contactPoint ); + + // Add tangential force at contact point + addForceAtWFPosAtomic( p_idx1, ac, fT, contactPoint ); + addForceAtWFPosAtomic( p_idx2, ac, -fT, contactPoint ); + } +} + +} //namespace kernel +} //namespace mesa_pd +} //namespace walberla \ No newline at end of file diff --git a/tests/mesa_pd/CMakeLists.txt b/tests/mesa_pd/CMakeLists.txt index 81a8a74cad16373f4b5f39856e7c25a4d13a0f2f..ffc72839000545a515160c4a50591265f49aa23b 100644 --- a/tests/mesa_pd/CMakeLists.txt +++ b/tests/mesa_pd/CMakeLists.txt @@ -134,6 +134,9 @@ waLBerla_execute_test( NAME MESA_PD_Kernel_LinkedCellsVsBruteForce PROCESSES 2 waLBerla_compile_test( NAME MESA_PD_Kernel_SingleCast FILES kernel/SingleCast.cpp DEPENDS core ) waLBerla_execute_test( NAME MESA_PD_Kernel_SingleCast ) +waLBerla_compile_test( NAME MESA_PD_Kernel_SpherePile FILES kernel/SpherePile.cpp DEPENDS core ) +waLBerla_execute_test( NAME MESA_PD_Kernel_SpherePile ) + waLBerla_compile_test( NAME MESA_PD_Kernel_SpringDashpot FILES kernel/SpringDashpot.cpp DEPENDS core ) waLBerla_execute_test( NAME MESA_PD_Kernel_SpringDashpot ) diff --git a/tests/mesa_pd/kernel/SpherePile.cpp b/tests/mesa_pd/kernel/SpherePile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e326a1110ad8f0ef4d0e32d2a275313f11c02879 --- /dev/null +++ b/tests/mesa_pd/kernel/SpherePile.cpp @@ -0,0 +1,211 @@ +//====================================================================================================================== +// +// 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 SpherePile.cpp +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +// +//====================================================================================================================== + +#include "mesa_pd/collision_detection/AnalyticContactDetection.h" +#include "mesa_pd/common/ParticleFunctions.h" + +#include "mesa_pd/data/ParticleAccessorWithShape.h" +#include "mesa_pd/data/ParticleStorage.h" +#include "mesa_pd/data/ShapeStorage.h" + +#include "mesa_pd/kernel/DoubleCast.h" +#include "mesa_pd/kernel/ExplicitEulerWithShape.h" +#include "mesa_pd/kernel/SpringDashpot.h" +#include "mesa_pd/kernel/SpringDashpotSpring.h" +#include "mesa_pd/mpi/ReduceContactHistory.h" +#include "mesa_pd/vtk/ParticleVtkOutput.h" + +#include "core/Environment.h" +#include "core/logging/Logging.h" +#include "vtk/VTKOutput.h" + +#include <iostream> + +namespace walberla { +namespace mesa_pd { + +auto createSphere(data::ParticleStorage &ps, const Vec3 &pos) +{ + auto p = ps.create(); + p->setPosition(pos); + p->setType(0); + return p; +} + +/* + * Simulates oblique sphere-wall collision and checks rebound angle, i.e. the tangential part of the collision model. + * + */ +int main(int argc, char **argv) +{ + walberla::mpi::Environment env(argc, argv); + WALBERLA_UNUSED(env); + walberla::mpi::MPIManager::instance()->useWorldComm(); + + auto dt = real_t(0.001); + auto cor = real_t(0.2); + auto ct = dt * real_t(20); + auto radius = real_t(1); + auto density = real_t(2700); + auto simSteps = 100000; + + //init data structures + auto ps = walberla::make_shared<data::ParticleStorage>(2); + auto ss = walberla::make_shared<data::ShapeStorage>(); + auto ac = walberla::make_shared<data::ParticleAccessorWithShape>(ps, ss); + + auto sphereShape = ss->create<data::Sphere>(radius); + ss->shapes[sphereShape]->updateMassAndInertia(density); + + // create sphere + createSphere(*ps, Vec3(0, 0, 0)); + createSphere(*ps, Vec3(2, 0, 0)); + auto sp = createSphere(*ps, Vec3(1, 0, std::sqrt(real_t(4) - real_t(1)))); + + // create plane + data::Particle &&p0 = *ps->create(true); + p0.setPosition(Vec3(0, 0, -1)); + p0.setShapeID(ss->create<data::HalfSpace>(Vector3<real_t>(0, 0, 1))); + p0.setType(0); + data::particle_flags::set(p0.getFlagsRef(), data::particle_flags::INFINITE); + data::particle_flags::set(p0.getFlagsRef(), data::particle_flags::FIXED); + + WALBERLA_LOG_INFO_ON_ROOT("*** VTK ***"); + auto vtkOutput = make_shared<mesa_pd::vtk::ParticleVtkOutput>(ps) ; + auto vtkWriter = walberla::vtk::createVTKOutput_PointData(vtkOutput, + "Bodies", + 1, + "vtk", + "simulation_step", + false, + false); + + // explicit euler + kernel::ExplicitEulerWithShape explEuler(dt); + collision_detection::AnalyticContactDetection acd; + kernel::DoubleCast double_cast; + kernel::SpringDashpot sd(1); + kernel::SpringDashpotSpring sds(1); + mpi::ReduceContactHistory rch; + + WALBERLA_LOG_DEVEL_VAR(sp->getPosition()); + + for (auto i = 0; i < simSteps; ++i) + { + ps->forEachParticle(false, + kernel::SelectLocal(), + *ac, + [&](const size_t idx1) + { + if (ac->getShape(idx1)->getShapeType() == data::Sphere::SHAPE_TYPE) + { + ac->setForce(idx1, Vec3(0,0,-1) * real_t(9.81) / ac->getInvMass(idx1)); + } + }); + ps->forEachParticlePairHalf(false, + kernel::SelectAll(), + *ac, + [&](const size_t idx1, const size_t idx2) + { + if ((ac->getShape(idx1)->getShapeType() != data::Sphere::SHAPE_TYPE) && + (ac->getShape(idx2)->getShapeType() != data::Sphere::SHAPE_TYPE)) + { + //skip plane - plane collision + } else + { + if (double_cast(idx1, idx2, *ac, acd, *ac)) + { + //WALBERLA_LOG_DEVEL_VAR(acd.getPenetrationDepth()); + auto meff = real_t(1) / (ac->getInvMass(idx1) + ac->getInvMass(idx2)); + sds.setParametersFromCOR(0, 0, cor, ct, meff); + sds.setCoefficientOfFriction(0,0, real_t(0.4)); + sds.setStiffnessT(0,0, sds.getStiffnessN(0,0)); + sds(acd.getIdx1(), acd.getIdx2(), *ac, acd.getContactPoint(), + acd.getContactNormal(), acd.getPenetrationDepth(), dt); + } + } + }); + + rch(*ps); + ps->forEachParticle(false, + kernel::SelectLocal(), + *ac, + explEuler, + *ac); + } + WALBERLA_LOG_DEVEL_VAR(sp->getPosition()); + WALBERLA_CHECK_GREATER(sp->getPosition()[2], real_t(1)); + + for (auto i = 0; i < simSteps; ++i) + { + ps->forEachParticle(false, + kernel::SelectLocal(), + *ac, + [&](const size_t idx1) + { + if (ac->getShape(idx1)->getShapeType() == data::Sphere::SHAPE_TYPE) + { + ac->setForce(idx1, Vec3(0,0,-1) * real_t(9.81) / ac->getInvMass(idx1)); + } + }); + ps->forEachParticlePairHalf(false, + kernel::SelectAll(), + *ac, + [&](const size_t idx1, const size_t idx2) + { + if ((ac->getShape(idx1)->getShapeType() != data::Sphere::SHAPE_TYPE) && + (ac->getShape(idx2)->getShapeType() != data::Sphere::SHAPE_TYPE)) + { + //skip plane - plane collision + } else + { + if (double_cast(idx1, idx2, *ac, acd, *ac)) + { + //WALBERLA_LOG_DEVEL_VAR(acd.getPenetrationDepth()); + auto meff = real_t(1) / (ac->getInvMass(idx1) + ac->getInvMass(idx2)); + sd.setParametersFromCOR(0, 0, cor, ct, meff); + sd.setFriction(0,0, real_t(0.4)); + sd.setDampingT(0,0, sd.getDampingN(0,0)); + sd(acd.getIdx1(), acd.getIdx2(), *ac, acd.getContactPoint(), + acd.getContactNormal(), acd.getPenetrationDepth()); + } + } + }); + + rch(*ps); + ps->forEachParticle(false, + kernel::SelectLocal(), + *ac, + explEuler, + *ac); + } + WALBERLA_LOG_DEVEL_VAR(sp->getPosition()); + WALBERLA_CHECK_LESS(sp->getPosition()[2], real_t(1)); + + return EXIT_SUCCESS; +} + +} //namespace mesa_pd +} //namespace walberla + +int main(int argc, char **argv) +{ + return walberla::mesa_pd::main(argc, argv); +}