diff --git a/src/pe/cr/HCSITS.h b/src/pe/cr/HCSITS.h index 896f358080c9aed186ecf1d203b503f3285b297c..67056df71da2ccc3b15a3085517e0529c951d502 100644 --- a/src/pe/cr/HCSITS.h +++ b/src/pe/cr/HCSITS.h @@ -84,12 +84,12 @@ private: { void resize(const size_t n); - std::vector<Vec3> r1_, r2_; + std::vector<Vec3> r1_, r2_; // vector pointing from body1/body2 to the contact position std::vector<BodyID> body1_, body2_; - std::vector<Vec3> n_, t_, o_; - std::vector<real_t> dist_; - std::vector<real_t> mu_; - std::vector<Mat3> diag_nto_; + 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<Mat3> diag_nto_; std::vector<Mat3> diag_nto_inv_; std::vector<Mat2> diag_to_inv_; std::vector<real_t> diag_n_inv_; @@ -102,9 +102,9 @@ public: enum RelaxationModel { InelasticFrictionlessContact, ApproximateInelasticCoulombContactByDecoupling, -// ApproximateInelasticCoulombContactByOrthogonalProjections, + ApproximateInelasticCoulombContactByOrthogonalProjections, InelasticCoulombContactByDecoupling, -// InelasticCoulombContactByOrthogonalProjections, + InelasticCoulombContactByOrthogonalProjections, InelasticGeneralizedMaximumDissipationContact }; //********************************************************************************************** @@ -136,6 +136,7 @@ public: inline const std::map<IBlockID::IDType, ContactCache> getContactCache() const { return blockToContactCache_; } inline real_t getSpeedLimitFactor() const; inline size_t getMaxIterations() const { return maxIterations_; } + inline real_t getOverRelaxationParameter() const { return overRelaxationParam_; } inline real_t getRelaxationParameter() const { return relaxationParam_; } inline real_t getErrorReductionParameter() const { return erp_; } inline RelaxationModel getRelaxationModel() const { return relaxationModel_; } @@ -145,6 +146,7 @@ public: //**Set functions******************************************************************************* /*!\name Set functions */ //@{ + inline void setOverRelaxationParameter( real_t omega ); inline void setRelaxationParameter( real_t f ); inline void setMaxIterations( size_t n ); inline void setRelaxationModel( RelaxationModel relaxationModel ); @@ -235,6 +237,7 @@ private: size_t maxSubIterations_; //!< Maximum number of iterations of iterative solvers in the one-contact problem. real_t abortThreshold_; //!< If L-infinity iterate difference drops below this threshold the iteration is aborted. RelaxationModel relaxationModel_; //!< The method used to relax unilateral contacts + real_t overRelaxationParam_; //!< Parameter specifying the convergence speed for othogonal projection models. real_t relaxationParam_; //!< Parameter specifying underrelaxation of velocity corrections for boundary bodies. real_t maximumPenetration_; size_t numContacts_; @@ -322,6 +325,28 @@ inline real_t HardContactSemiImplicitTimesteppingSolvers::getSpeedLimitFactor() // //================================================================================================= +//************************************************************************************************* +/*!\brief Sets the relaxation parameter for boundary bodies. + * + * \param f The overrelaxation parameter. + * \return void + * + * The overrelaxation parameter \omega is only used when the relaxation model is one of + * - ApproximateInelasticCoulombContactByOrthogonalProjections + * - InelasticCoulombContactByOrthogonalProjections + * + * It is used to control the convergence of the model. Large values show faster convergence, + * but they can also lead to divergence ("exploding" particles). The default values is 1.0. + */ +inline void HardContactSemiImplicitTimesteppingSolvers::setOverRelaxationParameter( real_t omega ) +{ + WALBERLA_ASSERT_GREATER( omega, 0, "Overrelaxation parameter must be positive." ); + + overRelaxationParam_ = omega; +} +//************************************************************************************************* + + //************************************************************************************************* /*!\brief Sets the relaxation parameter for boundary bodies. * diff --git a/src/pe/cr/HCSITS.impl.h b/src/pe/cr/HCSITS.impl.h index ba8fd6aa29d0ec028448b838838406c9d49c50f2..7218b4580519cd623a74976c678a5786eba69184 100644 --- a/src/pe/cr/HCSITS.impl.h +++ b/src/pe/cr/HCSITS.impl.h @@ -97,6 +97,7 @@ inline HardContactSemiImplicitTimesteppingSolvers::HardContactSemiImplicitTimest , maxSubIterations_ ( 20 ) , abortThreshold_ ( real_c(1e-7) ) , relaxationModel_ ( InelasticFrictionlessContact ) + , overRelaxationParam_( real_c(1.0) ) , relaxationParam_ ( real_c(0.75) ) , maximumPenetration_ ( real_c(0.0) ) , numContacts_ ( 0 ) @@ -388,17 +389,17 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d delta_max = relaxApproximateInelasticCoulombContactsByDecoupling( dtinv, contactCache, bodyCache ); break; - // case ApproximateInelasticCoulombContactByOrthogonalProjections: - // delta_max = relaxInelasticCoulombContactsByOrthogonalProjections( dtinv, true, contactCache, bodyCache ); - // break; + case ApproximateInelasticCoulombContactByOrthogonalProjections: + delta_max = relaxInelasticCoulombContactsByOrthogonalProjections( dtinv, true, contactCache, bodyCache ); + break; case InelasticCoulombContactByDecoupling: delta_max = relaxInelasticCoulombContactsByDecoupling( dtinv, contactCache, bodyCache ); break; - // case InelasticCoulombContactByOrthogonalProjections: - // delta_max = relaxInelasticCoulombContactsByOrthogonalProjections( dtinv, false, contactCache, bodyCache ); - // break; + case InelasticCoulombContactByOrthogonalProjections: + delta_max = relaxInelasticCoulombContactsByOrthogonalProjections( dtinv, false, contactCache, bodyCache ); + break; case InelasticGeneralizedMaximumDissipationContact: delta_max = relaxInelasticGeneralizedMaximumDissipationContacts( dtinv, contactCache, bodyCache ); @@ -1049,55 +1050,68 @@ inline real_t HardContactSemiImplicitTimesteppingSolvers::relaxInelasticCoulombC 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 ); - //real_t gdot_n ( trans( contactCache.n_[i] ) * gdot ); // The component of gdot along the contact normal n - //Vec3 gdot_t ( gdot - gdot_n * contactCache.n_[i] ); // The components of gdot tangential to the contact normal n - //real_t g_n ( gdot_n * dt /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + contactCache.dist_[i] ); // The gap in normal direction + //real_t gdot_n( trans( contactCache.n_[i] ) * gdot ); // The component of gdot along the contact normal n + //Vec3 gdot_t( gdot - gdot_n * contactCache.n_[i] ); // The components of gdot tangential to the contact normal n + //real_t g_n( gdot_n * dt /* + trans( contactCache.n_[i] ) * ( contactCache.body1_[i]->getPosition() + contactCache.r1_[i] ) - ( contactCache.body2_[i]->getPosition() + contactCache.r2_[i] ) */ + contactCache.dist_[i] ); // The gap in normal direction // 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; + + // Tasora et al., 2011 starts here + // ------------------------------------------------------------------------------------- - const real_t w( 1 ); // w > 0 Vec3 p_cf( contactframe.getTranspose() * contactCache.p_[i] ); if( approximate ) { - // Calculate next iterate (Anitescu/Tasora). - p_cf = p_cf - w * ( contactCache.diag_nto_[i] * p_cf + gdot_nto ); + // Calculate next iterate (Tasora et al., 2011). + p_cf = p_cf - overRelaxationParam_ * ( contactCache.diag_nto_[i] * p_cf + gdot_nto ); } else { // Calculate next iterate (De Saxce/Feng). Vec3 tmp( contactCache.diag_nto_[i] * p_cf + gdot_nto ); tmp[0] += std::sqrt( math::sq( tmp[1] ) + math::sq( tmp[2] ) ) * contactCache.mu_[i]; - p_cf = p_cf - w * tmp; + p_cf = p_cf - overRelaxationParam_ * tmp; } - // Project. + // 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 in cone leading to a static contact and no projection - // is necessary. + // 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 < p_cf[0] / math::sq( contactCache.mu_[i] ) ) { - // Unconstrained minimum is in dual cone leading to a separating contact where no contact - // reaction is present (the unconstrained minimum is projected to the tip of the cone). - + 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(); } else { - // The contact is dynamic. - real_t f( std::sqrt( fsq ) ); - p_cf[0] = ( f * contactCache.mu_[i] + p_cf[0] ) / ( math::sq( contactCache.mu_[i] ) + 1 ); + // 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; } + // Tasora et al., 2011 ends here + // ------------------------------------------------------------------------------------- + Vec3 p_wf( contactframe * p_cf ); Vec3 dp( contactCache.p_[i] - p_wf ); delta_max = std::max( delta_max, std::max( std::abs( dp[0] ), std::max( std::abs( dp[1] ), std::abs( dp[2] ) ) ) );