Adhesive Rolling Resistance Linear Model
See this page for the documentation of this contact model.
contactmodelarrlinear.h
1#pragma once
2// contactmodelARRLinear.h
3
4#include "contactmodel/src/contactmodelmechanical.h"
5
6#ifdef ARRLINEAR_LIB
7# define ARRLINEAR_EXPORT EXPORT_TAG
8#elif defined(NO_MODEL_IMPORT)
9# define ARRLINEAR_EXPORT
10#else
11# define ARRLINEAR_EXPORT IMPORT_TAG
12#endif
13
14namespace cmodelsxd {
15 using namespace itasca;
16
17 class ContactModelARRLinear : public ContactModelMechanical {
18 public:
19 // Constructor: Set default values for contact model properties.
20 ARRLINEAR_EXPORT ContactModelARRLinear();
21 ARRLINEAR_EXPORT ContactModelARRLinear(const ContactModelARRLinear &) noexcept;
22 ARRLINEAR_EXPORT const ContactModelARRLinear & operator=(const ContactModelARRLinear &);
23 ARRLINEAR_EXPORT void addToStorage(poly::vector<ContactModelMechanical> *s,int ww = -1) override;
24 // Destructor, called when contact is deleted: free allocated memory, etc.
25 ARRLINEAR_EXPORT virtual ~ContactModelARRLinear();
26 // Contact model name (used as keyword for commands and FISH).
27 QString getName() const override { return "arrlinear"; }
28 // The index provides a quick way to determine the type of contact model.
29 // Each type of contact model in PFC must have a unique index; this is assigned
30 // by PFC when the contact model is loaded. This index should be set to -1
31 void setIndex(int i) override { index_=i;}
32 int getIndex() const override {return index_;}
33 // Contact model version number (e.g., MyModel05_1). The version number can be
34 // accessed during the save-restore operation (within the archive method,
35 // testing {stream.getRestoreVersion() == getMinorVersion()} to allow for
36 // future modifications to the contact model data structure.
37 uint32 getMinorVersion() const override;
38 // Copy the state information to a newly created contact model.
39 // Provide access to state information, for use by copy method.
40 void copy(const ContactModel *c) override;
41 // Provide save-restore capability for the state information.
42 void archive(ArchiveStream &) override;
43 // Enumerator for the properties.
44 enum PropertyKeys {
45 kwKn=1
46 , kwKs
47 , kwFric
48 , kwLinF
49 , kwLinS
50 , kwLinMode
51 , kwRGap
52 , kwEmod
53 , kwKRatio
54 , kwDpNRatio
55 , kwDpSRatio
56 , kwDpMode
57 , kwDpF
58 , kwResFric
59 , kwResMoment
60 , kwResS
61 , kwResKr
62 , kwAdhesiveF0
63 , kwAdhesiveD0
64 , kwAdhesiveF
65 , kwUserArea
66 };
67 // Contact model property names in a comma separated list. The order corresponds with
68 // the order of the PropertyKeys enumerator above. One can visualize any of these
69 // properties in PFC automatically.
70 QString getProperties() const override {
71 return "kn"
72 ",ks"
73 ",fric"
74 ",lin_force"
75 ",lin_slip"
76 ",lin_mode"
77 ",rgap"
78 ",emod"
79 ",kratio"
80 ",dp_nratio"
81 ",dp_sratio"
82 ",dp_mode"
83 ",dp_force"
84 ",rr_fric"
85 ",rr_moment"
86 ",rr_slip"
87 ",rr_kr"
88 ",adh_f0"
89 ",adh_d0"
90 ",adh_force"
91 ",user_area";
92 }
93 // Enumerator for the energies.
94 enum EnergyKeys {
95 kwEStrain=1
96 , kwERRStrain
97 , kwESlip
98 , kwERRSlip
99 , kwEDashpot
100 , kwEAdhesive
101 };
102 // Contact model energy names in a comma separated list. The order corresponds with
103 // the order of the EnergyKeys enumerator above.
104 QString getEnergies() const override {
105 return " energy-strain"
106 ",energy-rrstrain"
107 ",energy-slip"
108 ",energy-rrslip"
109 ",energy-dashpot"
110 ",energy-adhesive";
111 }
112 // Returns the value of the energy (base 1 - getEnergy(1) returns the estrain energy).
113 double getEnergy(uint32 i) const override;
114 // Returns whether or not each energy is accumulated (base 1 - getEnergyAccumulate(1)
115 // returns whether or not the estrain energy is accumulated which is false).
116 bool getEnergyAccumulate(uint32 i) const override;
117 // Set an energy value (base 1 - setEnergy(1) sets the estrain energy).
118 void setEnergy(uint32 i,const double &d) override; // Base 1
119 // Activate the energy. This is only called if the energy tracking is enabled.
120 void activateEnergy() override { if (energies_) return; energies_ = NEW Energies();}
121 // Returns whether or not the energy tracking has been enabled for this contact.
122 bool getEnergyActivated() const override {return (energies_ != 0);}
123
124 // Enumerator for contact model related FISH callback events.
125 enum FishCallEvents {
126 fActivated=0
127 ,fSlipChange
128 };
129 // Contact model FISH callback event names in a comma separated list. The order corresponds with
130 // the order of the FishCallEvents enumerator above.
131 QString getFishCallEvents() const override {
132 return
133 "contact_activated"
134 ",slip_change";
135 }
136
137 // Return the specified contact model property.
138 QVariant getProperty(uint32 i,const IContact *) const;
139 // The return value denotes whether or not the property corresponds to the global
140 // or local coordinate system (TRUE: global system, FALSE: local system). The
141 // local system is the contact-plane system (nst) defined as follows.
142 // If a vector V is expressed in the local system as (Vn, Vs, Vt), then V is
143 // expressed in the global system as {Vn*nc + Vs*sc + Vt*tc} where where nc, sc
144 // and tc are unit vectors in directions of the nst axes.
145 // This is used when rendering contact model properties that are vectors.
146 bool getPropertyGlobal(uint32 i) const override;
147 // Set the specified contact model property, ensuring that it is of the correct type
148 // and within the correct range --- if not, then throw an exception.
149 // The return value denotes whether or not the update has affected the timestep
150 // computation (by having modified the translational or rotational tangent stiffnesses).
151 // If true is returned, then the timestep will be recomputed.
152 bool setProperty(uint32 i,const QVariant &v,IContact *) override;
153 // The return value denotes whether or not the property is read-only
154 // (TRUE: read-only, FALSE: read-write).
155 bool getPropertyReadOnly(uint32 i) const override;
156
157 // The return value denotes whether or not the property is inheritable
158 // (TRUE: inheritable, FALSE: not inheritable). Inheritance is provided by
159 // the endPropertyUpdated method.
160 bool supportsInheritance(uint32 i) const override;
161 // Return whether or not inheritance is enabled for the specified property.
162 bool getInheritance(uint32 i) const override { assert(i<32); uint32 mask = to<uint32>(1 << i); return (inheritanceField_ & mask) ? true : false; }
163 // Set the inheritance flag for the specified property.
164 void setInheritance(uint32 i,bool b) override { assert(i<32); uint32 mask = to<uint32>(1 << i); if (b) inheritanceField_ |= mask; else inheritanceField_ &= ~mask; }
165
166 // Enumerator for contact model methods.
167 enum MethodKeys { kwDeformability=1, kwArea};
168 // Contact model methoid names in a comma separated list. The order corresponds with
169 // the order of the MethodKeys enumerator above.
170 QString getMethods() const override { return "deformability,area";}
171 // Return a comma seprated list of the contact model method arguments (base 1).
172 QString getMethodArguments(uint32 i) const override;
173 // Set contact model method arguments (base 1).
174 // The return value denotes whether or not the update has affected the timestep
175 // computation (by having modified the translational or rotational tangent stiffnesses).
176 // If true is returned, then the timestep will be recomputed.
177 bool setMethod(uint32 i,const QVector<QVariant> &vl,IContact *con=0) override;
178
179 // Prepare for entry into ForceDispLaw. The validate function is called when:
180 // (1) the contact is created, (2) a property of the contact that returns a true via
181 // the setProperty method has been modified and (3) when a set of cycles is executed
182 // via the {cycle N} command.
183 // Return value indicates contact activity (TRUE: active, FALSE: inactive).
184 bool validate(ContactModelMechanicalState *state,const double ×tep) override;
185 // The endPropertyUpdated method is called whenever a surface property (with a name
186 // that matches an inheritable contact model property name) of one of the contacting
187 // pieces is modified. This allows the contact model to update its associated
188 // properties. The return value denotes whether or not the update has affected
189 // the time step computation (by having modified the translational or rotational
190 // tangent stiffnesses). If true is returned, then the time step will be recomputed.
191 bool endPropertyUpdated(const QString &name,const IContactMechanical *c) override;
192 // The forceDisplacementLaw function is called during each cycle. Given the relative
193 // motion of the two contacting pieces (via
194 // state->relativeTranslationalIncrement_ (Ddn, Ddss, Ddst)
195 // state->relativeAngularIncrement_ (Dtt, Dtbs, Dtbt)
196 // Ddn : relative normal-displacement increment, Ddn > 0 is opening
197 // Ddss : relative shear-displacement increment (s-axis component)
198 // Ddst : relative shear-displacement increment (t-axis component)
199 // Dtt : relative twist-rotation increment
200 // Dtbs : relative bend-rotation increment (s-axis component)
201 // Dtbt : relative bend-rotation increment (t-axis component)
202 // The relative displacement and rotation increments:
203 // Dd = Ddn*nc + Ddss*sc + Ddst*tc
204 // Dt = Dtt*nc + Dtbs*sc + Dtbt*tc
205 // where nc, sc and tc are unit vectors in direc. of the nst axes, respectively.
206 // [see {Table 1: Contact State Variables} in PFC Model Components:
207 // Contacts and Contact Models: Contact Resolution]
208 // ) and the contact properties, this function must update the contact force and
209 // moment.
210 // The force_ is acting on piece 2, and is expressed in the local coordinate system
211 // (defined in getPropertyGlobal) such that the first component positive denotes
212 // compression. If we define the moment acting on piece 2 by Mc, and Mc is expressed
213 // in the local coordinate system (defined in getPropertyGlobal), then we must use the getMechanicalContact()->updateResultingTorquesLocal(...) method to
214 // get the total moment.
215 // The return value indicates the contact activity status (TRUE: active, FALSE:
216 // inactive) during the next cycle.
217 // Additional information:
218 // * If state->activated() is true, then the contact has just become active (it was
219 // inactive during the previous time step).
220 // * Fully elastic behavior is enforced during the SOLVE ELASTIC command by having
221 // the forceDispLaw handle the case of {state->canFail_ == true}.
222 bool forceDisplacementLaw(ContactModelMechanicalState *state,const double ×tep) override;
223 bool thermalCoupling(ContactModelMechanicalState*, ContactModelThermalState*, IContactThermal*, const double&) override;
224 // The getEffectiveXStiffness functions return the translational and rotational
225 // tangent stiffnesses used to compute a stable time step. When a contact is sliding,
226 // the translational tangent shear stiffness is zero (but this stiffness reduction
227 // is typically ignored when computing a stable time step). If the contact model
228 // includes a dashpot, then the translational stiffnesses must be increased (see
229 // Potyondy (2009)).
230 // [Potyondy, D. 'Stiffness Matrix at a Contact Between Two Clumps,' Itasca
231 // Consulting Group, Inc., Minneapolis, MN, Technical Memorandum ICG6863-L,
232 // December 7, 2009.]
233 DVect2 getEffectiveTranslationalStiffness() const override { return effectiveTranslationalStiffness_; }
234 DAVect getEffectiveRotationalStiffness() const override { return effectiveRotationalStiffness_;}
235
236 // Return a new instance of the contact model. This is used in the CMAT
237 // when a new contact is created.
238 ContactModelARRLinear *clone() const override { return NEW ContactModelARRLinear(); }
239 // The getActivityDistance function is called by the contact-resolution logic when
240 // the CMAT is modified. Return value is the activity distance used by the
241 // checkActivity function.
242 double getActivityDistance() const override {return (rgap_ + a_d0_);}
243 // The isOKToDelete function is called by the contact-resolution logic when...
244 // Return value indicates whether or not the contact may be deleted.
245 // If TRUE, then the contact may be deleted when it is inactive.
246 // If FALSE, then the contact may not be deleted (under any condition).
247 bool isOKToDelete() const override { return !isBonded(); }
248 // Zero the forces and moments stored in the contact model. This function is called
249 // when the contact becomes inactive.
250 void resetForcesAndMoments() override { lin_F(DVect(0.0)); dp_F(DVect(0.0));
251 res_M(DAVect(0.0)); a_F(0.0);
252 if (energies_) { energies_->estrain_ = 0.0;
253 energies_->errstrain_ = 0.0; }
254 }
255 void setForce(const DVect &v,IContact *c) override;
256 void setArea(const double &d) override { userArea_ = d; }
257 double getArea() const override { return userArea_; }
258 // The checkActivity function is called by the contact-resolution logic when...
259 // Return value indicates contact activity (TRUE: active, FALSE: inactive).
260 // A contact with the arrlinear model is active if the surface gap is less than
261 // or equal to the attraction range (a_d0_).
262 bool checkActivity(const double &gap) override { return gap <= (rgap_ + a_d0_); }
263
264 // Returns the sliding state (FALSE is returned if not implemented).
265 bool isSliding() const override { return lin_S_; }
266 // Returns the bonding state (FALSE is returned if not implemented).
267 bool isBonded() const override { return false; }
268
269 // Both of these methods are called only for contacts with facets where the wall
270 // resolution scheme is set the full. In such cases one might wish to propagate
271 // contact state information (e.g., shear force) from one active contact to another.
272 // See the Faceted Wall section in the documentation.
273 void propagateStateInformation(IContactModelMechanical* oldCm,const CAxes &oldSystem=CAxes(),const CAxes &newSystem=CAxes()) override;
274 void setNonForcePropsFrom(IContactModel *oldCM) override;
275
276 /// Return the total force that the contact model holds.
277 DVect getForce() const override;
278
279 /// Return the total moment on 1 that the contact model holds
280 DAVect getMomentOn1(const IContactMechanical *) const override;
281
282 /// Return the total moment on 1 that the contact model holds
283 DAVect getMomentOn2(const IContactMechanical *) const override;
284 /// Return moments without torque
285 DAVect getModelMomentOn1() const override;
286 DAVect getModelMomentOn2() const override;
287
288 // Used to efficiently get properties from the contact model for the object pane.
289 // List of properties for the object pane, comma separated.
290 // All properties will be cast to doubles for comparison. No other comparisons
291 // are supported. This may not be the same as the entire property list.
292 // Return property name and type for plotting.
293 void objectPropsTypes(std::vector<std::pair<QString,InfoTypes>> *) const override;
294 // All properties cast to doubles - this is what can be compared.
295 void objectPropValues(std::vector<double> *,const IContact *) const override;
296
297 // Methods to get and set properties.
298 const double & kn() const {return kn_;}
299 void kn(const double &d) {kn_=d;}
300 const double & ks() const {return ks_;}
301 void ks(const double &d) {ks_=d;}
302 const double & fric() const {return fric_;}
303 void fric(const double &d) {fric_=d;}
304 const DVect & lin_F() const {return lin_F_;}
305 void lin_F(const DVect &f) { lin_F_=f;}
306 bool lin_S() const {return lin_S_;}
307 void lin_S(bool b) { lin_S_=b;}
308 uint32 lin_mode() const {return lin_mode_;}
309 void lin_mode(uint32 i) { lin_mode_= i;}
310 const double & rgap() const {return rgap_;}
311 void rgap(const double &d) {rgap_=d;}
312 double emod(const IContact *c) const;
313 double kratio() const;
314
315 bool hasDamping() const {return dpProps_ ? true : false;}
316 double dp_nratio() const {return (hasDamping() ? (dpProps_->dp_nratio_) : 0.0);}
317 void dp_nratio(const double &d) { if(!hasDamping()) return; dpProps_->dp_nratio_=d;}
318 double dp_sratio() const {return hasDamping() ? dpProps_->dp_sratio_: 0.0;}
319 void dp_sratio(const double &d) { if(!hasDamping()) return; dpProps_->dp_sratio_=d;}
320 int dp_mode() const {return hasDamping() ? dpProps_->dp_mode_: -1;}
321 void dp_mode(int i) { if(!hasDamping()) return; dpProps_->dp_mode_=i;}
322 DVect dp_F() const {return hasDamping() ? dpProps_->dp_F_: DVect(0.0);}
323 void dp_F(const DVect &f) { if(!hasDamping()) return; dpProps_->dp_F_=f;}
324
325 bool hasEnergies() const {return energies_ ? true:false;}
326 double estrain() const {return hasEnergies() ? energies_->estrain_: 0.0;}
327 void estrain(const double &d) { if(!hasEnergies()) return; energies_->estrain_=d;}
328 double errstrain() const {return hasEnergies() ? energies_->errstrain_: 0.0;}
329 void errstrain(const double &d) { if(!hasEnergies()) return; energies_->errstrain_=d;}
330 double eslip() const {return hasEnergies() ? energies_->eslip_: 0.0;}
331 void eslip(const double &d) { if(!hasEnergies()) return; energies_->eslip_=d;}
332 double errslip() const {return hasEnergies() ? energies_->errslip_: 0.0;}
333 void errslip(const double &d) { if(!hasEnergies()) return; energies_->errslip_=d;}
334 double edashpot() const {return hasEnergies() ? energies_->edashpot_: 0.0;}
335 void edashpot(const double &d) { if(!hasEnergies()) return; energies_->edashpot_=d;}
336 double eadhesive() const {return hasEnergies() ? energies_->eadhesive_: 0.0;}
337 void eadhesive(const double &d) { if(!hasEnergies()) return; energies_->eadhesive_=d;}
338
339 uint32 inheritanceField() const {return inheritanceField_;}
340 void inheritanceField(uint32 i) {inheritanceField_ = i;}
341
342 const DVect2 & effectiveTranslationalStiffness() const {return effectiveTranslationalStiffness_;}
343 void effectiveTranslationalStiffness(const DVect2 &v ) {effectiveTranslationalStiffness_=v;}
344 const DAVect & effectiveRotationalStiffness() const {return effectiveRotationalStiffness_;}
345 void effectiveRotationalStiffness(const DAVect &v ) {effectiveRotationalStiffness_=v;}
346
347 // Rolling resistance methods
348 const double & res_fric() const {return res_fric_;}
349 void res_fric(const double &d) {res_fric_=d;}
350 const DAVect & res_M() const {return res_M_;}
351 void res_M(const DAVect &f) { res_M_=f;}
352 bool res_S() const {return res_S_;}
353 void res_S(bool b) { res_S_=b;}
354 const double & kr() const {return kr_;}
355 void kr(const double &d) {kr_=d;}
356 const double & fr() const {return fr_;}
357 void fr(const double &d) {fr_=d;}
358
359 // Adhesive methods
360 const double & a_f0() const {return a_f0_;}
361 void a_f0(const double &d) {a_f0_ = d;}
362 const double & a_d0() const {return a_d0_;}
363 void a_d0(const double &d) {a_d0_ = d;}
364 const double & a_F() const {return a_F_;}
365 void a_F(const double &d) {a_F_ = d;}
366
367 private:
368 // Index - used internally by PFC. Should be set to -1 in the cpp file.
369 static int index_;
370
371 // Structure to store the energies.
372 struct Energies {
373 Energies() : estrain_(0.0), errstrain_(0.0), eslip_(0.0), errslip_(0.0), edashpot_(0.0), eadhesive_(0.0) {}
374 double estrain_; // elastic energy stored in linear group
375 double errstrain_; // elastic energy stored in rolling resistance group
376 double eslip_; // work dissipated by friction
377 double errslip_; // work dissipated by rolling resistance friction
378 double edashpot_; // work dissipated by dashpots
379 double eadhesive_; // work done by attractive force on contacting pieces (positive or negative)
380 };
381
382 // Structure to store dashpot quantities.
383 struct dpProps {
384 dpProps() : dp_nratio_(0.0), dp_sratio_(0.0), dp_mode_(0), dp_F_(DVect(0.0)) {}
385 double dp_nratio_; // normal viscous critical damping ratio
386 double dp_sratio_; // shear viscous critical damping ratio
387 int dp_mode_; // for viscous mode (0-4) 0 = dashpots, 1 = tensile limit, 2 = shear limit, 3 = limit both
388 DVect dp_F_; // Force in the dashpots
389 };
390
391 bool updateKn(const IContactMechanical *con);
392 bool updateKs(const IContactMechanical *con);
393 bool updateFric(const IContactMechanical *con);
394 bool updateResFric(const IContactMechanical *con);
395
396 void updateStiffness(ContactModelMechanicalState *state);
397
398 void setDampCoefficients(const double &mass,double *vcn,double *vcs);
399
400 // Contact model inheritance fields.
401 uint32 inheritanceField_;
402
403 // Effective translational stiffness.
404 DVect2 effectiveTranslationalStiffness_ = DVect2(0.0);
405 DAVect effectiveRotationalStiffness_ = DAVect(0.0); // (Twisting,Bending,Bending) Rotational stiffness (twisting always 0)
406
407 // linear model properties
408 double kn_ = 0.0; // Normal stiffness
409 double ks_ = 0.0; // Shear stiffness
410 double fric_ = 0.0; // Coulomb friction coefficient
411 DVect lin_F_ = DVect(0.0); // Force carried in the linear model
412 bool lin_S_ = false; // The current slip state
413 uint32 lin_mode_ = 0; // Specifies absolute (0) or incremental (1) calculation mode
414 double rgap_ = 0; // Reference gap
415 dpProps * dpProps_ = nullptr; // The viscous properties
416
417 // rolling resistance properties
418 double res_fric_ = 0.0; // rolling friction coefficient
419 DAVect res_M_ = DAVect(0.0); // moment (bending only)
420 bool res_S_ = false; // The current rolling resistance slip state
421 double kr_ = 0.0; // bending rotational stiffness (read-only, calculated internaly)
422 double fr_ = 0.0; // rolling friction coefficient (rbar*res_fric_) (calculated internaly, not a property)
423
424 // Adhesive properties
425 double a_f0_ = 0.0; // maximum attractive force [force], "a_f0"
426 double a_d0_ = 0.0; // attraction range [length] , "a_d0"
427 double a_F_ = 0.0; // attractive force [force] , "a_force"
428 double userArea_ = 0.0; // Area as specified by the user
429 Energies * energies_ = nullptr; // The energies
430
431 };
432} // namespace cmodelsxd
433// EoF
contactmodelarrlinear.cpp
1// contactmodelARRLinear.cpp
2#include "contactmodelarrlinear.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 "shared/src/mathutil.h"
10
11#include "kernel/interface/iprogram.h"
12#include "module/interface/icontactthermal.h"
13#include "contactmodel/src/contactmodelthermal.h"
14#include "../version.txt"
15#include "fish/src/parameter.h"
16
17#ifdef ARRLINEAR_LIB
18#ifdef _WIN32
19 int __stdcall DllMain(void *,unsigned, void *) {
20 return 1;
21 }
22#endif
23
24 extern "C" EXPORT_TAG const char *getName() {
25#if DIM==3
26 return "contactmodelmechanical3dARRLinear";
27#else
28 return "contactmodelmechanical2dARRLinear";
29#endif
30 }
31
32 extern "C" EXPORT_TAG unsigned getMajorVersion() {
33 return MAJOR_VERSION;
34 }
35
36 extern "C" EXPORT_TAG unsigned getMinorVersion() {
37 return MINOR_VERSION; ;
38 }
39
40 extern "C" EXPORT_TAG void *createInstance() {
41 cmodelsxd::ContactModelARRLinear *m = NEW cmodelsxd::ContactModelARRLinear();
42 return (void *)m;
43 }
44#endif
45
46namespace cmodelsxd {
47 static const uint32 linKnMask = 0x00000002; // Base 1!
48 static const uint32 linKsMask = 0x00000004;
49 static const uint32 linFricMask = 0x00000008;
50 static const uint32 resFricMask = 0x00004000;
51
52 using namespace itasca;
53
54 int ContactModelARRLinear::index_ = -1;
55 uint32 ContactModelARRLinear::getMinorVersion() const { return MINOR_VERSION; }
56
57 ContactModelARRLinear::ContactModelARRLinear() : inheritanceField_(linKnMask|linKsMask|linFricMask|resFricMask)
58 { }
59
60 ContactModelARRLinear::ContactModelARRLinear(const ContactModelARRLinear &m) noexcept {
61 inheritanceField_ = linKnMask|linKsMask|linFricMask|resFricMask;
62 this->copy(&m);
63 }
64
65 const ContactModelARRLinear & ContactModelARRLinear::operator=(const ContactModelARRLinear &m) {
66 inheritanceField_ = linKnMask|linKsMask|linFricMask|resFricMask;
67 this->copy(&m);
68 return *this;
69 }
70
71 void ContactModelARRLinear::addToStorage(poly::vector<ContactModelMechanical> *s,int ww) {
72 s->addToStorage<ContactModelARRLinear>(*this,ww);
73 }
74
75 ContactModelARRLinear::~ContactModelARRLinear() {
76 // Make sure to clean up after yourself!
77 if (dpProps_)
78 delete dpProps_;
79 if (energies_)
80 delete energies_;
81 }
82
83 void ContactModelARRLinear::archive(ArchiveStream &stream) {
84 // The stream allows one to archive the values of the contact model
85 // so that it can be saved and restored. The minor version can be
86 // used here to allow for incremental changes to the contact model too.
87 stream & kn_;
88 stream & ks_;
89 stream & fric_;
90 stream & lin_F_;
91 stream & lin_S_;
92 stream & lin_mode_;
93 stream & rgap_;
94 stream & res_fric_;
95 stream & res_M_;
96 stream & res_S_;
97 stream & kr_;
98 stream & fr_;
99 stream & a_f0_;
100 stream & a_d0_;
101 stream & a_F_;
102
103 if (stream.getArchiveState()==ArchiveStream::Save) {
104 bool b = false;
105 if (dpProps_) {
106 b = true;
107 stream & b;
108 stream & dpProps_->dp_nratio_;
109 stream & dpProps_->dp_sratio_;
110 stream & dpProps_->dp_mode_;
111 stream & dpProps_->dp_F_;
112 }
113 else
114 stream & b;
115
116 b = false;
117 if (energies_) {
118 b = true;
119 stream & b;
120 stream & energies_->estrain_;
121 stream & energies_->errstrain_;
122 stream & energies_->eslip_;
123 stream & energies_->errslip_;
124 stream & energies_->edashpot_;
125 stream & energies_->eadhesive_;
126 }
127 else
128 stream & b;
129 } else {
130 bool b(false);
131 stream & b;
132 if (b) {
133 if (!dpProps_)
134 dpProps_ = NEW dpProps();
135 stream & dpProps_->dp_nratio_;
136 stream & dpProps_->dp_sratio_;
137 stream & dpProps_->dp_mode_;
138 stream & dpProps_->dp_F_;
139 }
140 stream & b;
141 if (b) {
142 if (!energies_)
143 energies_ = NEW Energies();
144 stream & energies_->estrain_;
145 stream & energies_->errstrain_;
146 stream & energies_->eslip_;
147 stream & energies_->errslip_;
148 stream & energies_->edashpot_;
149 stream & energies_->eadhesive_;
150 }
151 }
152
153 stream & inheritanceField_;
154 stream & effectiveTranslationalStiffness_;
155 stream & effectiveRotationalStiffness_;
156
157 if (stream.getArchiveState()==ArchiveStream::Save || stream.getRestoreVersion() > 1)
158 stream & userArea_;
159 }
160
161 void ContactModelARRLinear::copy(const ContactModel *cm) {
162 // Copy all of the contact model properties. Used in the CMAT
163 // when a new contact is created.
164 ContactModelMechanical::copy(cm);
165 const ContactModelARRLinear *in = dynamic_cast<const ContactModelARRLinear*>(cm);
166 if (!in) throw std::runtime_error("Internal error: contact model dynamic cast failed.");
167 kn(in->kn());
168 ks(in->ks());
169 fric(in->fric());
170 lin_F(in->lin_F());
171 lin_S(in->lin_S());
172 lin_mode(in->lin_mode());
173 rgap(in->rgap());
174 res_fric(in->res_fric());
175 res_M(in->res_M());
176 res_S(in->res_S());
177 kr(in->kr());
178 fr(in->fr());
179 a_f0(in->a_f0());
180 a_d0(in->a_d0());
181 a_F(in->a_F());
182
183 if (in->hasDamping()) {
184 if (!dpProps_)
185 dpProps_ = NEW dpProps();
186 dp_nratio(in->dp_nratio());
187 dp_sratio(in->dp_sratio());
188 dp_mode(in->dp_mode());
189 dp_F(in->dp_F());
190 }
191 if (in->hasEnergies()) {
192 if (!energies_)
193 energies_ = NEW Energies();
194 estrain(in->estrain());
195 errstrain(in->errstrain());
196 eslip(in->eslip());
197 errslip(in->errslip());
198 edashpot(in->edashpot());
199 eadhesive(in->eadhesive());
200 }
201 userArea_ = in->userArea_;
202 inheritanceField(in->inheritanceField());
203 effectiveTranslationalStiffness(in->effectiveTranslationalStiffness());
204 effectiveRotationalStiffness(in->effectiveRotationalStiffness());
205 }
206
207
208 QVariant ContactModelARRLinear::getProperty(uint32 i,const IContact *con) const {
209 // Return the property. The IContact pointer is provided so that
210 // more complicated properties, depending on contact characteristics,
211 // can be calcualted. The IContact pointer may be a nullptr!
212 const IContactMechanical *c(convert_getcast<IContactMechanical>(con));
213 QVariant var;
214 switch (i) {
215 case kwKn: return kn_;
216 case kwKs: return ks_;
217 case kwFric: return fric_;
218 case kwLinF: var.setValue(lin_F_); return var;
219 case kwLinS: return lin_S_;
220 case kwLinMode: return lin_mode_;
221 case kwRGap: return rgap_;
222 case kwEmod: return emod(con);
223 case kwKRatio: return kratio();
224 case kwDpNRatio: return dpProps_ ? dpProps_->dp_nratio_ : 0;
225 case kwDpSRatio: return dpProps_ ? dpProps_->dp_sratio_ : 0;
226 case kwDpMode: return dpProps_ ? dpProps_->dp_mode_ : 0;
227 case kwDpF: {
228 dpProps_ ? var.setValue(dpProps_->dp_F_) : var.setValue(DVect(0.0));
229 return var;
230 }
231 case kwResFric: return res_fric_;
232 case kwResMoment: var.setValue(res_M_); return var;
233 case kwResS: return res_S_;
234 case kwResKr: return kr_;
235 case kwAdhesiveF0: return a_f0_;
236 case kwAdhesiveD0: return a_d0_;
237 case kwAdhesiveF: return a_F_;
238 case kwUserArea : return userArea_;
239 }
240 assert(0);
241 return QVariant();
242 }
243
244 bool ContactModelARRLinear::getPropertyGlobal(uint32 i) const {
245 // Returns whether or not a property is held in the global axis system (TRUE)
246 // or the local system (FALSE). Used by the plotting logic.
247 switch (i) {
248 case kwLinF:
249 case kwDpF:
250 case kwResMoment:
251 return false;
252 }
253 return true;
254 }
255
256 bool ContactModelARRLinear::setProperty(uint32 i,const QVariant &v,IContact *) {
257 // Set a contact model property. Return value indicates that the timestep
258 // should be recalculated.
259 dpProps dp;
260 switch (i) {
261 case kwKn: {
262 if (!v.canConvert<double>())
263 throw Exception("kn must be a double.");
264 double val(v.toDouble());
265 if (val<0.0)
266 throw Exception("Negative kn not allowed.");
267 kn_ = val;
268 return true;
269 }
270 case kwKs: {
271 if (!v.canConvert<double>())
272 throw Exception("ks must be a double.");
273 double val(v.toDouble());
274 if (val<0.0)
275 throw Exception("Negative ks not allowed.");
276 ks_ = val;
277 return true;
278 }
279 case kwFric: {
280 if (!v.canConvert<double>())
281 throw Exception("fric must be a double.");
282 double val(v.toDouble());
283 if (val<0.0)
284 throw Exception("Negative fric not allowed.");
285 fric_ = val;
286 return false;
287 }
288 case kwLinF: {
289 if (!v.canConvert<DVect>())
290 throw Exception("lin_force must be a vector.");
291 DVect val(v.value<DVect>());
292 lin_F_ = val;
293 return false;
294 }
295 case kwLinMode: {
296 if (!v.canConvert<uint32>())
297 throw Exception("lin_mode must be 0 (absolute) or 1 (incremental).");
298 uint32 val(v.toUInt());
299 if (val >1)
300 throw Exception("lin_mode must be 0 (absolute) or 1 (incremental).");
301 lin_mode_ = val;
302 return false;
303 }
304 case kwRGap: {
305 if (!v.canConvert<double>())
306 throw Exception("Reference gap must be a double.");
307 double val(v.toDouble());
308 rgap_ = val;
309 return false;
310 }
311 case kwDpNRatio: {
312 if (!v.canConvert<double>())
313 throw Exception("dp_nratio must be a double.");
314 double val(v.toDouble());
315 if (val<0.0)
316 throw Exception("Negative dp_nratio not allowed.");
317 if (val == 0.0 && !dpProps_)
318 return false;
319 if (!dpProps_)
320 dpProps_ = NEW dpProps();
321 dpProps_->dp_nratio_ = val;
322 return true;
323 }
324 case kwDpSRatio: {
325 if (!v.canConvert<double>())
326 throw Exception("dp_sratio must be a double.");
327 double val(v.toDouble());
328 if (val<0.0)
329 throw Exception("Negative dp_sratio not allowed.");
330 if (val == 0.0 && !dpProps_)
331 return false;
332 if (!dpProps_)
333 dpProps_ = NEW dpProps();
334 dpProps_->dp_sratio_ = val;
335 return true;
336 }
337 case kwDpMode: {
338 if (!v.canConvert<int>())
339 throw Exception("The viscous mode dp_mode must be 0, 1, 2, or 3.");
340 int val(v.toInt());
341 if (val == 0 && !dpProps_)
342 return false;
343 if (val < 0 || val > 3)
344 throw Exception("The viscous mode dp_mode must be 0, 1, 2, or 3.");
345 if (!dpProps_)
346 dpProps_ = NEW dpProps();
347 dpProps_->dp_mode_ = val;
348 return false;
349 }
350 case kwDpF: {
351 if (!v.canConvert<DVect>())
352 throw Exception("dp_force must be a vector.");
353 DVect val(v.value<DVect>());
354 if (val.fsum() == 0.0 && !dpProps_)
355 return false;
356 if (!dpProps_)
357 dpProps_ = NEW dpProps();
358 dpProps_->dp_F_ = val;
359 return false;
360 }
361 case kwResFric: {
362 if (!v.canConvert<double>())
363 throw Exception("res_fric must be a double.");
364 double val(v.toDouble());
365 if (val<0.0)
366 throw Exception("Negative res_fric not allowed.");
367 res_fric_ = val;
368 return false;
369 }
370 case kwResMoment: {
371 DAVect val(0.0);
372#ifdef TWOD
373 if (!v.canConvert<DAVect>() && !v.canConvert<double>())
374 throw Exception("res_moment must be an angular vector.");
375 if (v.canConvert<DAVect>())
376 val = DAVect(v.value<DAVect>());
377 else
378 val = DAVect(v.toDouble());
379#else
380 if (!v.canConvert<DAVect>() && !v.canConvert<DVect>())
381 throw Exception("res_moment must be an angular vector.");
382 if (v.canConvert<DAVect>())
383 val = DAVect(v.value<DAVect>());
384 else
385 val = DAVect(v.value<DVect>());
386#endif
387 res_M_ = val;
388 return false;
389 }
390 case kwAdhesiveF0: {
391 if (!v.canConvert<double>())
392 throw Exception("a_f0 must be a double.");
393 double val(v.toDouble());
394 if (val<0.0)
395 throw Exception("Negative a_f0 not allowed.");
396 a_f0_ = val;
397 return true;
398 }
399 case kwAdhesiveD0: {
400 if (!v.canConvert<double>())
401 throw Exception("a_d0 must be a double.");
402 double val(v.toDouble());
403 if (val<0.0)
404 throw Exception("Negative a_d0 not allowed.");
405 a_d0_ = val;
406 return true;
407 }
408 case kwUserArea: {
409 if (!v.canConvert<double>())
410 throw Exception("user_area must be a double.");
411 double val(v.toDouble());
412 if (val < 0.0)
413 throw Exception("Negative user_area not allowed.");
414 userArea_ = val;
415 return true;
416 }
417 }
418 return false;
419 }
420
421 bool ContactModelARRLinear::getPropertyReadOnly(uint32 i) const {
422 // Returns TRUE if a property is read only or FALSE otherwise.
423 switch (i) {
424 case kwDpF:
425 case kwLinS:
426 case kwEmod:
427 case kwKRatio:
428 case kwResS:
429 case kwResKr:
430 case kwAdhesiveF:
431 return true;
432 default:
433 break;
434 }
435 return false;
436 }
437
438 bool ContactModelARRLinear::supportsInheritance(uint32 i) const {
439 // Returns TRUE if a property supports inheritance or FALSE otherwise.
440 switch (i) {
441 case kwKn:
442 case kwKs:
443 case kwFric:
444 case kwResFric:
445 return true;
446 default:
447 break;
448 }
449 return false;
450 }
451
452 QString ContactModelARRLinear::getMethodArguments(uint32 i) const {
453 // Return a list of contact model method argument names.
454 switch (i) {
455 case kwDeformability:
456 return "emod,kratio";
457 case kwArea:
458 return QString();
459 }
460 assert(0);
461 return QString();
462 }
463
464 bool ContactModelARRLinear::setMethod(uint32 i,const QVector<QVariant> &vl,IContact *con) {
465 // Apply the specified method.
466 IContactMechanical *c(convert_getcast<IContactMechanical>(con));
467 switch (i) {
468 case kwDeformability: {
469 double emod;
470 double krat;
471 if (vl.at(0).isNull())
472 throw Exception("Argument emod must be specified with method deformability in contact model {0}.",getName());
473 emod = vl.at(0).toDouble();
474 if (emod<0.0)
475 throw Exception("Negative emod not allowed in contact model {0}.",getName());
476 if (vl.at(1).isNull())
477 throw Exception("Argument kratio must be specified with method deformability in contact model {0}.",getName());
478 krat = vl.at(1).toDouble();
479 if (krat<0.0)
480 throw Exception("Negative stiffness ratio not allowed in contact model {0}.",getName());
481 double rsq(std::max(c->getEnd1Curvature().y(),c->getEnd2Curvature().y()));
482 double rsum(0.0);
483 if (c->getEnd1Curvature().y())
484 rsum += 1.0/c->getEnd1Curvature().y();
485 if (c->getEnd2Curvature().y())
486 rsum += 1.0/c->getEnd2Curvature().y();
487 if (userArea_) {
488#ifdef THREED
489 rsq = std::sqrt(userArea_ / dPi);
490#else
491 rsq = userArea_ / 2.0;
492#endif
493 rsum = rsq + rsq;
494 rsq = 1. / rsq;
495 }
496#ifdef TWOD
497 kn_ = 2.0 * emod / (rsq * rsum);
498#else
499 kn_ = dPi * emod / (rsq * rsq * rsum);
500#endif
501 ks_ = (krat == 0.0) ? 0.0 : kn_ / krat;
502 setInheritance(1,false);
503 setInheritance(2,false);
504 return true;
505 }
506 case kwArea: {
507 if (!userArea_) {
508 double rsq(1./std::max(c->getEnd1Curvature().y(),c->getEnd2Curvature().y()));
509#ifdef THREED
510 userArea_ = rsq * rsq * dPi;
511#else
512 userArea_ = rsq * 2.0;
513#endif
514 }
515 return true;
516 }
517 }
518 return false;
519 }
520
521 double ContactModelARRLinear::getEnergy(uint32 i) const {
522 // Return an energy value.
523 double ret(0.0);
524 if (!energies_)
525 return ret;
526 switch (i) {
527 case kwEStrain: return energies_->estrain_;
528 case kwERRStrain: return energies_->errstrain_;
529 case kwESlip: return energies_->eslip_;
530 case kwERRSlip: return energies_->errslip_;
531 case kwEDashpot: return energies_->edashpot_;
532 case kwEAdhesive: return energies_->eadhesive_;
533 }
534 assert(0);
535 return ret;
536 }
537
538 bool ContactModelARRLinear::getEnergyAccumulate(uint32 i) const {
539 // Returns TRUE if the corresponding energy is accumulated or FALSE otherwise.
540 switch (i) {
541 case kwEStrain: return false;
542 case kwERRStrain: return false;
543 case kwESlip: return true;
544 case kwERRSlip: return true;
545 case kwEDashpot: return true;
546 case kwEAdhesive: return true;
547 }
548 assert(0);
549 return false;
550 }
551
552 void ContactModelARRLinear::setEnergy(uint32 i,const double &d) {
553 // Set an energy value.
554 if (!energies_) return;
555 switch (i) {
556 case kwEStrain: energies_->estrain_ = d; return;
557 case kwERRStrain: energies_->errstrain_ = d; return;
558 case kwESlip: energies_->eslip_ = d; return;
559 case kwERRSlip: energies_->errslip_ = d; return;
560 case kwEDashpot: energies_->edashpot_= d; return;
561 case kwEAdhesive: energies_->eadhesive_= d; return;
562 }
563 assert(0);
564 return;
565 }
566
567 bool ContactModelARRLinear::validate(ContactModelMechanicalState *state,const double &) {
568 // Validate the / Prepare for entry into ForceDispLaw. The validate function is called when:
569 // (1) the contact is created, (2) a property of the contact that returns a true via
570 // the setProperty method has been modified and (3) when a set of cycles is executed
571 // via the {cycle N} command.
572 // Return value indicates contact activity (TRUE: active, FALSE: inactive).
573 assert(state);
574 const IContactMechanical *c = state->getMechanicalContact();
575 assert(c);
576
577 if (state->trackEnergy_)
578 activateEnergy();
579
580 if (inheritanceField_ & linKnMask)
581 updateKn(c);
582 if (inheritanceField_ & linKsMask)
583 updateKs(c);
584 if (inheritanceField_ & linFricMask)
585 updateFric(c);
586 if (inheritanceField_ & resFricMask)
587 updateResFric(c);
588
589 updateStiffness(state);
590 return checkActivity(state->gap_);
591 }
592
593 static const QString knstr("kn");
594 bool ContactModelARRLinear::updateKn(const IContactMechanical *con) {
595 assert(con);
596 QVariant v1 = con->getEnd1()->getProperty(knstr);
597 QVariant v2 = con->getEnd2()->getProperty(knstr);
598 if (!v1.isValid() || !v2.isValid())
599 return false;
600 double kn1 = v1.toDouble();
601 double kn2 = v2.toDouble();
602 double val = kn_;
603 if (kn1 && kn2)
604 kn_ = kn1*kn2/(kn1+kn2);
605 else if (kn1)
606 kn_ = kn1;
607 else if (kn2)
608 kn_ = kn2;
609 return ( (kn_ != val) );
610 }
611
612 static const QString ksstr("ks");
613 bool ContactModelARRLinear::updateKs(const IContactMechanical *con) {
614 assert(con);
615 QVariant v1 = con->getEnd1()->getProperty(ksstr);
616 QVariant v2 = con->getEnd2()->getProperty(ksstr);
617 if (!v1.isValid() || !v2.isValid())
618 return false;
619 double ks1 = v1.toDouble();
620 double ks2 = v2.toDouble();
621 double val = ks_;
622 if (ks1 && ks2)
623 ks_ = ks1*ks2/(ks1+ks2);
624 else if (ks1)
625 ks_ = ks1;
626 else if (ks2)
627 ks_ = ks2;
628 return ( (ks_ != val) );
629 }
630
631 static const QString fricstr("fric");
632 bool ContactModelARRLinear::updateFric(const IContactMechanical *con) {
633 assert(con);
634 QVariant v1 = con->getEnd1()->getProperty(fricstr);
635 QVariant v2 = con->getEnd2()->getProperty(fricstr);
636 if (!v1.isValid() || !v2.isValid())
637 return false;
638 double fric1 = std::max(0.0,v1.toDouble());
639 double fric2 = std::max(0.0,v2.toDouble());
640 double val = fric_;
641 fric_ = std::min(fric1,fric2);
642 return ( (fric_ != val) );
643 }
644
645 static const QString rfricstr("rr_fric");
646 bool ContactModelARRLinear::updateResFric(const IContactMechanical *con) {
647 assert(con);
648 QVariant v1 = con->getEnd1()->getProperty(rfricstr);
649 QVariant v2 = con->getEnd2()->getProperty(rfricstr);
650 if (!v1.isValid() || !v2.isValid())
651 return false;
652 double rfric1 = std::max(0.0,v1.toDouble());
653 double rfric2 = std::max(0.0,v2.toDouble());
654 double val = res_fric_;
655 res_fric_ = std::min(rfric1,rfric2);
656 return ( (res_fric_ != val) );
657 }
658
659 bool ContactModelARRLinear::endPropertyUpdated(const QString &name,const IContactMechanical *c) {
660 // The endPropertyUpdated method is called whenever a surface property (with a name
661 // that matches an inheritable contact model property name) of one of the contacting
662 // pieces is modified. This allows the contact model to update its associated
663 // properties. The return value denotes whether or not the update has affected
664 // the time step computation (by having modified the translational or rotational
665 // tangent stiffnesses). If true is returned, then the time step will be recomputed.
666 assert(c);
667 QStringList availableProperties = getProperties().simplified().replace(" ","").split(",",Qt::SkipEmptyParts);
668 QRegularExpression rx(name, QRegularExpression::CaseInsensitiveOption);
669 int idx = availableProperties.indexOf(rx)+1;
670 bool ret=false;
671
672 if (idx<=0)
673 return ret;
674
675 switch(idx) {
676 case kwKn: { //kn
677 if (inheritanceField_ & linKnMask)
678 ret = updateKn(c);
679 break;
680 }
681 case kwKs: { //ks
682 if (inheritanceField_ & linKsMask)
683 ret =updateKs(c);
684 break;
685 }
686 case kwFric: { //fric
687 if (inheritanceField_ & linFricMask)
688 updateFric(c);
689 break;
690 }
691 case kwResFric: { //rr_fric
692 if (inheritanceField_ & resFricMask)
693 ret = updateResFric(c);
694 break;
695 }
696 }
697 return ret;
698 }
699
700 void ContactModelARRLinear::updateStiffness(ContactModelMechanicalState *state) {
701 // first compute rolling resistance stiffness
702 kr_ = 0.0;
703 if (res_fric_ > 0.0) {
704 double rbar = 0.0;
705 double r1 = 1.0/state->end1Curvature_.y();
706 rbar = r1;
707 double r2 = 0.0;
708 if (state->end2Curvature_.y()) {
709 r2 = 1.0 / state->end2Curvature_.y();
710 rbar = (r1*r2) / (r1+r2);
711 }
712 if (userArea_) {
713#ifdef THREED
714 r1 = std::sqrt(userArea_ / dPi);
715#else
716 r1 = userArea_ / 2.0;
717#endif
718 r2 = r1;
719 rbar = (r1*r2) / (r1+r2);
720 }
721 kr_ = ks_*rbar*rbar;
722 fr_ = res_fric_*rbar;
723 }
724 // Now calculate effective stiffness
725 DVect2 retT(kn_,ks_);
726 // correction if viscous damping active
727 if (dpProps_) {
728 DVect2 correct(1.0);
729 if (dpProps_->dp_nratio_)
730 correct.rx() = sqrt(1.0+dpProps_->dp_nratio_*dpProps_->dp_nratio_) - dpProps_->dp_nratio_;
731 if (dpProps_->dp_sratio_)
732 correct.ry() = sqrt(1.0+dpProps_->dp_sratio_*dpProps_->dp_sratio_) - dpProps_->dp_sratio_;
733 retT /= (correct*correct);
734 }
735 // Correction for adhesive group
736 if (a_d0_ != 0.0) { retT.rdof(0) += a_f0_ / a_d0_; }
737
738 effectiveTranslationalStiffness_ = retT;
739 // Effective rotational stiffness (bending only)
740 effectiveRotationalStiffness_ = DAVect(kr_);
741#if DIM==3
742 effectiveRotationalStiffness_.rx() = 0.0;
743#endif
744 }
745
746 bool ContactModelARRLinear::forceDisplacementLaw(ContactModelMechanicalState *state,const double ×tep) {
747 assert(state);
748
749 // Current overlap
750 double overlap = rgap_ - state->gap_;
751 // Relative translational increment
752 DVect trans = state->relativeTranslationalIncrement_;
753 // Correction factor to account for when the contact becomes newly active.
754 // We estimate the time of activity during the timestep when the contact has first
755 // become active and scale the forces accordingly.
756 double correction = 1.0;
757
758 // The contact was just activated from an inactive state
759 if (state->activated()) {
760 // Trigger the FISH callback if one is hooked up to the
761 // contact_activated event.
762 if (cmEvents_[fActivated] >= 0) {
763 auto c = state->getContact();
764 std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()) };
765 IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
766 fi->setCMFishCallArguments(c,arg,cmEvents_[fActivated]);
767 }
768 // Calculate the correction factor.
769 if (trans.x()) {
770 correction = -1.0*overlap / trans.x();
771 if (correction < 0)
772 correction = 1.0;
773 }
774 }
775
776 // Angular dispacement increment.
777 DAVect ang = state->relativeAngularIncrement_;
778 DVect lin_F_old = lin_F_;
779
780 if (lin_mode_ == 0)
781 lin_F_.rx() = overlap * kn_; // absolute mode for normal force calculation
782 else
783 lin_F_.rx() -= correction * trans.x() * kn_; // incremental mode for normal force calculation
784
785 // Normal force can only be positive.
786 lin_F_.rx() = std::max(0.0,lin_F_.x());
787
788 // Calculate the shear force.
789 DVect sforce(0.0);
790 // dim holds the dimension (e.g., 2 for 2D and 3 for 3D)
791 // Loop over the shear components (note: the 0 component is the normal component)
792 // and calculate the shear force.
793 for (int i=1; i<dim; ++i)
794 sforce.rdof(i) = lin_F_.dof(i) - trans.dof(i) * ks_ * correction;
795
796 // The canFail flag corresponds to whether or not the contact can undergo non-linear
797 // force-displacement response. If the SOLVE ELASTIC command is given then the
798 // canFail state is set to FALSE. Otherwise it is always TRUE.
799 if (state->canFail_) {
800 // Resolve sliding. This is the normal force multiplied by the coefficient of friction.
801 double crit = lin_F_.x() * fric_;
802 // The is the magnitude of the shear force.
803 double sfmag = sforce.mag();
804 // Sliding occurs when the magnitude of the shear force is greater than the
805 // critical value.
806 if (sfmag > crit) {
807 // Lower the shear force to the critical value for sliding.
808 double rat = crit / sfmag;
809 sforce *= rat;
810 // Handle the slip_change event if one has been hooked up. Sliding has commenced.
811 if (!lin_S_ && cmEvents_[fSlipChange] >= 0) {
812 auto c = state->getContact();
813 std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
814 fish::Parameter() };
815 IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
816 fi->setCMFishCallArguments(c,arg,cmEvents_[fSlipChange]);
817 }
818 lin_S_ = true;
819 } else {
820 // Handle the slip_change event if one has been hooked up and
821 // the contact was previously sliding. Sliding has ceased.
822 if (lin_S_) {
823 if (cmEvents_[fSlipChange] >= 0) {
824 auto c = state->getContact();
825 std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
826 fish::Parameter((qint64)1) };
827 IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
828 fi->setCMFishCallArguments(c,arg,cmEvents_[fSlipChange]);
829 }
830 lin_S_ = false;
831 }
832 }
833 }
834
835 // Set the shear components of the total force.
836 for (int i=1; i<dim; ++i)
837 lin_F_.rdof(i) = sforce.dof(i);
838
839 // Rolling resistance
840 DAVect res_M_old = res_M_;
841 if ((fr_ == 0.0) || (kr_==0.0)) {
842 res_M_.fill(0.0);
843 } else {
844 DAVect angStiff(0.0);
845 DAVect MomentInc(0.0);
846#if DIM==3
847 angStiff.rx() = 0.0;
848 angStiff.ry() = kr_;
849#endif
850 angStiff.rz() = kr_;
851 MomentInc = ang * angStiff * (-1.0);
852 res_M_ += MomentInc;
853 if (state->canFail_) {
854 // Account for bending strength
855 double rmax = std::abs(fr_*lin_F_.x());
856 double rmag = res_M_.mag();
857 if (rmag > rmax) {
858 double fac = rmax/rmag;
859 res_M_ *= fac;
860 res_S_ = true;
861 } else {
862 res_S_ = false;
863 }
864 }
865 }
866
867 // Account for dashpot forces if the dashpot structure has been defined.
868 if (dpProps_) {
869 dpProps_->dp_F_.fill(0.0);
870 double vcn(0.0), vcs(0.0);
871 // Calculate the damping coefficients.
872 setDampCoefficients(state->inertialMass_,&vcn,&vcs);
873 // First damp the shear components
874 for (int i=1; i<dim; ++i)
875 dpProps_->dp_F_.rdof(i) = trans.dof(i) * (-1.0* vcs) / timestep;
876 // Damp the normal component
877 dpProps_->dp_F_.rx() -= trans.x() * vcn / timestep;
878 // Need to change behavior based on the dp_mode.
879 if ((dpProps_->dp_mode_ == 1 || dpProps_->dp_mode_ == 3)) {
880 // Limit in tension if not bonded.
881 if (dpProps_->dp_F_.x() + lin_F_.x() < 0)
882 dpProps_->dp_F_.rx() = - lin_F_.rx();
883 }
884 if (lin_S_ && dpProps_->dp_mode_ > 1) {
885 // Limit in shear if not sliding.
886 double dfn = dpProps_->dp_F_.rx();
887 dpProps_->dp_F_.fill(0.0);
888 dpProps_->dp_F_.rx() = dfn;
889 }
890 }
891
892 // Adhesive force
893 double a_F_old = a_F_;
894 double gs = state->gap_ - rgap_;
895 a_F_ = 0.0;
896 if ( gs <= 0.0 )
897 a_F_ = a_f0_;
898 else if ( gs < a_d0_ ) // if a_d0_ == 0.0, will not enter, so next line divide by a_d0_ is ok
899 a_F_ = a_f0_ * ( 1.0 - (gs/a_d0_) );
900
901 //Compute energies if energy tracking has been enabled.
902 if (state->trackEnergy_) {
903 assert(energies_);
904 energies_->estrain_ = 0.0;
905 if (kn_)
906 // Calculate the strain energy.
907 energies_->estrain_ = 0.5*lin_F_.x()*lin_F_.x()/kn_;
908 if (ks_) {
909 DVect s = lin_F_;
910 s.rx() = 0.0;
911 double smag2 = s.mag2();
912 // Add the shear component of the strain energy.
913 energies_->estrain_ += 0.5*smag2 / ks_;
914
915 if (lin_S_) {
916 // If sliding calculate the slip energy and accumulate it.
917 lin_F_old.rx() = 0.0;
918 DVect avg_F_s = (s + lin_F_old)*0.5;
919 DVect u_s_el = (s - lin_F_old) / ks_;
920 DVect u_s(0.0);
921 for (int i=1; i<dim; ++i)
922 u_s.rdof(i) = trans.dof(i);
923 energies_->eslip_ -= std::min(0.0,(avg_F_s | (u_s + u_s_el)));
924 }
925 }
926 // Add the rolling resistance energy contributions.
927 energies_->errstrain_ = 0.0;
928 if (kr_) {
929 energies_->errstrain_ = 0.5*res_M_.mag2() / kr_;
930 if (res_S_) {
931 // If sliding calculate the slip energy and accumulate it.
932 DAVect avg_M = (res_M_ + res_M_old)*0.5;
933 DAVect t_s_el = (res_M_ - res_M_old) / kr_;
934 energies_->errslip_ -= std::min(0.0,(avg_M | (ang + t_s_el)));
935 }
936 }
937 // Add the adhesive energy contribution:
938 energies_->eadhesive_ -= 0.5*(a_F_old + a_F_) * trans.x();
939
940 if (dpProps_) {
941 // Calculate damping energy (accumulated) if the dashpots are active.
942 energies_->edashpot_ -= dpProps_->dp_F_ | trans;
943 }
944 }
945
946 // This is just a sanity check to ensure, in debug mode, that the force isn't wonky.
947 assert(lin_F_ == lin_F_);
948 return true;
949 }
950
951 bool ContactModelARRLinear::thermalCoupling(ContactModelMechanicalState*, ContactModelThermalState* ts, IContactThermal*, const double&) {
952 // Account for thermal expansion in incremental mode
953 if (lin_mode_ == 0 || ts->gapInc_ == 0.0) return false;
954 DVect finc(0.0);
955 finc.rx() = kn_ * ts->gapInc_;
956 lin_F_ -= finc;
957 return true;
958 }
959
960 void ContactModelARRLinear::setForce(const DVect &v,IContact *c) {
961 lin_F(v);
962 if (v.x() > 0)
963 rgap_ = c->getGap() + v.x() / kn_;
964 }
965
966 void ContactModelARRLinear::propagateStateInformation(IContactModelMechanical* old,const CAxes &oldSystem,const CAxes &newSystem) {
967 // Only called for contacts with wall facets when the wall resolution scheme
968 // is set to full!
969 // Only do something if the contact model is of the same type
970 if (old->getContactModel()->getName().compare("arrlinear",Qt::CaseInsensitive) == 0 && !isBonded()) {
971 ContactModelARRLinear *oldCm = (ContactModelARRLinear *)old;
972#ifdef THREED
973 // Need to rotate just the shear component from oldSystem to newSystem
974
975 // Step 1 - rotate oldSystem so that the normal is the same as the normal of newSystem
976 DVect axis = oldSystem.e1() & newSystem.e1();
977 double c, ang, s;
978 DVect re2;
979 if (!checktol(axis.abs().maxComp(),0.0,1.0,1000)) {
980 axis = axis.unit();
981 c = oldSystem.e1()|newSystem.e1();
982 if (c > 0)
983 c = std::min(c,1.0);
984 else
985 c = std::max(c,-1.0);
986 ang = acos(c);
987 s = sin(ang);
988 double t = 1. - c;
989 DMatrix<3,3> rm;
990 rm.get(0,0) = t*axis.x()*axis.x() + c;
991 rm.get(0,1) = t*axis.x()*axis.y() - axis.z()*s;
992 rm.get(0,2) = t*axis.x()*axis.z() + axis.y()*s;
993 rm.get(1,0) = t*axis.x()*axis.y() + axis.z()*s;
994 rm.get(1,1) = t*axis.y()*axis.y() + c;
995 rm.get(1,2) = t*axis.y()*axis.z() - axis.x()*s;
996 rm.get(2,0) = t*axis.x()*axis.z() - axis.y()*s;
997 rm.get(2,1) = t*axis.y()*axis.z() + axis.x()*s;
998 rm.get(2,2) = t*axis.z()*axis.z() + c;
999 re2 = rm*oldSystem.e2();
1000 }
1001 else
1002 re2 = oldSystem.e2();
1003 // Step 2 - get the angle between the oldSystem rotated shear and newSystem shear
1004 axis = re2 & newSystem.e2();
1005 DVect2 tpf;
1006 DVect2 tpm;
1007 DMatrix<2,2> m;
1008 if (!checktol(axis.abs().maxComp(),0.0,1.0,1000)) {
1009 axis = axis.unit();
1010 c = re2|newSystem.e2();
1011 if (c > 0)
1012 c = std::min(c,1.0);
1013 else
1014 c = std::max(c,-1.0);
1015 ang = acos(c);
1016 if (!checktol(axis.x(),newSystem.e1().x(),1.0,100))
1017 ang *= -1;
1018 s = sin(ang);
1019 m.get(0,0) = c;
1020 m.get(1,0) = s;
1021 m.get(0,1) = -m.get(1,0);
1022 m.get(1,1) = m.get(0,0);
1023 tpf = m*DVect2(oldCm->lin_F_.y(),oldCm->lin_F_.z());
1024 tpm = m*DVect2(oldCm->res_M_.y(),oldCm->res_M_.z());
1025 } else {
1026 m.get(0,0) = 1.;
1027 m.get(0,1) = 0.;
1028 m.get(1,0) = 0.;
1029 m.get(1,1) = 1.;
1030 tpf = DVect2(oldCm->lin_F_.y(),oldCm->lin_F_.z());
1031 tpm = DVect2(oldCm->res_M_.y(),oldCm->res_M_.z());
1032 }
1033 DVect pforce = DVect(0,tpf.x(),tpf.y());
1034 //DVect pm = DVect(0,tpm.x(),tpm.y());
1035#else
1036 oldSystem;
1037 newSystem;
1038 DVect pforce = DVect(0,oldCm->lin_F_.y());
1039 //DVect pm = DVect(0,oldCm->res_M_.y());
1040#endif
1041 for (int i=1; i<dim; ++i)
1042 lin_F_.rdof(i) += pforce.dof(i);
1043 if (lin_mode_ && oldCm->lin_mode_)
1044 lin_F_.rx() = oldCm->lin_F_.x();
1045 oldCm->lin_F_ = DVect(0.0);
1046 oldCm->res_M_ = DAVect(0.0);
1047 if (dpProps_ && oldCm->dpProps_) {
1048#ifdef THREED
1049 tpf = m*DVect2(oldCm->dpProps_->dp_F_.y(),oldCm->dpProps_->dp_F_.z());
1050 pforce = DVect(oldCm->dpProps_->dp_F_.x(),tpf.x(),tpf.y());
1051#else
1052 pforce = oldCm->dpProps_->dp_F_;
1053#endif
1054 dpProps_->dp_F_ += pforce;
1055 oldCm->dpProps_->dp_F_ = DVect(0.0);
1056 }
1057 if(oldCm->getEnergyActivated()) {
1058 activateEnergy();
1059 energies_->estrain_ = oldCm->energies_->estrain_;
1060 energies_->edashpot_ = oldCm->energies_->edashpot_;
1061 energies_->eslip_ = oldCm->energies_->eslip_;
1062 oldCm->energies_->estrain_ = 0.0;
1063 oldCm->energies_->edashpot_ = 0.0;
1064 oldCm->energies_->eslip_ = 0.0;
1065 }
1066 }
1067 assert(lin_F_ == lin_F_);
1068 }
1069
1070 void ContactModelARRLinear::setNonForcePropsFrom(IContactModel *old) {
1071 // Only called for contacts with wall facets when the wall resolution scheme
1072 // is set to full!
1073 // Only do something if the contact model is of the same type
1074 if (old->getName().compare("arrlinear",Qt::CaseInsensitive) == 0 && !isBonded()) {
1075 ContactModelARRLinear *oldCm = (ContactModelARRLinear *)old;
1076 kn_ = oldCm->kn_;
1077 ks_ = oldCm->ks_;
1078 fric_ = oldCm->fric_;
1079 lin_mode_ = oldCm->lin_mode_;
1080 rgap_ = oldCm->rgap_;
1081 res_fric_ = oldCm->res_fric_;
1082 res_S_ = oldCm->res_S_;
1083 kr_ = oldCm->kr_;
1084 fr_ = oldCm->fr_;
1085 a_f0_ = oldCm->a_f0_;
1086 a_d0_ = oldCm->a_d0_;
1087 userArea_ = oldCm->userArea_;
1088
1089 if (oldCm->dpProps_) {
1090 if (!dpProps_)
1091 dpProps_ = NEW dpProps();
1092 dpProps_->dp_nratio_ = oldCm->dpProps_->dp_nratio_;
1093 dpProps_->dp_sratio_ = oldCm->dpProps_->dp_sratio_;
1094 dpProps_->dp_mode_ = oldCm->dpProps_->dp_mode_;
1095 }
1096 }
1097 }
1098
1099 DVect ContactModelARRLinear::getForce() const {
1100 DVect ret(lin_F_);
1101 if (dpProps_)
1102 ret += dpProps_->dp_F_;
1103 ret.rdof(0) -= a_F_;
1104 return ret;
1105 }
1106
1107 DAVect ContactModelARRLinear::getMomentOn1(const IContactMechanical *c) const {
1108 DVect force = getForce();
1109 DAVect ret(res_M_);
1110 c->updateResultingTorqueOn1Local(force,&ret);
1111 return ret;
1112 }
1113
1114 DAVect ContactModelARRLinear::getMomentOn2(const IContactMechanical *c) const {
1115 DVect force = getForce();
1116 DAVect ret(res_M_);
1117 c->updateResultingTorqueOn2Local(force,&ret);
1118 return ret;
1119 }
1120
1121 DAVect ContactModelARRLinear::getModelMomentOn1() const {
1122 DAVect ret(res_M_);
1123 return ret;
1124 }
1125
1126 DAVect ContactModelARRLinear::getModelMomentOn2() const {
1127 DAVect ret(res_M_);
1128 return ret;
1129 }
1130
1131 void ContactModelARRLinear::objectPropsTypes(std::vector<std::pair<QString,InfoTypes>> *ret) const {
1132 ret->clear();
1133 ret->push_back({"kn",ScalarInfo});
1134 ret->push_back({"ks",ScalarInfo});
1135 ret->push_back({"fric",ScalarInfo});
1136 ret->push_back({"lin_force",VectorInfo});
1137 ret->push_back({"lin_slip",ScalarInfo});
1138 ret->push_back({"lin_mode",ScalarInfo});
1139 ret->push_back({"rgap",ScalarInfo});
1140 ret->push_back({"emod",ScalarInfo});
1141 ret->push_back({"kratio",ScalarInfo});
1142 ret->push_back({"dp_nratio",ScalarInfo});
1143 ret->push_back({"dp_sratio",ScalarInfo});
1144 ret->push_back({"dp_mode",ScalarInfo});
1145 ret->push_back({"dp_force",VectorInfo});
1146 ret->push_back({"rr_fric",ScalarInfo});
1147 ret->push_back({"rr_moment",VectorInfo});
1148 ret->push_back({"rr_slip",ScalarInfo});
1149 ret->push_back({"rr_kr",ScalarInfo});
1150 ret->push_back({"adh_f0",ScalarInfo});
1151 ret->push_back({"adh_d0",ScalarInfo});
1152 ret->push_back({"adh_force",VectorInfo});
1153 ret->push_back({"user_area",ScalarInfo});
1154 }
1155
1156 void ContactModelARRLinear::objectPropValues(std::vector<double> *ret,const IContact *c) const {
1157 ret->clear();
1158 ret->push_back(kn());
1159 ret->push_back(ks());
1160 ret->push_back(fric());
1161 ret->push_back(lin_F().mag());
1162 ret->push_back(lin_S());
1163 ret->push_back(lin_mode());
1164 ret->push_back(rgap());
1165 ret->push_back(emod(c));
1166 ret->push_back(kratio());
1167 ret->push_back(dp_nratio());
1168 ret->push_back(dp_sratio());
1169 ret->push_back(dp_mode());
1170 ret->push_back(dp_F().mag());
1171 ret->push_back(res_fric());
1172 ret->push_back(res_M().mag());
1173 ret->push_back(res_S());
1174 ret->push_back(kr());
1175 ret->push_back(a_f0());
1176 ret->push_back(a_d0());
1177 ret->push_back(a_F());
1178 ret->push_back(getArea());
1179 }
1180
1181 double ContactModelARRLinear::emod(const IContact *con) const {
1182 const IContactMechanical *c(convert_getcast<IContactMechanical>(con));
1183 if (c ==nullptr) return 0.0;
1184 double rsq(std::max(c->getEnd1Curvature().y(),c->getEnd2Curvature().y()));
1185 double rsum(0.0);
1186 if (c->getEnd1Curvature().y())
1187 rsum += 1.0/c->getEnd1Curvature().y();
1188 if (c->getEnd2Curvature().y())
1189 rsum += 1.0/c->getEnd2Curvature().y();
1190 if (userArea_) {
1191#ifdef THREED
1192 rsq = std::sqrt(userArea_ / dPi);
1193#else
1194 rsq = userArea_ / 2.0;
1195#endif
1196 rsum = rsq + rsq;
1197 rsq = 1. / rsq;
1198 }
1199#ifdef TWOD
1200 return (kn_ * rsum * rsq / 2.0);
1201#else
1202 return (kn_ * rsum * rsq * rsq) / dPi;
1203#endif
1204 }
1205
1206 double ContactModelARRLinear::kratio() const {
1207 return (ks_ == 0.0) ? 0.0 : (kn_/ks_);
1208 }
1209
1210 void ContactModelARRLinear::setDampCoefficients(const double &mass,double *vcn,double *vcs) {
1211 *vcn = dpProps_->dp_nratio_ * 2.0 * sqrt(mass*(kn_));
1212 *vcs = dpProps_->dp_sratio_ * 2.0 * sqrt(mass*(ks_));
1213 }
1214
1215} // namespace cmodelsxd
1216// EoF
Was this helpful? ... | Itasca Software © 2024, Itasca | Updated: Dec 19, 2024 |