/* ---------------------------------------------------------------------- This is the ██╗ ██╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗████████╗███████╗ ██║ ██║██╔════╝ ██╔════╝ ██╔════╝ ██║ ██║╚══██╔══╝██╔════╝ ██║ ██║██║ ███╗██║ ███╗██║ ███╗███████║ ██║ ███████╗ ██║ ██║██║ ██║██║ ██║██║ ██║██╔══██║ ██║ ╚════██║ ███████╗██║╚██████╔╝╚██████╔╝╚██████╔╝██║ ██║ ██║ ███████║ ╚══════╝╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝® DEM simulation engine, released by DCS Computing Gmbh, Linz, Austria http://www.dcs-computing.com, office@dcs-computing.com LIGGGHTS® is part of CFDEM®project: http://www.liggghts.com | http://www.cfdem.com Core developer and main author: Christoph Kloss, christoph.kloss@dcs-computing.com LIGGGHTS® is open-source, distributed under the terms of the GNU Public License, version 2 or later. It 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. You should have received a copy of the GNU General Public License along with LIGGGHTS®. If not, see http://www.gnu.org/licenses . See also top-level README and LICENSE files. LIGGGHTS® and CFDEM® are registered trade marks of DCS Computing GmbH, the producer of the LIGGGHTS® software and the CFDEM®coupling software See http://www.cfdem.com/terms-trademark-policy for details. ------------------------------------------------------------------------- Contributing author and copyright for this file: Rahul Mohanty (University of Edinburgh, P&G) Tomaz M. Zorec (University of Ljubljana) ------------------------------------------------------------------------- */ #ifdef NORMAL_MODEL NORMAL_MODEL(EDINBURGH_STIFFNESS,edinburgh/stiffness,11) #else #ifndef NORMAL_MODEL_EDINBURGH_STIFFNESS_H_ #define NORMAL_MODEL_EDINBURGH_STIFFNESS_H_ #include "contact_models.h" #include "normal_model_base.h" #include #include "atom.h" #include "force.h" #include "update.h" #include "global_properties.h" namespace LIGGGHTS { namespace ContactModels { template<> class NormalModel : public NormalModelBase { public: NormalModel(LAMMPS * lmp, IContactHistorySetup * hsetup,class ContactModelBase *c) : NormalModelBase(lmp, hsetup, c), K_elastic(NULL), kn2k1(NULL), kn2kc(NULL), CoeffRestLog(NULL), betaeff(NULL), dex(0.0), f_adh(NULL), cex(0.0), gamma_surf(NULL), history_offset(-1), kc_offset(-1), fo_offset(-1), tangential_damping(false), limitForce(false), fixKc(false) { history_offset = hsetup->add_history_value("deltaMax", "1"); hsetup->add_history_value("old_delta", "1"); kc_offset = hsetup->add_history_value("kc", "1"); fo_offset = hsetup->add_history_value("fo", "1"); c->add_history_offset("kc_offset", kc_offset); c->add_history_offset("fo_offset", fo_offset); } void registerSettings(Settings & settings) { settings.registerOnOff("tangential_damping", tangential_damping, true); settings.registerOnOff("limitForce", limitForce,true); settings.registerOnOff("fixKc", fixKc); //TODO error->one(FLERR,"TODO here also check if right surface model used"); } inline void postSettings(IContactHistorySetup *, ContactModelBase *) {} void connectToProperties(PropertyRegistry & registry) { registry.registerProperty("K_elastic", &MODEL_PARAMS::createLoadingStiffness,"model edinburgh/stiffness"); registry.registerProperty("kn2k1", &MODEL_PARAMS::createUnloadingStiffness, "model edinburgh/stiffness"); registry.registerProperty("kn2kc", &MODEL_PARAMS::createCoeffAdhesionStiffness,"model edinburgh/stiffness"); registry.registerProperty("CoeffRestLog", &MODEL_PARAMS::createCoeffRestLog, "model edinburgh/stiffness"); registry.registerProperty("betaeff", &MODEL_PARAMS::createBetaEff,"model edinburgh/stiffness"); registry.registerProperty("dex", &MODEL_PARAMS::createOverlapExponent, "model edinburgh/stiffness"); registry.registerProperty("f_adh", &MODEL_PARAMS::createPullOffForce, "model edinburgh/stiffness"); registry.registerProperty("cex", &MODEL_PARAMS::createAdhesionExponent, "model edinburgh/stiffness"); registry.registerProperty("gamma_surf", &MODEL_PARAMS::createSurfaceEnergy, "model edinburgh/stiffness"); registry.connect("betaeff", betaeff,"model edinburgh/stiffness"); registry.connect("dex", dex,"model edinburgh/stiffness"); registry.connect("f_adh", f_adh,"model edinburgh/stiffness"); registry.connect("kn2k1", kn2k1,"model edinburgh/stiffness"); registry.connect("cex", cex,"model edinburgh/stiffness"); registry.connect("gamma_surf", gamma_surf,"model edinburgh/stiffness"); registry.connect("CoeffRestLog", CoeffRestLog, "model edinburgh/stiffness"); registry.connect("K_elastic", K_elastic, "model edinburgh/stiffness"); registry.connect("kn2kc", kn2kc,"model edinburgh/stiffness"); } // effective exponent for stress-strain relationship inline double stressStrainExponent() { return 1.5; } inline double calculate_deltan_p_max (double deltan_p, double * const history, int count_flag, const double f_0, double fTmp, const double k2, double dex, double dex_i, double deltan, double k_adh) { //calculating the maximum overlap double deltan_p_max; if (count_flag == 0 ) { if (deltan_p > history[0]) { deltan_p_max = deltan_p; history[0] = deltan_p_max; } else { deltan_p_max = history[0]; } } else { deltan_p_max = pow((pow(history[1], dex) + ((k_adh/k2)*pow(history[1], cex))), dex_i); history[0] = deltan_p_max; } return history[0]; } inline double whichd(double fTmp, double k1, double deltan_e, double d1, double d2) { //calculation distance between the particles based on branch if (fTmp >= k1 * deltan_e){ return d1; } else{ return d2; } } inline double calculate_k_adh(double d, double risq, double rjsq, const double g_surf, double f_min_lim, double deltan_pe_max, double deltan_p_max, const double k2, const double dex_i, const double cex) { double new_k_c, delta_min, a, f_min, dsq; dsq = d*d; a = (1./(2.*d)) * sqrt(4* dsq * risq - ((dsq - rjsq + risq)*(dsq - rjsq + risq))); f_min = ( 1.5 * M_PI * g_surf * a); if (f_min > f_min_lim) { f_min = f_min_lim; delta_min = 0.5 * deltan_p_max; } else { delta_min = pow((-f_min + k2 * deltan_pe_max)/k2, dex_i); } new_k_c = f_min/pow(delta_min, cex); return new_k_c; } inline void surfacesIntersect(SurfacesIntersectData & sidata, ForceData & i_forces, ForceData & j_forces) { const int itype = sidata.itype; const int jtype = sidata.jtype; const double deltan = sidata.deltan; double ri = sidata.radi; double rj = sidata.radj; double meff=sidata.meff; const double f_0 = f_adh[itype][jtype]; double kn = K_elastic[itype][jtype]; double kt = kn; // convert Kn and Kt from pressure units to force/distance^2 kn /= force->nktv2p; kt /= force->nktv2p; const double dex_i = 1./dex; const double k1 = kn; const double k2 = kn*kn2k1[itype][jtype]; double gamman, gammat; gamman = sqrt(4.*meff*kn/(1.+(M_PI/CoeffRestLog[itype][jtype])*(M_PI/CoeffRestLog[itype][jtype]))); gammat = sqrt(4.*meff*kn/(1.+(M_PI/CoeffRestLog[itype][jtype])*(M_PI/CoeffRestLog[itype][jtype]))); if(sidata.contact_flags) *sidata.contact_flags |= CONTACT_NORMAL_MODEL; double * const history = &sidata.contact_history[history_offset]; double * const kc_history = &sidata.contact_history[kc_offset]; double * const fo_history = &sidata.contact_history[fo_offset]; if (!tangential_damping) gammat = 0.0; double Fn_contact, deltan_e, deltan_ce, deltaMax_e,k_adh = 0.0; if (fixKc == false) { const double lambda = pow((1. - k1/k2), dex_i); int count_flag = 0; // get the history value -- maximal overlap double deltan_p = lambda*deltan; double deltan_p_max, deltan_pe_max; deltan_e = pow(deltan, dex); deltan_ce = pow(deltan, cex); double fTmp = k2*(deltan_e - history[0]); double d, d1, d2, r_sum; deltan_p_max = calculate_deltan_p_max (deltan_p, &sidata.contact_history[history_offset], count_flag, f_0, fTmp, k2, dex, dex_i, deltan, k_adh); // Normal force calculation for Edinburgh model const double g_surf = gamma_surf[itype][jtype]; r_sum = sidata.radsum; d1 = sidata.is_wall ? ri : r_sum - deltan; d2 = sidata.is_wall ? ri : r_sum - deltan_p_max; double risq = ri*ri; double rjsq = rj*rj; temp_calc: deltan_pe_max = pow(deltan_p_max, dex); const double f_min_lim = (k2 * deltan_pe_max) * 0.5; fTmp=k2*(deltan_e - deltan_pe_max); if (fTmp >= k1 * deltan_e){ // loading Fn_contact = k1 * deltan_e; }else{ d = whichd(fTmp, k1, deltan_e, d1, d2); k_adh = calculate_k_adh(d, risq, rjsq, g_surf, f_min_lim, deltan_pe_max, deltan_p_max, k2, dex_i, cex); kc_history[0] = k_adh; if (fTmp > (-k_adh * deltan_ce)){ Fn_contact = fTmp; }else{ // cohesion part if (deltan > history[1]){ count_flag = 1; deltan_p_max = calculate_deltan_p_max (deltan_p, &sidata.contact_history[history_offset], count_flag, f_0, fTmp, k2, dex, dex_i, deltan, k_adh); goto temp_calc; } Fn_contact = -k_adh * deltan_ce; } } history[1] = deltan; }else{ const double k_adh = kn2kc[itype][jtype] * kn ; kc_history[0] = k_adh; double deltaMax; // the 4th value of the history array is deltaMax if (deltan > history[0]) { history[0] = deltan; deltaMax = deltan; }else{ deltaMax = history[0]; } deltan_e = pow(deltan, dex); deltan_ce = pow(deltan, cex); deltaMax_e = pow(deltaMax, dex); const double fTmp = k2*(deltan_e-deltaMax_e)+k1*deltaMax_e; if (fTmp >= k1 * deltan_e) // loading { Fn_contact = k1 * deltan_e; } else { if (fTmp > (-k_adh * deltan_ce)) { Fn_contact = fTmp; } else { // cohesion part Fn_contact = -k_adh * deltan_ce; const double newDeltaMax = ((k2 + k_adh)/(k2-k1))*deltan; history[0] = newDeltaMax; } } } const double Fn_damping = -gamman*sidata.vn; double Fn = Fn_damping + Fn_contact + f_0; fo_history[0]=f_0; if(limitForce && (Fn<0.0) && k_adh == 0 && f_0 == 0.0) { Fn = 0.0; } // printf("History passed from normal: %f, f_o = %f\n",kc_history[0], fo_history[0]); sidata.Fn = Fn; sidata.kn = kn; sidata.kt = kt; sidata.gamman = gamman; sidata.gammat = gammat; // apply normal force if(sidata.is_wall) { const double Fn_ = Fn * sidata.area_ratio; i_forces.delta_F[0] = Fn_ * sidata.en[0]; i_forces.delta_F[1] = Fn_ * sidata.en[1]; i_forces.delta_F[2] = Fn_ * sidata.en[2]; } else { i_forces.delta_F[0] = sidata.Fn * sidata.en[0]; i_forces.delta_F[1] = sidata.Fn * sidata.en[1]; i_forces.delta_F[2] = sidata.Fn * sidata.en[2]; j_forces.delta_F[0] = -i_forces.delta_F[0]; j_forces.delta_F[1] = -i_forces.delta_F[1]; j_forces.delta_F[2] = -i_forces.delta_F[2]; } } void surfacesClose(SurfacesCloseData & scdata, ForceData&, ForceData&) { if(scdata.contact_flags) *scdata.contact_flags &= ~CONTACT_NORMAL_MODEL; double * const history = &scdata.contact_history[history_offset]; history[0] = 0.0; history[1] = 0.0; } void beginPass(SurfacesIntersectData&, ForceData&, ForceData&){} void endPass(SurfacesIntersectData&, ForceData&, ForceData&){} protected: double **K_elastic; double **kn2k1; double **kn2kc; double **CoeffRestLog; double **betaeff; double dex; double **f_adh; double cex; double **gamma_surf; int history_offset; int kc_offset; int fo_offset; bool tangential_damping; bool limitForce; bool fixKc; }; } } #endif #endif