diff --git a/src/pe/Materials.cpp b/src/pe/Materials.cpp index 1bdc0326bb166b0590b6b33c39efc85a18ca2a1a..e7619d68e9fca921dd780d2059b79c5d96cbd39b 100644 --- a/src/pe/Materials.cpp +++ b/src/pe/Materials.cpp @@ -160,7 +160,8 @@ bool Material::activateMaterials() */ MaterialID createMaterial( const std::string& name, real_t density, real_t cor, real_t csf, real_t cdf, real_t poisson, real_t young, - real_t stiffness, real_t dampingN, real_t dampingT, real_t surfEnergy ) + real_t stiffness, real_t dampingN, real_t dampingT, + real_t surfEnergy, real_t yieldStrength ) { using M = Material; @@ -207,8 +208,12 @@ MaterialID createMaterial( const std::string& name, real_t density, real_t cor, if( surfEnergy < real_c(0) ) throw std::invalid_argument( "Invalid surface energy" ); + if( yieldStrength < real_c(0) ) + throw std::invalid_argument( "Invalid yield strength" ); + // Registering the new material - M::materials_.push_back( Material( name, density, cor, csf, cdf, poisson, young, stiffness, dampingN, dampingT, surfEnergy ) ); + M::materials_.push_back( Material( name, density, cor, csf, cdf, poisson, young, stiffness, dampingN, dampingT, + surfEnergy, yieldStrength ) ); const MaterialID mat( M::materials_.size()-1 ); // Updating the restitution table, the static friction table and the dynamic friction table @@ -255,7 +260,7 @@ MaterialID createMaterial( const std::string& name, real_t density, real_t cor, // Note that the material has to be created on all processes in an MPI parallel simulation. */ MaterialID createMaterial( real_t density, real_t cor, real_t csf, real_t cdf, real_t poisson, real_t young, - real_t stiffness, real_t dampingN, real_t dampingT, real_t surfEnergy ) + real_t stiffness, real_t dampingN, real_t dampingT, real_t surfEnergy, real_t yieldStrength ) { std::ostringstream sstr; @@ -266,7 +271,8 @@ MaterialID createMaterial( real_t density, real_t cor, real_t csf, real_t cdf, r sstr << "Material" << ++Material::anonymousMaterials_; } while( Material::find( sstr.str() ) != invalid_material ); - return createMaterial( sstr.str(), density, cor, csf, cdf, poisson, young, stiffness, dampingN, dampingT, surfEnergy ); + return createMaterial( sstr.str(), density, cor, csf, cdf, poisson, young, stiffness, dampingN, dampingT, + surfEnergy, yieldStrength ); } //************************************************************************************************* @@ -327,9 +333,10 @@ std::string Material::toString( const MaterialID& v ) << " stiffness = " << Material::getStiffness(v) << "\n" << " dampingN = " << Material::getDampingN(v) << "\n" << " dampingT = " << Material::getDampingT(v) << "\n" - << " surface energy = " << Material::getSurfaceEnergy(v); + << " surface energy = " << Material::getSurfaceEnergy(v) << "\n" + << " yield strength = " << Material::getYieldStrength(v); return ss.str(); } } // namespace pe -} // namespace walberla +} // namespace walberla diff --git a/src/pe/Materials.h b/src/pe/Materials.h index 59a46e8f5f56353b08e1c93d2ca4ce8af4984de1..06e36abcfa8a886ae51db1d07414c7ea46274aa8 100644 --- a/src/pe/Materials.h +++ b/src/pe/Materials.h @@ -106,7 +106,8 @@ public: //@{ explicit inline Material( const std::string& name, real_t density, real_t cor, real_t csf, real_t cdf, real_t poisson, real_t young, real_t stiffness, - real_t dampingN, real_t dampingT, real_t surfEnergy = real_t(0) ); + real_t dampingN, real_t dampingT, + real_t surfEnergy = real_t(0), real_t yieldStrength = real_t(1E8) ); // No explicitly declared copy constructor. //@} //********************************************************************************************** @@ -133,6 +134,7 @@ public: inline real_t getDampingN() const; inline real_t getDampingT() const; inline real_t getSurfaceEnergy() const; + inline real_t getYieldStrength() const; static MaterialID find( const std::string& name ); static std::vector<MaterialID> findPrefix( const std::string& prefix ); @@ -154,6 +156,7 @@ public: static inline real_t getDampingT( MaterialID material ); static inline real_t getDampingT( MaterialID material1, MaterialID material2 ); static inline real_t getSurfaceEnergy( MaterialID material ); + static inline real_t getYieldStrength( MaterialID material ); static std::string toString( const MaterialID& v ); //@} //********************************************************************************************** @@ -236,7 +239,8 @@ private: /*!< Friction counteracts the tangential relative velocity and thus can be modelled as a viscous damper with a limited damping force. The viscous damping coefficient corresponds to this parameter.*/ - real_t surf_; //!< The surface energy at the contact region in mJ/m². + real_t surf_; //!< The surface energy at the contact region in \f$ mJ/m^2 \f$. + real_t yieldStrength_; //!< The elastic limit or yield strength of the material in \f$ N/m^2 \f$. static Materials materials_; //!< Vector for the registered materials. static MatN corTable_; //!< Table for the coefficients of restitution. @@ -251,10 +255,12 @@ private: /*! \cond internal */ friend MaterialID createMaterial( const std::string& name, real_t density, real_t cor, real_t csf, real_t cdf, real_t poisson, real_t young, - real_t stiffness, real_t dampingN, real_t dampingT, real_t surfEnergy = real_t(1) ); + real_t stiffness, real_t dampingN, real_t dampingT, + real_t surfEnergy = real_t(1), real_t yieldStrength = real_t(1E8) ); friend MaterialID createMaterial( real_t density, real_t cor, real_t csf, real_t cdf, real_t poisson, real_t young, - real_t stiffness, real_t dampingN, real_t dampingT, real_t surfEnergy = real_t(1) ); + real_t stiffness, real_t dampingN, real_t dampingT, + real_t surfEnergy = real_t(1), real_t yieldStrength = real_t(1E8) ); /*! \endcond */ //********************************************************************************************** }; @@ -274,10 +280,12 @@ private: * \param stiffness The stiffness in normal direction of the material's contact region. * \param dampingN The damping coefficient in normal direction of the material's contact region. * \param dampingT The damping coefficient in tangential direction of the material's contact region. + * \param surfEnergy The surface energy at the contact region in \f$ mJ/m^2 \f$. + * \param yieldStrength The yield strength of the material in \f$ N/m^2 \f$. */ inline Material::Material( const std::string& name, real_t density, real_t cor, real_t csf, real_t cdf, real_t poisson, real_t young, - real_t stiffness, real_t dampingN, real_t dampingT, real_t surfEnergy ) + real_t stiffness, real_t dampingN, real_t dampingT, real_t surfEnergy, real_t yieldStrength ) : name_ ( name ) // The name of the material , density_ ( density ) // The density of the material , restitution_( cor ) // The coefficient of restitution of the material @@ -289,6 +297,7 @@ inline Material::Material( const std::string& name, real_t density, real_t cor, , dampingN_ ( dampingN ) // The damping coefficient in normal direction of the material's contact region. , dampingT_ ( dampingT ) // The damping coefficient in tangential direction of the material's contact region. , surf_ ( surfEnergy ) // The surface energy at the contact region. + , yieldStrength_( yieldStrength ) // The yield strength of the material. {} //************************************************************************************************* @@ -425,6 +434,18 @@ inline real_t Material::getSurfaceEnergy() const //************************************************************************************************* +//************************************************************************************************* +/*!\brief Returns the yield strength of the material in \f$ N/m^2 \f$. + * + * \return The yield strength of the material. + */ +inline real_t Material::getYieldStrength() const +{ + return yieldStrength_; +} +//************************************************************************************************* + + //************************************************************************************************* /*!\brief Returns the name of the given material. * @@ -726,6 +747,20 @@ inline real_t Material::getSurfaceEnergy( MaterialID material ) //************************************************************************************************* +//************************************************************************************************* +/*!\brief Returns the yield strength of the material in \f$ N/m^2 \f$. + * + * \param material The material to be queried. + * \return The yield strength of the given material. + */ +inline real_t Material::getYieldStrength( MaterialID material ) +{ + WALBERLA_ASSERT( material < materials_.size(), "Invalid material ID" ); + return materials_[material].getYieldStrength(); +} +//************************************************************************************************* + + //************************************************************************************************* /*!\brief Setting the coefficient of restitution between material \a material1 and \a material2. * @@ -1114,9 +1149,11 @@ const MaterialID invalid_material = static_cast<MaterialID>( -1 ); /*!\name Material functions */ //@{ WALBERLA_PUBLIC MaterialID createMaterial( const std::string& name, real_t density, real_t cor, - real_t csf, real_t cdf, real_t poisson, real_t young, real_t stiffness, real_t dampingN, real_t dampingT, real_t surfEnergy ); + real_t csf, real_t cdf, real_t poisson, real_t young, real_t stiffness, real_t dampingN, real_t dampingT, + real_t surfEnergy, real_t yieldStrength ); WALBERLA_PUBLIC MaterialID createMaterial( real_t density, real_t cor, real_t csf, real_t cdf, - real_t poisson, real_t young, real_t stiffness, real_t dampingN, real_t dampingT, real_t surfEnergy ); + real_t poisson, real_t young, real_t stiffness, real_t dampingN, real_t dampingT, + real_t surfEnergy, real_t yieldStrength ); //@} //************************************************************************************************* diff --git a/src/pe/contact/ContactFunctions.h b/src/pe/contact/ContactFunctions.h index 1e328b628504afb0c0bf3b46c0bd6ac9a29b7773..f753b1f2e67eb2237f2ae406bb7a35068bb37441 100644 --- a/src/pe/contact/ContactFunctions.h +++ b/src/pe/contact/ContactFunctions.h @@ -37,6 +37,7 @@ inline real_t getDampingT(ConstContactID c); // inline real_t getCorParam(ConstContactID c); inline real_t getFriction(ConstContactID c); inline real_t getSurfaceEnergy(ConstContactID c); +inline real_t getYieldStrength(ConstContactID c); } // namespace pe } // namespace walberla diff --git a/src/pe/contact/ContactFunctions.impl.h b/src/pe/contact/ContactFunctions.impl.h index 673946214006c41c70c7c776d03bd55cb4ce7ecd..4641e63681096ecd9dc73b32b0d0a0c07dbc5675 100644 --- a/src/pe/contact/ContactFunctions.impl.h +++ b/src/pe/contact/ContactFunctions.impl.h @@ -166,5 +166,10 @@ inline real_t getSurfaceEnergy( ConstContactID c ) return math::max( Material::getSurfaceEnergy( c->getBody1()->getMaterial() ), Material::getSurfaceEnergy( c->getBody2()->getMaterial() ) ); } +inline real_t getYieldStrength( ConstContactID c ) +{ + return math::min( Material::getYieldStrength( c->getBody1()->getMaterial() ), Material::getYieldStrength( c->getBody2()->getMaterial() ) ); +} + } // namespace pe } // namespace walberla diff --git a/src/pe/cr/HCSITS.h b/src/pe/cr/HCSITS.h index 2099644a1a42764ef398a0a066ba18bda0628c71..e1113588e405452ee95bf26ecd85c2cdd360429c 100644 --- a/src/pe/cr/HCSITS.h +++ b/src/pe/cr/HCSITS.h @@ -73,7 +73,7 @@ private: // bodies struct BodyCache { - void resize(const size_t n); + void resize( size_t n ); std::vector<Vec3> v_, w_, dv_, dw_; }; @@ -82,19 +82,20 @@ private: // contacts struct ContactCache { - void resize(const size_t n); + void resize( size_t n ); std::vector<Vec3> r1_, r2_; // vector pointing from body1/body2 to the contact position - std::vector<BodyID> body1_, body2_; + std::vector<BodyID> body1_, body2_; // the two bodies involved into the contact std::vector<Vec3> n_, t_, o_; // contact normal and the two other directions perpendicular to the normal std::vector<real_t> dist_; // overlap length, a contact is present if dist_ < 0 std::vector<real_t> mu_; // contact friction std::vector<real_t> surf_; // surface energy - std::vector<Mat3> diag_nto_; - std::vector<Mat3> diag_nto_inv_; - std::vector<Mat2> diag_to_inv_; - std::vector<real_t> diag_n_inv_; - std::vector<Vec3> p_; + std::vector<real_t> k_; // yield strength + std::vector<Mat3> diag_nto_; // transforms impulse (n/t/o) to velocity in the contact frame + std::vector<Mat3> diag_nto_inv_; // transforms velocity to impulse (n/t/o) in the contact frame + std::vector<Mat2> diag_to_inv_; // transforms velocity to tangential impulses (t/o) in the world frame + std::vector<real_t> diag_n_inv_; // transforms velocity to normal impulse (n) in the world frame + std::vector<Vec3> p_; // the contact impulse }; std::map<IBlockID::IDType, ContactCache> blockToContactCache_; @@ -118,7 +119,7 @@ public: domain_decomposition::BlockDataID storageID, domain_decomposition::BlockDataID ccdID, domain_decomposition::BlockDataID fcdID, - WcTimingTree* tt = NULL ); + WcTimingTree* tt = nullptr ); //@} //********************************************************************************************** //**Destructor********************************************************************************** @@ -153,7 +154,7 @@ public: inline void setRelaxationModel( RelaxationModel relaxationModel ); inline void setErrorReductionParameter( real_t erp ); inline void setAbortThreshold( real_t threshold ); - inline void setSpeedLimiter( bool active, const real_t speedLimitFactor = real_t(0.0) ); + inline void setSpeedLimiter( bool active, real_t speedLimitFactor = real_t(0.0) ); //@} //********************************************************************************************** @@ -168,9 +169,9 @@ public: /// forwards to timestep /// Convenience operator to make class a functor. - void operator()(const real_t dt) { timestep(dt); } + void operator()( real_t dt ) { timestep(dt); } /// Advances the simulation dt seconds. - void timestep( const real_t dt ); + void timestep( real_t dt ); private: @@ -217,7 +218,7 @@ private: //**Utility functions*************************************************************************** /*!\name Utility functions */ //@{ - bool checkUpdateFlags(); + bool checkUpdateFlags(); //@} //********************************************************************************************** diff --git a/src/pe/cr/HCSITS.impl.h b/src/pe/cr/HCSITS.impl.h index f5eb4d6c49191dabc478a661235367bb8647b80c..9388cd22976cf23defece2e4eb3e6502842231d3 100644 --- a/src/pe/cr/HCSITS.impl.h +++ b/src/pe/cr/HCSITS.impl.h @@ -30,6 +30,7 @@ #include "pe/ccd/ICCD.h" #include "pe/fcd/IFCD.h" #include "pe/contact/ContactFunctions.h" +#include "pe/cr/YieldSurface.h" #include "core/math/Constants.h" #include "core/math/Limits.h" @@ -43,7 +44,7 @@ namespace walberla { namespace pe { namespace cr { -inline void HardContactSemiImplicitTimesteppingSolvers::BodyCache::resize(const size_t n) +inline void HardContactSemiImplicitTimesteppingSolvers::BodyCache::resize( const size_t n ) { v_.resize(n); w_.resize(n); @@ -51,7 +52,7 @@ inline void HardContactSemiImplicitTimesteppingSolvers::BodyCache::resize(const dw_.resize(n); } -inline void HardContactSemiImplicitTimesteppingSolvers::ContactCache::resize(const size_t n) +inline void HardContactSemiImplicitTimesteppingSolvers::ContactCache::resize( const size_t n ) { r1_.resize(n); r2_.resize(n); @@ -63,6 +64,7 @@ inline void HardContactSemiImplicitTimesteppingSolvers::ContactCache::resize(con dist_.resize(n); mu_.resize(n); surf_.resize(n); + k_.resize(n); diag_nto_.resize(n); diag_nto_inv_.resize(n); diag_to_inv_.resize(n); @@ -260,6 +262,7 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d contactCache.mu_[j] = getFriction(c); contactCache.surf_[j] = getSurfaceEnergy(c); + contactCache.k_[i] = getYieldStrength(c); Mat3 diag = -( math::skewSymCrossProduct(contactCache.r1_[j], math::skewSymCrossProduct(b1->getInvInertia(), contactCache.r1_[j])) + math::skewSymCrossProduct(contactCache.r2_[j], math::skewSymCrossProduct(b2->getInvInertia(), contactCache.r2_[j]))); @@ -540,26 +543,25 @@ inline real_t HardContactSemiImplicitTimesteppingSolvers::relaxInelasticFriction bodyCache.dv_[contactCache.body2_[i]->index_] += contactCache.body2_[i]->getInvMass() * contactCache.p_[i]; bodyCache.dw_[contactCache.body2_[i]->index_] += contactCache.body2_[i]->getInvInertia() * ( contactCache.r2_[i] % contactCache.p_[i] ); - // Calculate the relative contact VELOCITY in the global world frame (if no contact reaction is present at contact i) - Vec3 gdot ( ( bodyCache.v_[contactCache.body1_[i]->index_] + bodyCache.dv_[contactCache.body1_[i]->index_] ) - - ( bodyCache.v_[contactCache.body2_[i]->index_] + bodyCache.dv_[contactCache.body2_[i]->index_] ) + - ( bodyCache.w_[contactCache.body1_[i]->index_] + bodyCache.dw_[contactCache.body1_[i]->index_] ) % contactCache.r1_[i] - - ( bodyCache.w_[contactCache.body2_[i]->index_] + bodyCache.dw_[contactCache.body2_[i]->index_] ) % contactCache.r2_[i] /* + diag_[i] * p */ ); + // Calculate the relative contact VELOCITY in the global world frame (as if no contact reaction was present at contact i) + Vec3 gdot( ( bodyCache.v_[contactCache.body1_[i]->index_] + bodyCache.dv_[contactCache.body1_[i]->index_] ) + - ( bodyCache.v_[contactCache.body2_[i]->index_] + bodyCache.dv_[contactCache.body2_[i]->index_] ) + + ( bodyCache.w_[contactCache.body1_[i]->index_] + bodyCache.dw_[contactCache.body1_[i]->index_] ) % contactCache.r1_[i] + - ( bodyCache.w_[contactCache.body2_[i]->index_] + bodyCache.dw_[contactCache.body2_[i]->index_] ) % contactCache.r2_[i] /* + diag_[i] * p */ ); // Change from the global world frame to the contact frame Mat3 contactframe( contactCache.n_[i], contactCache.t_[i], contactCache.o_[i] ); Vec3 gdot_nto( contactframe.getTranspose() * gdot ); // The constraint in normal direction is actually a positional constraint but instead of g_n we use g_n/dt equivalently and call it gdot_n - gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + contactCache.dist_[i] ) * dtinv; + gdot_nto[0] += ( /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) + * - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + contactCache.dist_[i] ) * dtinv; if( gdot_nto[0] >= 0 ) { - // Contact is separating if no contact reaction is present at contact i. - + // Contact is separating if no contact reaction is present at contact i delta_max = std::max( delta_max, std::max( std::abs( contactCache.p_[i][0] ), std::max( std::abs( contactCache.p_[i][1] ), std::abs( contactCache.p_[i][2] ) ) ) ); + // Reset impulse to zero. contactCache.p_[i] = Vec3(); - - // No need to apply zero impulse. } else { // Contact is persisting. @@ -1086,35 +1088,40 @@ inline real_t HardContactSemiImplicitTimesteppingSolvers::relaxInelasticCoulombC p_cf = p_cf - overRelaxationParam_ * tmp; } - // TODO: calculate cohesion from contact area - const real_t cohesion = contactCache.surf_[i]; - p_cf[0] += cohesion; + // Cohesion --> + real_t c( 0 ); + if( contactCache.surf_[i] > real_t(0) ) + c = contactCache.surf_[i] * contactCache.dist_[i] * contactCache.dist_[i]; + // <-- Cohesion - // Project on friction cone (Tasora et al., 2011; Fig. 2; Eq. (46)). - real_t flimit( contactCache.mu_[i] * p_cf[0] ); - real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); +// // Project on friction cone (Tasora et al., 2011; Fig. 2; Eq. (46)). +// real_t flimit( contactCache.mu_[i] * p_cf[0] ); +// real_t fsq( p_cf[1] * p_cf[1] + p_cf[2] * p_cf[2] ); +// +// if( p_cf[0] > 0 && fsq < flimit * flimit ) { +// // Unconstrained minimum is INSIDE THE UPPER CONE, +// // with slope n*mu +// // leading to a static contact and no projection is necessary. +// } +// else if( p_cf[0] < 0 && fsq < math::sq( p_cf[0] / contactCache.mu_[i] ) ) { +// // Unconstrained minimum is INSIDE THE LOWER CONE, +// // with slope n/mu +// // leading to a separating contact where no contact reaction is present, +// // i.e. the unconstrained minimum is projected to the tip of the lower cone. +// p_cf = Vec3( cohesion, 0, 0 ); +// } +// else { +// // Unconstrained minimum is OUTSIDE THE CONE -> Project on cone surface, Eq. (45) in Tasora et al., 2011 +// real_t f( std::sqrt( fsq ) ); // gamma_r +// p_cf[0] = ( f * contactCache.mu_[i] + p_cf[0] ) / ( math::sq( contactCache.mu_[i] ) + 1 ); // p_cf[0] = gamma_n +// +// real_t factor( contactCache.mu_[i] * p_cf[0] / f ); +// p_cf[1] *= factor; +// p_cf[2] *= factor; +// } - if( p_cf[0] > 0 && fsq < flimit * flimit ) { - // Unconstrained minimum is INSIDE THE UPPER CONE, - // with slope n*mu - // leading to a static contact and no projection is necessary. - } - else if( p_cf[0] < 0 && fsq < math::sq( p_cf[0] / contactCache.mu_[i] ) ) { - // Unconstrained minimum is INSIDE THE LOWER CONE, - // with slope n/mu - // leading to a separating contact where no contact reaction is present, - // i.e. the unconstrained minimum is projected to the tip of the lower cone. - p_cf = Vec3( cohesion, 0, 0 ); - } - else { - // Unconstrained minimum is OUTSIDE THE CONE -> Project on cone surface, Eq. (45) in Tasora et al., 2011 - real_t f( std::sqrt( fsq ) ); // gamma_r - p_cf[0] = ( f * contactCache.mu_[i] + p_cf[0] ) / ( math::sq( contactCache.mu_[i] ) + 1 ); // p_cf[0] = gamma_n - - real_t factor( contactCache.mu_[i] * p_cf[0] / f ); - p_cf[1] *= factor; - p_cf[2] *= factor; - } + CoulombOrowan cone( contactCache.mu_[i], contactCache.k_[i], c ); + cone.project( p_cf ); // Tasora et al., 2011 ends here // ------------------------------------------------------------------------------------- diff --git a/src/pe/cr/YieldSurface.h b/src/pe/cr/YieldSurface.h new file mode 100644 index 0000000000000000000000000000000000000000..e9260b56ae885a41fd984c5a01382f042df4a141 --- /dev/null +++ b/src/pe/cr/YieldSurface.h @@ -0,0 +1,249 @@ +//====================================================================================================================== +// +// 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 HCSITS.h +//! \author Tobias Preclik +//! \author Sebastian Eibl <sebastian.eibl@fau.de> +//! \brief Header file for the hard contact solver +// +//====================================================================================================================== + +#pragma once + +//************************************************************************************************* +// Includes +//************************************************************************************************* + +#include "pe/Types.h" + +namespace walberla { +namespace pe { +namespace cr { + + +//================================================================================================= +// +// CLASS DEFINITIONS +// +//================================================================================================= + +//************************************************************************************************* +/** + * \ingroup pe + * \brief Abstract base class for yield surfaces. + */ +class YieldSurface +{ + /** + * \brief Checks if the contact reaction is inside the yield surface. + * \param p The contact reaction in contact frame coordinates. + * \return A bool indicating whether the contact reaction is inside the yield surface. + */ + virtual bool contains( const Vec3 & p ) = 0; + + /** + * \brief Handles the projection of the contact reaction on the yield surface. + * \param p The contact reaction in contact frame coordinates. + */ + virtual void project( Vec3 & p ) = 0; +}; + + +/** + * \ingroup pe + * \brief The Coulomb friction cone with optional cohesion. + */ +class Coulomb : public YieldSurface +{ +public: + explicit Coulomb( real_t mu, real_t c = real_t(0) ) + : mu_( mu ), c_( c ) { } + + bool contains( const Vec3 & p ) WALBERLA_OVERRIDE + { + // Unconstrained minimum is INSIDE THE UPPER CONE, + // with slope n*mu and minimum -c. + return ( p[0] > -c_ && p[1] < mu_ * p[0] ); + } + + void project( Vec3 & p ) WALBERLA_OVERRIDE + { + // Project on friction cone (Tasora et al., 2011; Fig. 2; Eq. (46)). + real_t pt_sq( p[1] * p[1] + p[2] * p[2] ); + real_t pt_lim( mu_ * p[0] ); + + if( p[0] > -c_ && pt_sq < pt_lim * pt_lim ) + { + // Unconstrained minimum is INSIDE THE UPPER CONE, + // with slope n*mu + // leading to a static contact and no projection is necessary. + } + else if( p[0] < -c_ && pt_sq < math::sq( p[0] / mu_ ) ) + { + // Unconstrained minimum is INSIDE THE LOWER CONE, + // with slope n/mu + // leading to a separating contact where no contact reaction is present, + // i.e. the unconstrained minimum is projected to the tip of the lower cone. + p = Vec3( -c_, 0, 0 ); + } + else + { + // Unconstrained minimum is OUTSIDE THE CONE -> Project on cone surface, Eq. (45) in Tasora et al., 2011 + real_t pt( std::sqrt( pt_sq ) ); // gamma_r + p[0] = ( pt * mu_ + p[0] ) / ( math::sq( mu_ ) + 1 ); // p_cf[0] = gamma_n + real_t factor( mu_ * p[0] / pt ); + p[1] *= factor; + p[2] *= factor; + } + } + +private: + const real_t mu_; + const real_t c_; +}; // class Coulomb + + +/** + * \ingroup pe + * \brief The Tresca law yield surface. + */ +class Tresca : public YieldSurface +{ +public: + explicit Tresca( const real_t pt_cf_max ) + : pt_cf_max_( pt_cf_max ) { } + + bool contains( const Vec3 & p ) WALBERLA_OVERRIDE + { + return p[1] < pt_cf_max_; + } + + void project( Vec3 & p ) WALBERLA_OVERRIDE + { + // Ratio of (squared) tangential reaction and (squared) tangential limit + const real_t factor = ( p[1] * p[1] + p[2] * p[2] ) / pt_cf_max_ * pt_cf_max_; + + // Actual reaction exceeds the limit + if( factor > real_t(1) ) + { + p[1] /= factor; + p[2] /= factor; + } + } + +private: + const real_t pt_cf_max_; +}; // class Tresca + + +///** +// * \ingroup pe +// * \brief The Shaw law yield surface with optional cohesion. +// */ +//class Shaw : public YieldSurface +//{ +//public: +// Shaw( real_t k, real_t c = real_t(0) ) +// : k_( k ), c_( c ) { } +// +// bool contains( const Vec3 & p_cf ) WALBERLA_OVERRIDE +// { +// +// } +// +// void project( Vec3 & p_cf ) WALBERLA_OVERRIDE +// { +// +// } +// +// +//private: +// const real_t k_; +// const real_t c_; +//}; // class Shaw + + +/** + * \ingroup pe + * \brief The Coulomb-Orowan yield surface with optional cohesion. + */ +class CoulombOrowan : public YieldSurface +{ +public: + + /** + * \brief The constructor. + * \param mu The coulomb friction coefficient \in [0, 1.0]. + * \param k The elastic limit or yield strength contact reaction of the material. + * \param c The optional cohesion of the material. + */ + CoulombOrowan( real_t mu, real_t k, real_t c = real_t(0) ) + : mu_( mu ), k_( k ), c_( c ), pnc_( k / mu ) { } + + bool contains( const Vec3 & p ) WALBERLA_OVERRIDE + { + return ( p[0] > -c_ && p[1] < math::min( p[0] * mu_, k_ ) ); + } + + void project( Vec3 & p ) WALBERLA_OVERRIDE + { + // Project on friction cone (Tasora et al., 2011; Fig. 2; Eq. (46)). + real_t pt_sq(p[1] * p[1] + p[2] * p[2] ); + real_t pt_lim( math::min(mu_ * p[0], k_ ) ); + + if(p[0] >= pnc_ && pt_sq > k_ * k_ ) + { + // OUTSIDE THE UPPER CONE ABOVE THE CRITICAL NORMAL REACTION + // where k is the limit tangential reaction + const real_t factor = k_ * k_ / pt_sq; + p[1] *= factor; + p[2] *= factor; + } + else if(p[0] > -c_ && pt_sq < pt_lim * pt_lim ) + { + // INSIDE THE UPPER CONE, + // with slope n*mu + // leading to a static contact and no projection is necessary. + } + else if(p[0] < -c_ && pt_sq < math::sq(p[0] / mu_ ) ) + { + // INSIDE THE LOWER CONE, + // with slope n/mu + // leading to a separating contact where no contact reaction is present, + // i.e. the unconstrained minimum is projected to the tip of the lower cone. + p = Vec3(-c_, 0, 0 ); + } + else + { + // OUTSIDE THE UPPER CONE BELOW -> Project on cone surface, Eq. (45) in Tasora et al., 2011 + real_t pt( std::sqrt( pt_sq ) ); // gamma_r + p[0] = (pt * mu_ + p[0] ) / (math::sq(mu_ ) + 1 ); // p[0] = gamma_n + const real_t factor( mu_ * p[0] / pt ); + p[1] *= factor; + p[2] *= factor; + } + } + + +private: + const real_t mu_; + const real_t k_; + const real_t c_; + const real_t pnc_; +}; // class CoulombOrowan + +} // namespace cr +} // namespace pe +} // namespace walberla