JKR Model Implementation
See this page for the documentation of this contact model.
contactmodeljkr.h
1// contactmodeljkr.h
2#include "contactmodel/src/contactmodelmechanical.h"
3
4#include "module/interface/itablelist.h"
5#include "module/interface/itable.h"
6
7#ifdef JKR_LIB
8# define JKR_EXPORT EXPORT_TAG
9#elif defined(NO_MODEL_IMPORT)
10# define JKR_EXPORT
11#else
12# define JKR_EXPORT IMPORT_TAG
13#endif
14
15namespace cmodelsxd {
16 using namespace itasca;
17
18 class ContactModelJKR : public ContactModelMechanical {
19 public:
20 // Constructor: Set default values for contact model properties.
21 JKR_EXPORT ContactModelJKR();
22 JKR_EXPORT ContactModelJKR(const ContactModelJKR &) noexcept;
23 JKR_EXPORT const ContactModelJKR & operator=(const ContactModelJKR &);
24 JKR_EXPORT void addToStorage(poly::vector<ContactModelMechanical> *s,int ww = -1) override;
25 // Destructor, called when contact is deleted: free allocated memory, etc.
26 JKR_EXPORT virtual ~ContactModelJKR();
27 // Contact model name (used as keyword for commands and FISH).
28 QString getName() const override { return "jkr"; }
29 // The index provides a quick way to determine the type of contact model.
30 // Each type of contact model in PFC must have a unique index; this is assigned
31 // by PFC when the contact model is loaded. This index should be set to -1
32 void setIndex(int i) override { index_ = i; }
33 int getIndex() const override { return index_; }
34 // Contact model version number (e.g., MyModel05_1). The version number can be
35 // accessed during the save-restore operation (within the archive method,
36 // testing {stream.getRestoreVersion() == getMinorVersion()} to allow for
37 // future modifications to the contact model data structure.
38 uint32 getMinorVersion() const override;
39 // Copy the state information to a newly created contact model.
40 // Provide access to state information, for use by copy method.
41 void copy(const ContactModel *c) override;
42 // Provide save-restore capability for the state information.
43 void archive(ArchiveStream &) override;
44 // Enumerator for the properties.
45 enum PropertyKeys {
46 kwShear = 1
47 , kwPoiss
48 , kwFric
49 , kwjkrF
50 , kwjkrS
51 , kwDpNRatio
52 , kwDpSRatio
53 , kwDpMode
54 , kwDpF
55 , kwResFric
56 , kwResMoment
57 , kwResS
58 , kwKsFac
59 , kwSurfAdh
60 , kwActiveMode
61 , kwA0
62 , kwPullOff
63 , kwTearOff
64 };
65 // Contact model property names in a comma separated list. The order corresponds with
66 // the order of the PropertyKeys enumerator above. One can visualize any of these
67 // properties in PFC automatically.
68 QString getProperties() const override {
69 return "jkr_shear"
70 ",jkr_poiss"
71 ",fric"
72 ",jkr_force"
73 ",jkr_slip"
74 ",dp_nratio"
75 ",dp_sratio"
76 ",dp_mode"
77 ",dp_force"
78 ",rr_fric"
79 ",rr_moment"
80 ",rr_slip"
81 ",ks_fac"
82 ",surf_adh"
83 ",active_mode"
84 ",a0"
85 ",pull_off_f"
86 ",tear_off_d";
87 }
88 // Enumerator for the energies.
89 enum EnergyKeys {
90 kwEStrain = 1
91 , kwERRStrain
92 , kwESlip
93 , kwERRSlip
94 , kwEDashpot
95 };
96 // Contact model energy names in a comma separated list. The order corresponds with
97 // the order of the EnergyKeys enumerator above.
98 QString getEnergies() const override {
99 return "energy-strain-jkr"
100 ",energy-rrstrain"
101 ",energy-slip"
102 ",energy-rrslip"
103 ",energy-dashpot";
104 }
105 // Returns the value of the energy (base 1 - getEnergy(1) returns the estrain energy).
106 double getEnergy(uint32 i) const override;
107 // Returns whether or not each energy is accumulated (base 1 - getEnergyAccumulate(1)
108 // returns whether or not the estrain energy is accumulated which is false).
109 bool getEnergyAccumulate(uint32 i) const override;
110 // Set an energy value (base 1 - setEnergy(1) sets the estrain energy).
111 void setEnergy(uint32 i, const double &d) override; // Base 1
112 // Activate the energy. This is only called if the energy tracking is enabled.
113 void activateEnergy() override { if (energies_) return; energies_ = NEWC(Energies()); }
114 // Returns whether or not the energy tracking has been enabled for this contact.
115 bool getEnergyActivated() const override { return (energies_ != 0); }
116
117 // Enumerator for contact model related FISH callback events.
118 enum FishCallEvents {
119 fActivated = 0
120 , fSlipChange
121 };
122 // Contact model FISH callback event names in a comma separated list. The order corresponds with
123 // the order of the FishCallEvents enumerator above.
124 QString getFishCallEvents() const override {
125 return
126 "contact_activated"
127 ",slip_change";
128 }
129
130 // Return the specified contact model property.
131 QVariant getProperty(uint32 i, const IContact *) const override;
132 // The return value denotes whether or not the property corresponds to the global
133 // or local coordinate system (TRUE: global system, FALSE: local system). The
134 // local system is the contact-plane system (nst) defined as follows.
135 // If a vector V is expressed in the local system as (Vn, Vs, Vt), then V is
136 // expressed in the global system as {Vn*nc + Vs*sc + Vt*tc} where where nc, sc
137 // and tc are unit vectors in directions of the nst axes.
138 // This is used when rendering contact model properties that are vectors.
139 bool getPropertyGlobal(uint32 i) const override;
140 // Set the specified contact model property, ensuring that it is of the correct type
141 // and within the correct range --- if not, then throw an exception.
142 // The return value denotes whether or not the update has affected the timestep
143 // computation (by having modified the translational or rotational tangent stiffnesses).
144 // If true is returned, then the timestep will be recomputed.
145 bool setProperty(uint32 i, const QVariant &v, IContact *c) override;
146 // The return value denotes whether or not the property is read-only
147 // (TRUE: read-only, FALSE: read-write).
148 bool getPropertyReadOnly(uint32 i) const override;
149
150 // The return value denotes whether or not the property is inheritable
151 // (TRUE: inheritable, FALSE: not inheritable). Inheritance is provided by
152 // the endPropertyUpdated method.
153 bool supportsInheritance(uint32 i) const override;
154 // Return whether or not inheritance is enabled for the specified property.
155 bool getInheritance(uint32 i) const override { assert(i < 32); uint32 mask = to<uint32>(1 << i); return (inheritanceField_ & mask) ? true : false; }
156 // Set the inheritance flag for the specified property.
157 void setInheritance(uint32 i, bool b) override { assert(i < 32); uint32 mask = to<uint32>(1 << i); if (b) inheritanceField_ |= mask; else inheritanceField_ &= ~mask; }
158
159 // Prepare for entry into ForceDispLaw. The validate function is called when:
160 // (1) the contact is created, (2) a property of the contact that returns a true via
161 // the setProperty method has been modified and (3) when a set of cycles is executed
162 // via the {cycle N} command.
163 // Return value indicates contact activity (TRUE: active, FALSE: inactive).
164 bool validate(ContactModelMechanicalState *state, const double ×tep) override;
165 // The endPropertyUpdated method is called whenever a surface property (with a name
166 // that matches an inheritable contact model property name) of one of the contacting
167 // pieces is modified. This allows the contact model to update its associated
168 // properties. The return value denotes whether or not the update has affected
169 // the time step computation (by having modified the translational or rotational
170 // tangent stiffnesses). If true is returned, then the time step will be recomputed.
171 bool endPropertyUpdated(const QString &name, const IContactMechanical *c) override;
172 // The forceDisplacementLaw function is called during each cycle. Given the relative
173 // motion of the two contacting pieces (via
174 // state->relativeTranslationalIncrement_ (Ddn, Ddss, Ddst)
175 // state->relativeAngularIncrement_ (Dtt, Dtbs, Dtbt)
176 // Ddn : relative normal-displacement increment, Ddn > 0 is opening
177 // Ddss : relative shear-displacement increment (s-axis component)
178 // Ddst : relative shear-displacement increment (t-axis component)
179 // Dtt : relative twist-rotation increment
180 // Dtbs : relative bend-rotation increment (s-axis component)
181 // Dtbt : relative bend-rotation increment (t-axis component)
182 // The relative displacement and rotation increments:
183 // Dd = Ddn*nc + Ddss*sc + Ddst*tc
184 // Dt = Dtt*nc + Dtbs*sc + Dtbt*tc
185 // where nc, sc and tc are unit vectors in direc. of the nst axes, respectively.
186 // [see {Table 1: Contact State Variables} in PFC Model Components:
187 // Contacts and Contact Models: Contact Resolution]
188 // ) and the contact properties, this function must update the contact force and
189 // moment.
190 // The force_ is acting on piece 2, and is expressed in the local coordinate system
191 // (defined in getPropertyGlobal) such that the first component positive denotes
192 // compression. If we define the moment acting on piece 2 by Mc, and Mc is expressed
193 // in the local coordinate system (defined in getPropertyGlobal), then we must use the getMechanicalContact()->updateResultingTorquesLocal(...) method to
194 // get the total moment.
195 // The return value indicates the contact activity status (TRUE: active, FALSE:
196 // inactive) during the next cycle.
197 // Additional information:
198 // * If state->activated() is true, then the contact has just become active (it was
199 // inactive during the previous time step).
200 // * Fully elastic behavior is enforced during the SOLVE ELASTIC command by having
201 // the forceDispLaw handle the case of {state->canFail_ == true}.
202 bool forceDisplacementLaw(ContactModelMechanicalState *state, const double ×tep) override;
203 // The getEffectiveXStiffness functions return the translational and rotational
204 // tangent stiffnesses used to compute a stable time step. When a contact is sliding,
205 // the translational tangent shear stiffness is zero (but this stiffness reduction
206 // is typically ignored when computing a stable time step). If the contact model
207 // includes a dashpot, then the translational stiffnesses must be increased (see
208 // Potyondy (2009)).
209 // [Potyondy, D. 'Stiffness Matrix at a Contact Between Two Clumps,' Itasca
210 // Consulting Group, Inc., Minneapolis, MN, Technical Memorandum ICG6863-L,
211 // December 7, 2009.]
212 DVect2 getEffectiveTranslationalStiffness() const override { return effectiveTranslationalStiffness_; }
213 DAVect getEffectiveRotationalStiffness() const override { return effectiveRotationalStiffness_; }
214
215 // Return a new instance of the contact model. This is used in the CMAT
216 // when a new contact is created.
217 ContactModelJKR *clone() const override { return NEWC(ContactModelJKR()); }
218 // The getActivityDistance function is called by the contact-resolution logic when
219 // the CMAT is modified. Return value is the activity distance used by the
220 // checkActivity function.
221 double getActivityDistance() const override { return (rgap_ + distance_active_); } // initially 'distance_active_' is zero so that the contat becomes active when the particles touch (zerp overlap)
222 // when the contact becomes active,it is set so that the contact only becomes inactive at this tear_off_ distance
223 // The isOKToDelete function is called by the contact-resolution logic when...
224 // Return value indicates whether or not the contact may be deleted.
225 // If TRUE, then the contact may be deleted when it is inactive.
226 // If FALSE, then the contact may not be deleted (under any condition).
227 bool isOKToDelete() const override { return !isBonded(); }
228 // Zero the forces and moments stored in the contact model. This function is called
229 // when the contact becomes inactive.
230 void resetForcesAndMoments() override {
231 jkr_F(DVect(0.0)); dp_F(DVect(0.0));
232 res_M(DAVect(0.0));
233 if (energies_) {
234 //energies_->estrain_ = 0.0;
235 energies_->errstrain_ = 0.0;
236 }
237 distance_active_ = 0.0; // reset
238 }
239 void setForce(const DVect &v, IContact *c) override;
240 void setArea(const double&) override { throw Exception("The setArea method cannot be used with the JKR contact model."); }
241 double getArea() const override { return 0.0; }
242 // The checkActivity function is called by the contact-resolution logic when...
243 // Return value indicates contact activity (TRUE: active, FALSE: inactive).
244 // A contact with the arrlinear model is active if the surface gap is less than
245 // or equal to the attraction range (a_d0_).
246 bool checkActivity(const double &gap) override { return gap <= (rgap_ + distance_active_); } // initially 'distance_active_' is zero so that the contat becomes active when the particles touch (zerp overlap)
247 // when the contact becomes active,it is set so that the contact only becomes inactive at this tear_off_ distance
248 // Returns the sliding state (FALSE is returned if not implemented).
249 bool isSliding() const override { return jkr_S_; }
250 // Returns the bonding state (FALSE is returned if not implemented).
251 bool isBonded() const override { return false; }
252 // Both of these methods are called only for contacts with facets where the wall
253 // resolution scheme is set the full. In such cases one might wish to propagate
254 // contact state information (e.g., shear force) from one active contact to another.
255 // See the Faceted Wall section in the documentation.
256
257 // Return the total force that the contact model holds.
258 DVect getForce() const override;
259 // Return the total moment on 1 that the contact model holds
260 DAVect getMomentOn1(const IContactMechanical *) const override;
261 // Return the total moment on 1 that the contact model holds
262 DAVect getMomentOn2(const IContactMechanical *) const override;
263
264 DAVect getModelMomentOn1() const override;
265 DAVect getModelMomentOn2() const override;
266
267 // Used to efficiently get properties from the contact model for the object pane.
268 // List of properties for the object pane, comma separated.
269 // All properties will be cast to doubles for comparison. No other comparisons
270 // are supported. This may not be the same as the entire property list.
271 // Return property name and type for plotting.
272 void objectPropsTypes(std::vector<std::pair<QString,InfoTypes>> *) const override;
273 // All properties cast to doubles - this is what can be compared.
274 void objectPropValues(std::vector<double> *,const IContact *) const override;
275
276 // Methods to get and set properties.
277 const double & shear() const { return shear_; }
278 void shear(const double &d) { shear_ = d; }
279 const double & poiss() const { return poiss_; }
280 void poiss(const double &d) { poiss_ = d; }
281 const double & fric() const { return fric_; }
282 void fric(const double &d) { fric_ = d; }
283 const DVect & jkr_F() const { return jkr_F_; }
284 void jkr_F(const DVect &f) { jkr_F_ = f; }
285 bool jkr_S() const { return jkr_S_; }
286 void jkr_S(bool b) { jkr_S_ = b; }
287 const double & rgap() const { return rgap_; }
288 void rgap(const double &d) { rgap_ = d; }
289
290 const double & kn() const { return kn_; }
291 void kn(const double &d) { kn_ = d; }
292 const double & ks() const { return ks_; }
293 void ks(const double &d) { ks_ = d; }
294 const double & ks_fac() const { return ks_fac_; }
295 void ks_fac(const double &d) { ks_fac_ = d; }
296 const double & surf_adh() const { return surf_adh_; }
297 void surf_adh(const double &d) { surf_adh_ = d; }
298 const int & active_mode() const { return active_mode_; }
299 void active_mode(const int &d) { active_mode_ = d; }
300 const double & distance_active() const { return distance_active_; }
301 void distance_active(const double &d) { distance_active_ = d; }
302 const double & a0() const { return a0_; }
303 void a0(const double &d) { a0_ = d; }
304 const double & pull_off() const { return pull_off_f_; }
305 void pull_off(const double &d) { pull_off_f_ = d; }
306 const double & tear_off() const { return tear_off_d_; }
307 void tear_off(const double &d) { tear_off_d_ = d; }
308 const double & r_hertz() const { return r_hertz_; }
309 void r_hertz(const double &d) { r_hertz_ = d; }
310 const double & c1() const { return c1_; }
311 void c1(const double &d) { c1_ = d; }
312
313 bool hasDamping() const { return dpProps_ ? true : false; }
314 double dp_nratio() const { return (hasDamping() ? (dpProps_->dp_nratio_) : 0.0); }
315 void dp_nratio(const double &d) { if (!hasDamping()) return; dpProps_->dp_nratio_ = d; }
316 double dp_sratio() const { return hasDamping() ? dpProps_->dp_sratio_ : 0.0; }
317 void dp_sratio(const double &d) { if (!hasDamping()) return; dpProps_->dp_sratio_ = d; }
318 int dp_mode() const { return hasDamping() ? dpProps_->dp_mode_ : -1; }
319 void dp_mode(int i) { if (!hasDamping()) return; dpProps_->dp_mode_ = i; }
320 DVect dp_F() const { return hasDamping() ? dpProps_->dp_F_ : DVect(0.0); }
321 void dp_F(const DVect &f) { if (!hasDamping()) return; dpProps_->dp_F_ = f; }
322
323 bool hasEnergies() const { return energies_ ? true : false; }
324 double estrain() const { return hasEnergies() ? energies_->estrain_ : 0.0; }
325 void estrain(const double &d) { if (!hasEnergies()) return; energies_->estrain_ = d; }
326 double errstrain() const { return hasEnergies() ? energies_->errstrain_ : 0.0; }
327 void errstrain(const double &d) { if (!hasEnergies()) return; energies_->errstrain_ = d; }
328 double eslip() const { return hasEnergies() ? energies_->eslip_ : 0.0; }
329 void eslip(const double &d) { if (!hasEnergies()) return; energies_->eslip_ = d; }
330 double errslip() const { return hasEnergies() ? energies_->errslip_ : 0.0; }
331 void errslip(const double &d) { if (!hasEnergies()) return; energies_->errslip_ = d; }
332 double edashpot() const { return hasEnergies() ? energies_->edashpot_ : 0.0; }
333 void edashpot(const double &d) { if (!hasEnergies()) return; energies_->edashpot_ = d; }
334
335 uint32 inheritanceField() const { return inheritanceField_; }
336 void inheritanceField(uint32 i) { inheritanceField_ = i; }
337
338 const DVect2 & effectiveTranslationalStiffness() const { return effectiveTranslationalStiffness_; }
339 void effectiveTranslationalStiffness(const DVect2 &v) { effectiveTranslationalStiffness_ = v; }
340 const DAVect & effectiveRotationalStiffness() const { return effectiveRotationalStiffness_; }
341 void effectiveRotationalStiffness(const DAVect &v) { effectiveRotationalStiffness_ = v; }
342
343 // Rolling resistance methods
344 const double & res_fric() const { return res_fric_; }
345 void res_fric(const double &d) { res_fric_ = d; }
346 const DAVect & res_M() const { return res_M_; }
347 void res_M(const DAVect &f) { res_M_ = f; }
348 bool res_S() const { return res_S_; }
349 void res_S(bool b) { res_S_ = b; }
350 const double & kr() const { return kr_; }
351 void kr(const double &d) { kr_ = d; }
352 const double & fr() const { return fr_; }
353 void fr(const double &d) { fr_ = d; }
354 const double & rbar_square() const { return rbar_square_; }
355 void rbar_square(const double &d) { rbar_square_ = d; }
356 //
357
358 private:
359 // Index - used internally by PFC. Should be set to -1 in the cpp file.
360 static int index_;
361
362 // Structure to store the energies.
363 struct Energies {
364 Energies() : estrain_(0.0), errstrain_(0.0), eslip_(0.0), errslip_(0.0), edashpot_(0.0) {}
365 double estrain_; // elastic energy stored in linear group
366 double errstrain_; // elastic energy stored in rolling resistance group
367 double eslip_; // work dissipated by friction
368 double errslip_; // work dissipated by rolling resistance friction
369 double edashpot_; // work dissipated by dashpots
370 };
371
372 // Structure to store dashpot quantities.
373 struct dpProps {
374 dpProps() : dp_nratio_(0.0), dp_sratio_(0.0), dp_mode_(0), dp_F_(DVect(0.0)) {}
375 double dp_nratio_; // normal viscous critical damping ratio
376 double dp_sratio_; // shear viscous critical damping ratio
377 int dp_mode_; // for viscous mode (0-1) 0 = no limit or cutoff; 1 = shear cut-off when sliding
378 DVect dp_F_; // Force in the dashpots
379 };
380
381 bool updateEndStiffCoef(const IContactMechanical *con);
382 bool updateEndFric(const IContactMechanical *con);
383 bool updateEndResFric(const IContactMechanical *con);
384 bool updateEndSurfAdh(const IContactMechanical *con);
385 void updateStiffCoef(const IContactMechanical *con);
386 void updateEffectiveStiffness(ContactModelMechanicalState *state);
387 void setDampCoefficients(const double &mass, double *kn_tangent, double *ks_tangent, double *vcn, double *vcs);
388
389 bool SetJKRTable(ContactModelMechanicalState *state);
390 DVect2 InterpolateJKRTable_Method1(double overlap_ratio);
391 DVect2 InterpolateJKRTable_Method2(double overlap_ratio, double trans_n);
392 DVect2 JKR_Analytical(double overlap);
393
394 static const uint32 shearMask = 0x00000002; // Base 1!
395 static const uint32 poissMask = 0x00000004;
396 static const uint32 fricMask = 0x00000008;
397 static const uint32 resFricMask = 0x00004000;
398 static const uint32 surfAdhMask = 0x00004000;
399
400 // Contact model inheritance fields.
401 uint32 inheritanceField_ = shearMask | poissMask | fricMask | resFricMask | surfAdhMask;
402
403 // Effective translational stiffness.
404 DVect2 effectiveTranslationalStiffness_ = DVect2(0);
405 DAVect effectiveRotationalStiffness_ = DAVect(0); // (Twisting,Bending,Bending) Rotational stiffness (twisting always 0)
406
407 // JKR model properties
408 double shear_ = 0.0; // Shear modulus
409 double poiss_ = 0.0; // Poisson's ratio
410 double fric_ = 0.0; // Coulomb friction coefficient
411 DVect jkr_F_ = DVect(0); // Force carried in the eepa model
412 bool jkr_S_ = false; // The current slip state
413 double rgap_ = 0.0; // Reference gap
414 dpProps * dpProps_ = nullptr; // The viscous properties
415 double kn_ = 0.0; // normal stiffness
416 double ks_ = 0.0; // shear stiffness
417 double ks_fac_ = 1.0; // shear stiffness scaling factor
418 double surf_adh_ = 0.0; // surface adhesion/cohesion energy (energy/area = J/m^2)
419 int active_mode_ = 1; // mode=0: no negtaive overlap is allowed - contact activated and deactivated when physical contact is made (Simplified SJKR-A model)
420 // mode=1: (default) negative overlap is allowed - contact activated on physical contact, but negative overlap (up to tear-off distance) allowed when unloading (full JKR model)
421 double distance_active_ = 0.0;// active distance - initially set to zero to establish contact, then after first time active it is set to the tear_off_ distance to allow for a seperation distance while under adhesion force
422 double a0_ = 0.0; // contact patch radius where the JKR force is equal to zero
423 double pull_off_f_ = 0.0; // the pull-off force which is the maximum tensile (adhesive) force
424 double tear_off_d_ = 0.0; // tear-off distance where the the adhesion snaps to zero under te
425 double r_hertz_ = 0.0; // effective contact radius: r_hertz_ = R1xR2/(R1+R2)
426 double c1_ = 0.0; // constant used in the analytical solution to the JKR contact patch radius
427
428 // rolling resistance properties
429 double res_fric_ = 0.0; // rolling friction coefficient
430 DAVect res_M_ = DAVect(0); // moment (bending only)
431 bool res_S_ = false; // The current rolling resistance slip state
432 double kr_ = 0.0; // bending rotational stiffness (read-only, calculated internaly)
433 double fr_ = 0.0; // rolling friction coefficient (rbar*res_fric_) (calculated internaly, not a property)
434 double rbar_square_ = 0.0; // used to update the rolling stiffness
435
436 Energies * energies_ = nullptr; // The energies
437 };
438}
439// EoF
contactmodeljkr.cpp
1// contactmodeljkr.cpp
2#include "contactmodeljkr.h"
3
4#include "module/interface/icontactmechanical.h"
5#include "module/interface/icontact.h"
6#include "module/interface/ipiece.h"
7#include "module/interface/ifishcalllist.h"
8
9#include "../version.txt"
10
11#include "utility/src/tptr.h"
12
13#include "kernel/interface/iprogram.h"
14#include "fish/src/parameter.h"
15
16#ifdef JKR_LIB
17int __stdcall DllMain(void *, unsigned, void *) {
18 return 1;
19}
20
21extern "C" EXPORT_TAG const char *getName() {
22#if DIM==3
23 return "contactmodelmechanical3dJKR";
24#else
25 return "contactmodelmechanical2dJKR";
26#endif
27}
28
29extern "C" EXPORT_TAG unsigned getMajorVersion() {
30 return MAJOR_VERSION;
31}
32
33extern "C" EXPORT_TAG unsigned getMinorVersion() {
34 return MINOR_VERSION;
35}
36
37extern "C" EXPORT_TAG void *createInstance() {
38 cmodelsxd::ContactModelJKR *m = new cmodelsxd::ContactModelJKR();
39 return (void *)m;
40}
41#endif
42
43namespace cmodelsxd {
44 using namespace itasca;
45
46 int ContactModelJKR::index_ = -1;
47 uint32 ContactModelJKR::getMinorVersion() const { return MINOR_VERSION; }
48
49 ContactModelJKR::ContactModelJKR() {
50 }
51
52 ContactModelJKR::ContactModelJKR(const ContactModelJKR &m) noexcept {
53 this->copy(&m);
54 }
55
56 const ContactModelJKR & ContactModelJKR::operator=(const ContactModelJKR &m) {
57 this->copy(&m);
58 return *this;
59 }
60
61 void ContactModelJKR::addToStorage(poly::vector<ContactModelMechanical> *s,int ww) {
62 s->addToStorage<ContactModelJKR>(*this,ww);
63 }
64
65 ContactModelJKR::~ContactModelJKR() {
66 // Make sure to clean up after yourself!
67 if (dpProps_)
68 delete dpProps_;
69 if (energies_)
70 delete energies_;
71 }
72
73 void ContactModelJKR::archive(ArchiveStream &stream) {
74 // The stream allows one to archive the values of the contact model
75 // so that it can be saved and restored. The minor version can be
76 // used here to allow for incremental changes to the contact model too.
77 stream & shear_;
78 stream & poiss_;
79 stream & fric_;
80 stream & jkr_F_;
81 stream & jkr_S_;
82 stream & rgap_;
83 stream & res_fric_;
84 stream & res_M_;
85 stream & res_S_;
86 stream & kr_;
87 stream & fr_;
88 stream & rbar_square_;
89 stream & kn_;
90 stream & ks_;
91 stream & ks_fac_;
92 stream & surf_adh_;
93 stream & active_mode_;
94 stream & distance_active_;
95 stream & a0_;
96 stream & pull_off_f_;
97 stream & tear_off_d_;
98 stream & r_hertz_;
99 stream & c1_;
100
101 if (stream.getArchiveState() == ArchiveStream::Save) {
102 bool b = false;
103 if (dpProps_) {
104 b = true;
105 stream & b;
106 stream & dpProps_->dp_nratio_;
107 stream & dpProps_->dp_sratio_;
108 stream & dpProps_->dp_mode_;
109 stream & dpProps_->dp_F_;
110 }
111 else
112 stream & b;
113
114 b = false;
115 if (energies_) {
116 b = true;
117 stream & b;
118 stream & energies_->estrain_;
119 stream & energies_->errstrain_;
120 stream & energies_->eslip_;
121 stream & energies_->errslip_;
122 stream & energies_->edashpot_;
123 }
124 else
125 stream & b;
126 }
127 else {
128 bool b(false);
129 stream & b;
130 if (b) {
131 if (!dpProps_)
132 dpProps_ = NEWC(dpProps());
133 stream & dpProps_->dp_nratio_;
134 stream & dpProps_->dp_sratio_;
135 stream & dpProps_->dp_mode_;
136 stream & dpProps_->dp_F_;
137 }
138 stream & b;
139 if (b) {
140 if (!energies_)
141 energies_ = NEWC(Energies());
142 stream & energies_->estrain_;
143 stream & energies_->errstrain_;
144 stream & energies_->eslip_;
145 stream & energies_->errslip_;
146 stream & energies_->edashpot_;
147 }
148 }
149
150 stream & inheritanceField_;
151 stream & effectiveTranslationalStiffness_;
152 stream & effectiveRotationalStiffness_;
153 }
154
155 void ContactModelJKR::copy(const ContactModel *cm) {
156 // Copy all of the contact model properties. Used in the CMAT
157 // when a new contact is created.
158 ContactModelMechanical::copy(cm);
159 const ContactModelJKR *in = dynamic_cast<const ContactModelJKR*>(cm);
160 if (!in) throw std::runtime_error("Internal error: contact model dynamic cast failed.");
161 shear(in->shear());
162 poiss(in->poiss());
163 fric(in->fric());
164 jkr_F(in->jkr_F());
165 jkr_S(in->jkr_S());
166 rgap(in->rgap());
167 res_fric(in->res_fric());
168 res_M(in->res_M());
169 res_S(in->res_S());
170 kr(in->kr());
171 fr(in->fr());
172 rbar_square(in->rbar_square());
173 kn(in->kn());
174 ks(in->ks());
175 ks_fac(in->ks_fac());
176 surf_adh(in->surf_adh());
177 active_mode(in->active_mode());
178 distance_active(in->distance_active());
179 a0(in->a0());
180 pull_off(in->pull_off());
181 tear_off(in->tear_off());
182 r_hertz(in->r_hertz());
183 c1(in->c1());
184
185 if (in->hasDamping()) {
186 if (!dpProps_)
187 dpProps_ = NEWC(dpProps());
188 dp_nratio(in->dp_nratio());
189 dp_sratio(in->dp_sratio());
190 dp_mode(in->dp_mode());
191 dp_F(in->dp_F());
192 }
193 if (in->hasEnergies()) {
194 if (!energies_)
195 energies_ = NEWC(Energies());
196 estrain(in->estrain());
197 errstrain(in->errstrain());
198 eslip(in->eslip());
199 errslip(in->errslip());
200 edashpot(in->edashpot());
201 }
202 inheritanceField(in->inheritanceField());
203 effectiveTranslationalStiffness(in->effectiveTranslationalStiffness());
204 effectiveRotationalStiffness(in->effectiveRotationalStiffness());
205 }
206
207 QVariant ContactModelJKR::getProperty(uint32 i, const IContact *) const {
208 // Return the property. The IContact pointer is provided so that
209 // more complicated properties, depending on contact characteristics,
210 // can be calcualted. The IContact pointer may be a nullptr!
211 QVariant var;
212 switch (i) {
213 case kwShear: return shear_;
214 case kwPoiss: return poiss_;
215 case kwFric: return fric_;
216 case kwjkrF: var.setValue(jkr_F_); return var;
217 case kwjkrS: return jkr_S_;
218 case kwDpNRatio: return dpProps_ ? dpProps_->dp_nratio_ : 0;
219 case kwDpSRatio: return dpProps_ ? dpProps_->dp_sratio_ : 0;
220 case kwDpMode: return dpProps_ ? dpProps_->dp_mode_ : 0;
221 case kwDpF: {
222 dpProps_ ? var.setValue(dpProps_->dp_F_) : var.setValue(DVect(0.0));
223 return var;
224 }
225 case kwResFric: return res_fric_;
226 case kwResMoment: var.setValue(res_M_); return var;
227 case kwResS: return res_S_;
228
229 case kwKsFac: return ks_fac_;
230 case kwSurfAdh: return surf_adh_;
231 case kwActiveMode: return active_mode_;
232 case kwA0: return a0_;
233 case kwPullOff: return pull_off_f_;
234 case kwTearOff: return tear_off_d_;
235 }
236 assert(0);
237 return QVariant();
238 }
239
240 bool ContactModelJKR::getPropertyGlobal(uint32 i) const {
241 // Returns whether or not a property is held in the global axis system (TRUE)
242 // or the local system (FALSE). Used by the plotting logic.
243 switch (i) {
244 case kwjkrF:
245 case kwDpF:
246 case kwResMoment:
247 return false;
248 }
249 return true;
250 }
251
252 bool ContactModelJKR::setProperty(uint32 i, const QVariant &v, IContact *) {
253 // Set a contact model property. Return value indicates that the timestep
254 // should be recalculated.
255 dpProps dp;
256 switch (i) {
257 case kwShear: {
258 if (!v.canConvert<double>())
259 throw Exception("shear must be a double.");
260 double val(v.toDouble());
261 if (val <= 0.0)
262 throw Exception("zero or negative shear not allowed in the 'jkr' model.");
263 shear_ = val;
264 return true;
265 }
266 case kwPoiss: {
267 if (!v.canConvert<double>())
268 throw Exception("poiss must be a double.");
269 double val(v.toDouble());
270 if (val < 0.0 || val > 0.5)
271 throw Exception("poiss must be in the range [0, 0.5].");
272 poiss_ = val;
273 return true;
274 }
275 case kwFric: {
276 if (!v.canConvert<double>())
277 throw Exception("fric must be a double.");
278 double val(v.toDouble());
279 if (val < 0.0)
280 throw Exception("negative fric not allowed.");
281 fric_ = val;
282 return false;
283 }
284 case kwjkrF: {
285 if (!v.canConvert<DVect>())
286 throw Exception("jkr_force must be a vector.");
287 DVect val(v.value<DVect>());
288 jkr_F_ = val;
289 return false;
290 }
291 case kwDpNRatio: {
292 if (!v.canConvert<double>())
293 throw Exception("dp_nratio must be a double.");
294 double val(v.toDouble());
295 if (val < 0.0)
296 throw Exception("negative dp_nratio not allowed.");
297 if (val == 0.0 && !dpProps_)
298 return false;
299 if (!dpProps_)
300 dpProps_ = NEWC(dpProps());
301 dpProps_->dp_nratio_ = val;
302 return true;
303 }
304 case kwDpSRatio: {
305 if (!v.canConvert<double>())
306 throw Exception("dp_sratio must be a double.");
307 double val(v.toDouble());
308 if (val < 0.0)
309 throw Exception("negative dp_sratio not allowed.");
310 if (val == 0.0 && !dpProps_)
311 return false;
312 if (!dpProps_)
313 dpProps_ = NEWC(dpProps());
314 dpProps_->dp_sratio_ = val;
315 return true;
316 }
317 case kwDpMode: {
318 if (!v.canConvert<int>())
319 throw Exception("the viscous mode dp_mode must be 0 or 1");
320 int val(v.toInt());
321 if (val == 0 && !dpProps_)
322 return false;
323 if (val < 0 || val > 1)
324 throw Exception("the viscous mode dp_mode must be 0 or 1.");
325 if (!dpProps_)
326 dpProps_ = NEWC(dpProps());
327 dpProps_->dp_mode_ = val;
328 return false;
329 }
330 case kwDpF: {
331 if (!v.canConvert<DVect>())
332 throw Exception("dp_force must be a vector.");
333 DVect val(v.value<DVect>());
334 if (val.fsum() == 0.0 && !dpProps_)
335 return false;
336 if (!dpProps_)
337 dpProps_ = NEWC(dpProps());
338 dpProps_->dp_F_ = val;
339 return false;
340 }
341 case kwResFric: {
342 if (!v.canConvert<double>())
343 throw Exception("res_fric must be a double.");
344 double val(v.toDouble());
345 if (val < 0.0)
346 throw Exception("negative res_fric not allowed.");
347 res_fric_ = val;
348 return true;
349 }
350 case kwResMoment: {
351 DAVect val(0.0);
352#ifdef TWOD
353 if (!v.canConvert<DAVect>() && !v.canConvert<double>())
354 throw Exception("res_moment must be an angular vector.");
355 if (v.canConvert<DAVect>())
356 val = DAVect(v.value<DAVect>());
357 else
358 val = DAVect(v.toDouble());
359#else
360 if (!v.canConvert<DAVect>() && !v.canConvert<DVect>())
361 throw Exception("res_moment must be an angular vector.");
362 if (v.canConvert<DAVect>())
363 val = DAVect(v.value<DAVect>());
364 else
365 val = DAVect(v.value<DVect>());
366#endif
367 res_M_ = val;
368 return false;
369 }
370 case kwKsFac: {
371 if (!v.canConvert<double>())
372 throw Exception("shear stiffness factor must be a double.");
373 double val(v.toDouble());
374 if (val <= 0.0)
375 throw Exception("negative or zero ks_fac not allowed.");
376 ks_fac_ = val;
377 return true;
378 }
379 case kwSurfAdh: {
380 if (!v.canConvert<double>())
381 throw Exception("surface adhesion must be a double.");
382 double val(v.toDouble());
383 if (val <= 0.0)
384 throw Exception("negative or zero surf_adh not allowed.");
385 surf_adh_ = val;
386 return true;
387 }
388 case kwActiveMode: {
389 if (!v.canConvert<int>())
390 throw Exception("the active mode must be an integer: 0 or 1.");
391 int val(v.toInt());
392 if (val < 0 || val > 1)
393 throw Exception("active_mode must be 0 or 1.");
394 active_mode_ = val;
395 return true;
396 }
397 }//switch
398 return false;
399 }
400
401 bool ContactModelJKR::getPropertyReadOnly(uint32 i) const {
402 // Returns TRUE if a property is read only or FALSE otherwise.
403 switch (i) {
404 case kwDpF:
405 case kwjkrS:
406 case kwResS:
407 case kwA0:
408 case kwPullOff:
409 case kwTearOff:
410 return true;
411 default:
412 break;
413 }
414 return false;
415 }
416
417 bool ContactModelJKR::supportsInheritance(uint32 i) const {
418 // Returns TRUE if a property supports inheritance or FALSE otherwise.
419 switch (i) {
420 case kwShear:
421 case kwPoiss:
422 case kwFric:
423 case kwResFric:
424 case kwSurfAdh:
425 return true;
426 default:
427 break;
428 }
429 return false;
430 }
431
432 double ContactModelJKR::getEnergy(uint32 i) const {
433 // Return an energy value.
434 double ret(0.0);
435 if (!energies_)
436 return ret;
437 switch (i) {
438 case kwEStrain: return energies_->estrain_;
439 case kwERRStrain: return energies_->errstrain_;
440 case kwESlip: return energies_->eslip_;
441 case kwERRSlip: return energies_->errslip_;
442 case kwEDashpot: return energies_->edashpot_;
443 }
444 assert(0);
445 return ret;
446 }
447
448 bool ContactModelJKR::getEnergyAccumulate(uint32 i) const {
449 // Returns TRUE if the corresponding energy is accumulated or FALSE otherwise.
450 switch (i) {
451 case kwEStrain: return true;
452 case kwERRStrain: return false;
453 case kwESlip: return true;
454 case kwERRSlip: return true;
455 case kwEDashpot: return true;
456 }
457 assert(0);
458 return false;
459 }
460
461 void ContactModelJKR::setEnergy(uint32 i, const double &d) {
462 // Set an energy value.
463 if (!energies_) return;
464 switch (i) {
465 case kwEStrain: energies_->estrain_ = d; return;
466 case kwERRStrain: energies_->errstrain_ = d; return;
467 case kwESlip: energies_->eslip_ = d; return;
468 case kwERRSlip: energies_->errslip_ = d; return;
469 case kwEDashpot: energies_->edashpot_ = d; return;
470 }
471 assert(0);
472 return;
473 }
474
475 bool ContactModelJKR::validate(ContactModelMechanicalState *state, const double &) {
476 // Validate the / Prepare for entry into ForceDispLaw. The validate function is called when:
477 // (1) the contact is created, (2) a property of the contact that returns a true via
478 // the setProperty method has been modified and (3) when a set of cycles is executed
479 // via the {cycle N} command.
480 // Return value indicates contact activity (TRUE: active, FALSE: inactive).
481 assert(state);
482 const IContactMechanical *c = state->getMechanicalContact();
483 assert(c);
484 //
485 if (state->trackEnergy_)
486 activateEnergy();
487 //
488 if ((inheritanceField_ & shearMask) || (inheritanceField_ & poissMask))
489 updateEndStiffCoef(c);
490 if (inheritanceField_ & fricMask)
491 updateEndFric(c);
492 if (inheritanceField_ & resFricMask)
493 updateEndResFric(c);
494 if (inheritanceField_ & surfAdhMask)
495 updateEndSurfAdh(c);
496
497 updateStiffCoef(c); // calculate the stiffness values based on the material properties - specified directly or via inheritance
498 updateEffectiveStiffness(state); // effective stiffness for translation and rotation used in the time step estimation
499 //
500 return checkActivity(state->gap_);
501 }
502
503 void ContactModelJKR::updateStiffCoef(const IContactMechanical *con) {
504 //
505 double c12 = con->getEnd1Curvature().y();
506 double c22 = con->getEnd2Curvature().y();
507 r_hertz_ = c12 + c22;
508 if (r_hertz_ == 0.0)
509 throw Exception("jkr contact model undefined for 2 non-curved surfaces");
510 //
511 r_hertz_ = 1.0 / r_hertz_; // r_hertz = R1*R2/(R1 + R2)
512 double young_eff = shear_ / (1.0 - poiss_); // effective contact Young's modulus assuming the two contact pieces are identical in properties
513 double shear_eff = shear_ / (4.0 - 2.0*poiss_); // effective shear modulus for contact assuming the two contact pieces are identical in properties
514 kn_ = 2.0 * young_eff; // normal stiffness - needs to be multiplied by contact patch radius to obtain the tangent normal stiffness - (Marshall, 2009)
515 ks_ = ks_fac_ * 8.0*shear_eff; // shear stiffness - needs to be multiplied by contact patch radius to obtain the tangent shear stiffness - (Marshall, 2009)
516 //
517 a0_ = 9.0*M_PI*surf_adh_*r_hertz_*r_hertz_ / young_eff; // contact patch radius where the JKR force is equal to zero
518 a0_ = pow(a0_, 1.0 / 3.0);
519 //
520 pull_off_f_ = 3.0*M_PI*surf_adh_*r_hertz_; // pull-off force, the maximum tensile/adhesion force in JKR model (not the force at tear-off when the contact snaps)
521 tear_off_d_ = a0_* a0_ / (2.0*pow(6.0, (1.0 / 3.0))*r_hertz_); // tear-off distance where the the adhesion snaps to zero under tension
522 //
523 c1_ = -4.0*M_PI*surf_adh_ * r_hertz_*r_hertz_ / young_eff; // constant used in the analytical solution for the patch radius
524 //
525 // rolling resistance
526 kr_ = 0.0;
527 fr_ = 0.0;
528 if (res_fric_ > 0.0) {
529 rbar_square_ = r_hertz_ * r_hertz_; // store this value with contact - used again when 'ks_tangent' is updated during loading-unloading to calculate the rolling stiffness
530 fr_ = res_fric_ * r_hertz_; // store this value with contact - used in force-displacement calculation
531 kr_ = ks_ * rbar_square_; // based on 'ks_' for now, this is updated and based on the shear tangent stiffness during loading-unloading
532 }
533 }
534
535 bool ContactModelJKR::endPropertyUpdated(const QString &name, const IContactMechanical *c) {
536 // The endPropertyUpdated method is called whenever a surface property (with a name
537 // that matches an inheritable contact model property name) of one of the contacting
538 // pieces is modified. This allows the contact model to update its associated
539 // properties. The return value denotes whether or not the update has affected
540 // the time step computation (by having modified the translational or rotational
541 // tangent stiffnesses). If true is returned, then the time step will be recomputed.
542 assert(c);
543 QStringList availableProperties = getProperties().simplified().replace(" ", "").split(",", Qt::SkipEmptyParts);
544 QRegExp rx(name, Qt::CaseInsensitive);
545 int idx = availableProperties.indexOf(rx) + 1;
546 bool ret = false;
547
548 if (idx <= 0)
549 return ret;
550
551 switch (idx) {
552 case kwShear: { //Shear
553 if (inheritanceField_ & shearMask)
554 ret = true; // return 'true' to ensure 'validate()' is called to affect the change in Shear Modulus through 'updateEndStiffCoef()'
555 break;
556 }
557 case kwPoiss: { //Poisson's
558 if (inheritanceField_ & poissMask)
559 ret =true; // return 'true' to ensure 'validate()' is called to affect the change in Poisson's ratio through 'updateEndStiffCoef()'
560 break;
561 }
562 case kwFric: { //fric
563 if (inheritanceField_ & fricMask)
564 updateEndFric(c); // friction does not influence any of the other parameters or time step size, so directly update the contact model friction
565 break;
566 }
567 case kwResFric: { //rr_fric
568 if (inheritanceField_ & resFricMask)
569 ret = true; // return 'true' to ensure 'validate()' is called to affect the change in rolling friction through 'updateEndResFric()' and 'fr_'
570 break;
571 }
572 case kwSurfAdh: { //surf_adh
573 if (inheritanceField_ & surfAdhMask)
574 ret = true; // return 'true' to ensure 'validate()' is called to affect the change in surface adhesion through 'updateEndSurfAdh()'
575 break;
576 }
577 //
578 }
579 return ret;
580 }
581
582 static const QString gstr("jkr_shear");
583 static const QString nustr("jkr_poiss");
584 bool ContactModelJKR::updateEndStiffCoef(const IContactMechanical *con) {
585 assert(con);
586 double g1 = shear_;
587 double g2 = shear_;
588 double nu1 = poiss_;
589 double nu2 = poiss_;
590 QVariant vg1 = con->getEnd1()->getProperty(gstr);
591 QVariant vg2 = con->getEnd2()->getProperty(gstr);
592 QVariant vnu1 = con->getEnd1()->getProperty(nustr);
593 QVariant vnu2 = con->getEnd2()->getProperty(nustr);
594 if (vg1.isValid() && vg2.isValid()) {
595 g1 = vg1.toDouble();
596 g2 = vg2.toDouble();
597 if (g1 <= 0.0 || g2 <= 0.0)
598 throw Exception("Negative or zero shear modulus not allowed in jkr contact model");
599 }
600 if (vnu1.isValid() && vnu2.isValid()) {
601 nu1 = vnu1.toDouble();
602 nu2 = vnu2.toDouble();
603 if (nu1 < 0.0 || nu1 > 0.5 || nu2 < 0.0 || nu2 > 0.5)
604 throw Exception("Poisson ratio should be in range [0,0.5] in jkr contact model");
605 }
606 if (g1*g2 == 0.0) return false;
607 double es = 1.0 / ((1.0 - nu1) / (2.0*g1) + (1.0 - nu2) / (2.0*g2));
608 double gs = 1.0 / ((2.0 - nu1) / g1 + (2.0 - nu2) / g2);
609 poiss_ = (4.0*gs - es) / (2.0*gs - es);
610 shear_ = 2.0*gs*(2 - poiss_);
611 if (shear_ <= 0.0)
612 throw Exception("Negative or zero shear modulus not allowed in jkr contact model");
613 if (poiss_ < 0.0 || poiss_ > 0.5)
614 throw Exception("Poisson ratio should be in range [0,0.5] in jkr contact model");
615 return true;
616 }
617
618 static const QString fricstr("fric");
619 bool ContactModelJKR::updateEndFric(const IContactMechanical *con) {
620 assert(con);
621 QVariant v1 = con->getEnd1()->getProperty(fricstr);
622 QVariant v2 = con->getEnd2()->getProperty(fricstr);
623 if (!v1.isValid() || !v2.isValid())
624 return false;
625 double fric1 = std::max(0.0, v1.toDouble());
626 double fric2 = std::max(0.0, v2.toDouble());
627 double val = fric_;
628 fric_ = std::min(fric1, fric2);
629 if (fric_ < 0.0)
630 throw Exception("Negative friction value not allowed in jkr contact model");
631 return ((fric_ != val));
632 }
633
634 static const QString rfricstr("rr_fric");
635 bool ContactModelJKR::updateEndResFric(const IContactMechanical *con) {
636 assert(con);
637 QVariant v1 = con->getEnd1()->getProperty(rfricstr);
638 QVariant v2 = con->getEnd2()->getProperty(rfricstr);
639 if (!v1.isValid() || !v2.isValid())
640 return false;
641 double rfric1 = std::max(0.0, v1.toDouble());
642 double rfric2 = std::max(0.0, v2.toDouble());
643 double val = res_fric_;
644 res_fric_ = std::min(rfric1, rfric2);
645 if (res_fric_ < 0.0)
646 throw Exception("Negative rolling friction value not allowed in jkr contact model");
647 return ((res_fric_ != val));
648 }
649
650 static const QString surfadhstr("surf_adh");
651 bool ContactModelJKR::updateEndSurfAdh(const IContactMechanical *con) {
652 assert(con);
653 QVariant v1 = con->getEnd1()->getProperty(surfadhstr);
654 QVariant v2 = con->getEnd2()->getProperty(surfadhstr);
655 if (!v1.isValid() || !v2.isValid())
656 return false;
657 double surfadh1 = std::max(0.0, v1.toDouble());
658 double surfadh2 = std::max(0.0, v2.toDouble());
659 if (surfadh1 <= 0.0 || surfadh2 <= 0.0)
660 throw Exception("Negative or zero surface adhesion not allowed in 'jkr' contact model");
661 double val = surf_adh_;
662 surf_adh_ = 0.5*(surfadh1 + surfadh2); // new surface adhesion enery
663 return ((surf_adh_ != val));
664 }
665
666 void ContactModelJKR::updateEffectiveStiffness(ContactModelMechanicalState *state) {
667
668 double overlap = rgap_ - state->gap_;
669 if (overlap <= -distance_active_) { // if contact was just created but not yet activated, distance_active_ == 0.
670 // The assumption below is based on the Hill contact model in PFC
671 // 0.01% of Diameter = 0.0001*D
672 // For monodisperse system, r_hertz_ = 0.5*R where R is the particle radius
673 // r_hertz_ = 0.5*(0.5*D) = 0.25*D where D is the particle diameter
674 // D = 4*r_hertz_
675 // 0.01%*D = 0.0001*D = 0.0001*4*r_hertz_ = 0.0004*r_hertz_
676 overlap = 0.0004*r_hertz_;
677 }
678 //
679 DVect2 force_radius = JKR_Analytical(overlap); // analytical patch radius and force for this (assumed) overlap
680 double patch_radius = force_radius.dof(1); // contact patch radius
681 // Assume Hertz tangent stiffness in the normal direction - the tangent stiffness for JKR is different, but no closed-form exists - good enough of an assumption
682 double kn_tangent = kn_ * patch_radius; // tangent stiffness in normal direction
683 double ks_tangent = ks_ * patch_radius; // tangent stiffness in the shear direction - tis is not an assumption, but the correct solution for JKR
684 effectiveTranslationalStiffness_ = DVect2(kn_tangent, ks_tangent); // set using the tangent stiffness
685 if (res_fric_ > 0.0) { // rolling
686 kr_ = ks_tangent * rbar_square_; // based on the tangent shear stiffness (Wensrich and Katterfeld, 2012)
687 effectiveRotationalStiffness_ = DAVect(kr_); // effective rotational stiffness (bending only)
688#if DIM==3
689 effectiveRotationalStiffness_.rx() = 0.0;
690#endif
691 }
692 // correction if viscous damping active
693 if (dpProps_) {
694 DVect2 correct(1.0);
695 if (dpProps_->dp_nratio_)
696 correct.rx() = sqrt(1.0 + dpProps_->dp_nratio_*dpProps_->dp_nratio_) - dpProps_->dp_nratio_;
697 if (dpProps_->dp_sratio_)
698 correct.ry() = sqrt(1.0 + dpProps_->dp_sratio_*dpProps_->dp_sratio_) - dpProps_->dp_sratio_;
699 effectiveTranslationalStiffness_ /= (correct*correct);
700 }
701 }
702 //
703 // --------------------------------------------------------------------------------------------------------------------------------------------------------
704 //
705 DVect2 ContactModelJKR::JKR_Analytical(double overlap) { // analytical solution according to Parteli et al. 2014
706 DVect2 ret(0.0);
707 // reduces to Hertz model
708 if (surf_adh_ == 0.0) {
709 double patch_radius = std::sqrt(r_hertz_*overlap);
710 ret.rdof(0) = (2.0 / 3.0)*kn_*patch_radius*patch_radius*patch_radius / r_hertz_;
711 ret.rdof(1) = patch_radius;
712 return ret;
713 }
714 // otherwise jkr is active ...
715 double rad_over = r_hertz_ * overlap;
716 double c0 = rad_over * rad_over;
717 double c2 = -2.0*rad_over;
718 double P = -1.0 / 12.0*c2*c2 - c0;
719 double Q = -1.0 / 108.0*c2*c2*c2 + c0 * c2 / 3.0 - c1_*c1_ / 8.0;
720 double U = pow((-0.5*Q + sqrt(0.25*Q*Q + P*P*P / 27.0)), (1.0 / 3.0));
721 double s = 0.0;
722 if (P == 0.0) {
723 s = -5.0*c2 / 6.0 - pow(Q, (1.0 / 3.0));
724 }
725 else {
726 s = -5.0*c2 / 6.0 + U - P / (3.0*U);
727 }
728 double w = sqrt(c2 + 2.0*s);
729 double lambda = c1_ / (2.0*w);
730 double patch_radius = 0.5*(w + sqrt(w*w - 4.0*(c2 + s + lambda)));
731 // reduces to Hertz model due to very small values for surface adhesion
732 if (std::isnan(patch_radius)) {
733 patch_radius = std::sqrt(r_hertz_*overlap);
734 ret.rdof(0) = (2.0 / 3.0)*kn_*patch_radius*patch_radius*patch_radius / r_hertz_;
735 ret.rdof(1) = patch_radius;
736 return ret;
737 }
738 // force = 4*E*(a^3)/(3*R) - sqrt(16*pi*surf_adh*E*a^3);
739 // with E = kn/2 - where E is the effective contact Young's modulus - see updateStiffCoef(const IContactMechanical *con)
740 // the above reduces to the following for the JKR force ...
741 double patch_radius3 = patch_radius* patch_radius*patch_radius;
742 ret.rdof(0) = (2.0 / 3.0)*kn_*patch_radius3 / r_hertz_ - sqrt(8.0*M_PI*surf_adh_*kn_*patch_radius3); // set the total force
743 ret.rdof(1) = patch_radius; // set the patch contact radius
744 return ret;
745 }
746 //
747 // --------------------------------------------------------------------------------------------------------------------------------------------------------
748 //
749 bool ContactModelJKR::forceDisplacementLaw(ContactModelMechanicalState *state, const double ×tep) {
750 assert(state);
751 if (shear_ <= 0.0) {
752 throw Exception("'shear' must be specified using a value larger than zero in the 'jkr' model.");
753 }
754 if (surf_adh_ <= 0.0) {
755 throw Exception("'surf_adh' must be specified using a value larger than zero in the 'jkr' model. For zero surface adhesion, use the Hertz model!");
756 }
757 //
758 // Current overlap
759 double overlap = rgap_ - state->gap_; // state->gap is negative in sign when the particles physically overlap. If rgap == 0, then the overlap is positive when the two particles physically overlap.
760 // Relative translational increment
761 DVect trans = state->relativeTranslationalIncrement_;
762 //
763 if (active_mode_ == 1 && distance_active_ == 0.0) { // only when in 'full jkr' mode = 1, for Simplified JKR-A (SJKR-A), negative overlap is not allowed
764 distance_active_ = tear_off_d_; // contact has become active - set the activity distance to rgap_ + distance_active = rgap_ + tear_off to allow for negative overlap under adhesion force
765 }
766 //
767 // The contact was just activated from an inactive state
768 // Trigger the FISH callback if one is hooked up to the contact_activated event.
769 if (cmEvents_[fActivated] >= 0) {
770 // An FArray of QVariant is returned and these will be passed
771 // to the FISH function as an array of FISH symbols as the second
772 // argument to the FISH callback function.
773 auto c = state->getContact();
774 std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()) };
775 IFishCallList* fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
776 fi->setCMFishCallArguments(c, arg, cmEvents_[fActivated]);
777
778 }
779 //
780 // Angular dispacement increment.
781 DAVect ang = state->relativeAngularIncrement_;
782 //
783 DVect jkr_F_old = jkr_F_; // store for energy computations only
784 //
785 // NORMAL FORCE ==============================================================================================================
786 //
787 DVect2 force_radius = JKR_Analytical(overlap); // analytical force and patch radius
788 jkr_F_.rdof(0) = force_radius.dof(0); // current contact force in the normal direction
789 double patch_radius = force_radius.dof(1); // current contact patch radius
790 //
791 double kn_tangent = kn_ * patch_radius;
792 if (trans.dof(0))
793 kn_tangent = std::abs((jkr_F_old.dof(0) - jkr_F_.dof(0)) / trans.dof(0));// tangent stiffness in normal direction - used for timestep calculations and damping
794 //
795 // SHEAR FORCE ==============================================================================================================
796 //
797 double ks_tangent = ks_* patch_radius; // tangent stiffness in the shear direction
798 DVect sforce(0.0);
799 // dim holds the dimension (e.g., 2 for 2D and 3 for 3D)
800 // Loop over the shear components (note: the 0 component is the normal component) and calculate the shear force.
801 for (int i = 1; i < dim; ++i)
802 sforce.rdof(i) = jkr_F_.dof(i) - trans.dof(i) * ks_tangent; // shear force update
803 //
804 // The canFail flag corresponds to whether or not the contact can undergo non-linear
805 // force-displacement response. If the SOLVE ELASTIC command is given then the
806 // canFail state is set to FALSE. Otherwise it is always TRUE.
807 if (state->canFail_) {
808 // Resolve sliding. This is the normal force multiplied by the coefficient of friction.
809 double crit = fric_ * std::abs(jkr_F_.rdof(0) + 2.0*pull_off_f_);
810 // The is the magnitude of the shear force.
811 double sfmag = sforce.mag();
812 // Sliding occurs when the magnitude of the shear force is greater than the critical value.
813 if (sfmag > crit) {
814 // Lower the shear force to the critical value for sliding.
815 double rat = crit / sfmag;
816 sforce *= rat;
817 // Handle the slip_change event if one has been hooked up. Sliding has commenced.
818 if (!jkr_S_ && cmEvents_[fSlipChange] >= 0) {
819 auto c = state->getContact();
820 std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
821 fish::Parameter((qint64)0) };
822 IFishCallList* fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
823 fi->setCMFishCallArguments(c, arg, cmEvents_[fSlipChange]);
824 }
825 jkr_S_ = true;
826 }
827 else {
828 // Handle the slip_change event if one has been hooked up and
829 // the contact was previously sliding. Sliding has ceased.
830 if (jkr_S_) {
831 if (cmEvents_[fSlipChange] >= 0) {
832 auto c = state->getContact();
833 std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
834 fish::Parameter((qint64)1) };
835 IFishCallList* fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
836 fi->setCMFishCallArguments(c, arg, cmEvents_[fSlipChange]);
837 }
838 jkr_S_ = false;
839 }
840 }
841 }
842 //
843 // Set the shear components of the total force.
844 for (int i = 1; i < dim; ++i)
845 jkr_F_.rdof(i) = sforce.dof(i);
846 //
847 // ROLLING RESISTANCE ==============================================================================================================
848 //
849 DAVect res_M_old = res_M_; // used in energy calculation only
850 if (fr_ == 0.0) {
851 res_M_.fill(0.0);
852 kr_ = 0.0;
853 }
854 else {
855 DAVect angStiff(0.0);
856 DAVect MomentInc(0.0);
857 kr_ = ks_tangent * rbar_square_; // update rolling stiffness based on the tangent shear stiffness (Wensrich and Katterfeld, 2012)
858#if DIM==3
859 angStiff.rx() = 0.0;
860 angStiff.ry() = kr_;
861#endif
862 angStiff.rz() = kr_;
863 MomentInc = ang * angStiff * (-1.0);
864 res_M_ += MomentInc;
865 if (state->canFail_) {
866 // Account for bending strength
867 double rmax = std::abs(fr_*(jkr_F_.rdof(0) + 2.0*pull_off_f_)); // Using the same normal force as in the shear limit
868 double rmag = res_M_.mag();
869 if (rmag > rmax) {
870 double fac = rmax / rmag;
871 res_M_ *= fac;
872 res_S_ = true;
873 }
874 else {
875 res_S_ = false;
876 }
877 }
878 }
879 //
880 // STIFFNESS FOR TIME STEP UPDATES ============================================================================================
881 //
882 // set effective stiffness in the normal and shear directions for timestep calculation
883 effectiveTranslationalStiffness_ = DVect2(kn_tangent, ks_tangent);
884 // set the effective rotational stiffness (bending only)
885 effectiveRotationalStiffness_ = DAVect(kr_);
886#if DIM==3
887 effectiveRotationalStiffness_.rx() = 0.0;
888#endif
889 //
890 // DAMPING FORCE ==============================================================================================================
891 //
892 // Account for dashpot forces if the dashpot structure has been defined.
893 if (dpProps_) {
894 dpProps_->dp_F_.fill(0.0);
895 double vcn(0.0), vcs(0.0);
896 // Calculate the damping coefficients.
897 setDampCoefficients(state->inertialMass_, &kn_tangent, &ks_tangent, &vcn, &vcs); // this is based on the current tangent stiffness
898 // First damp the shear components
899 for (int i = 1; i < dim; ++i)
900 dpProps_->dp_F_.rdof(i) = trans.dof(i) * (-1.0* vcs) / timestep;
901 // Damp the normal component
902 dpProps_->dp_F_.rx() -= trans.x() * vcn / timestep;
903 //
904 if (jkr_S_ && dpProps_->dp_mode_ == 1) {
905 // Limit in shear if sliding.
906 double dfn = dpProps_->dp_F_.rx();
907 dpProps_->dp_F_.fill(0.0);
908 dpProps_->dp_F_.rx() = dfn;
909 }
910 // Correct effective translational stiffness
911 DVect2 correct(1.0);
912 if (dpProps_->dp_nratio_)
913 correct.rx() = sqrt(1.0 + dpProps_->dp_nratio_*dpProps_->dp_nratio_) - dpProps_->dp_nratio_;
914 if (dpProps_->dp_sratio_)
915 correct.ry() = sqrt(1.0 + dpProps_->dp_sratio_*dpProps_->dp_sratio_) - dpProps_->dp_sratio_;
916 effectiveTranslationalStiffness_ /= (correct*correct);
917 }
918
919 //Compute energies if energy tracking has been enabled.
920 if (state->trackEnergy_) {
921 assert(energies_);
922 // calculate the strain energy increment in the normal direction
923 energies_->estrain_ -= 0.5*(jkr_F_old.dof(0) + jkr_F_.dof(0))*trans.dof(0);// under loading, trans.x < 0, hence the negative sign
924 if (ks_) {
925 DVect u_s_elastic = trans; // set the elastic displacement increment equal to the total displacement increment
926 u_s_elastic.rx() = 0.0; // set the normal component to zero: u_s_elatic = [0, trans_shear_1, trans_shear_2]
927 DVect shearF = jkr_F_; // set the shear force equal to the total jkr force (including the normal component)
928 shearF.rx() = 0.0; // set the normal component to zero: shearF = [0, shear_force_1, shear_force_2]
929 jkr_F_old.rx() = 0.0; // set normal component of the previous force equal to zero
930 DVect avg_F_s = (shearF + jkr_F_old)*0.5; // average shear force vector
931 if (jkr_S_) { // if sliding, calculate the slip energy and accumulate it
932 DVect u_s_total = u_s_elastic; // total shear displacement increment
933 u_s_elastic = (shearF - jkr_F_old) / ks_tangent; // elastic shear displacement increment
934 energies_->eslip_ -= std::min(0.0, (avg_F_s | (u_s_total + u_s_elastic))); // where (u_s_total + u_s_elatic) is the slip displacment increment (due to the sign convention, the terms are added up)
935 }
936 energies_->estrain_ -= avg_F_s | u_s_elastic; // add the elastic component (if any - if previous force equals current force, the elastic incrment is zero)
937 }
938 // Add the rolling resistance energy contributions. // done incrementally since the stiffness kr_ changes with the tangent shear stiffness (non-linearly)
939 if (kr_) {
940 DAVect t_s_elastic = ang; // set the elastic rotation increment equal to the total angle increment
941 DAVect avg_M = (res_M_ + res_M_old)*0.5; // average moment from this time step and the previous
942 if (res_S_) { // if sliding, calculate the slip energy and accumulate it
943 t_s_elastic = (res_M_ - res_M_old) / kr_; // elastic angle increment
944 energies_->errslip_ -= std::min(0.0, (avg_M | (ang + t_s_elastic)));// where (ang + t_s_elatic) is the slip rotation increment (due to the sign convention, the terms are added up)
945 }
946 energies_->errstrain_ -= avg_M | t_s_elastic; // add the elastic component (if any - if previous force equals current force, the elastic incrment is zero)
947 }
948 // Calculate damping energy (accumulated) if the dashpots are active.
949 if (dpProps_) {
950 energies_->edashpot_ -= dpProps_->dp_F_ | trans;
951 }
952 }
953
954 // This is just a sanity check to ensure, in debug mode, that the force isn't wonky.
955 assert(jkr_F_ == jkr_F_);
956 return true;
957 }
958
959 void ContactModelJKR::setForce(const DVect &, IContact *) {
960 //
961 }
962
963 DVect ContactModelJKR::getForce() const {
964 DVect ret(jkr_F_);
965 if (dpProps_)
966 ret += dpProps_->dp_F_;
967 return ret;
968 }
969
970 DAVect ContactModelJKR::getMomentOn1(const IContactMechanical *c) const {
971 DAVect ret(res_M_);
972 if (c) {
973 DVect force = getForce();
974 c->updateResultingTorqueOn1Local(force, &ret);
975 }
976 return ret;
977 }
978
979 DAVect ContactModelJKR::getMomentOn2(const IContactMechanical *c) const {
980 DAVect ret(res_M_);
981 if (c) {
982 DVect force = getForce();
983 c->updateResultingTorqueOn2Local(force, &ret);
984 }
985 return ret;
986 }
987
988 DAVect ContactModelJKR::getModelMomentOn1() const {
989 DAVect ret(res_M_);
990 return ret;
991 }
992
993 DAVect ContactModelJKR::getModelMomentOn2() const {
994 DAVect ret(res_M_);
995 return ret;
996 }
997
998 void ContactModelJKR::objectPropsTypes(std::vector<std::pair<QString,InfoTypes>> *ret) const {
999 ret->clear();
1000 ret->push_back({"jkr_shear",ScalarInfo});
1001 ret->push_back({"jkr_poiss",ScalarInfo});
1002 ret->push_back({"fric",ScalarInfo});
1003 ret->push_back({"jkr_force",VectorInfo});
1004 ret->push_back({"jkr_slip",ScalarInfo});
1005 ret->push_back({"dp_nratio",ScalarInfo});
1006 ret->push_back({"dp_sratio",ScalarInfo});
1007 ret->push_back({"dp_mode",ScalarInfo});
1008 ret->push_back({"dp_force",VectorInfo});
1009 ret->push_back({"rr_fric",ScalarInfo});
1010 ret->push_back({"rr_moment",VectorInfo});
1011 ret->push_back({"rr_slip",ScalarInfo});
1012 ret->push_back({"ks_fac",ScalarInfo});
1013 ret->push_back({"surf_adh",ScalarInfo});
1014 ret->push_back({"active_mode",ScalarInfo});
1015 ret->push_back({"a0",ScalarInfo});
1016 ret->push_back({"pull_off_f",ScalarInfo});
1017 ret->push_back({"tear_off_d",ScalarInfo});
1018 }
1019
1020 void ContactModelJKR::objectPropValues(std::vector<double> *ret,const IContact *) const {
1021 ret->clear();
1022 ret->push_back(shear_);
1023 ret->push_back(poiss_);
1024 ret->push_back(fric_);
1025 ret->push_back(jkr_F_.mag());
1026 ret->push_back(jkr_S_);
1027 ret->push_back(dpProps_ ? dpProps_->dp_nratio_ : 0);
1028 ret->push_back(dpProps_ ? dpProps_->dp_sratio_ : 0);
1029 ret->push_back(dpProps_ ? dpProps_->dp_mode_ : 0);
1030 ret->push_back(dpProps_ ? dpProps_->dp_F_.mag() : 0);
1031 ret->push_back(res_fric_);
1032 ret->push_back(res_M_.mag());
1033 ret->push_back(res_S_);
1034 ret->push_back(ks_fac_);
1035 ret->push_back(surf_adh_);
1036 ret->push_back(active_mode_);
1037 ret->push_back(a0_);
1038 ret->push_back(pull_off_f_);
1039 ret->push_back(tear_off_d_);
1040 }
1041
1042 void ContactModelJKR::setDampCoefficients(const double &mass, double *kn_tangent, double *ks_tangent, double *vcn, double *vcs) {
1043 *vcn = dpProps_->dp_nratio_ * 2.0 * sqrt(mass*(*kn_tangent));
1044 *vcs = dpProps_->dp_sratio_ * 2.0 * sqrt(mass*(*ks_tangent));
1045 }
1046
1047} // namespace cmodelsxd
1048// EoF
Was this helpful? ... | Itasca Software © 2024, Itasca | Updated: Oct 31, 2024 |