Rolling Resistance Linear Contact Model Implementation

See this page for the documentation of this contact model.

contactmodelrrlinear.h

  1#pragma once
  2// contactmodelrrlinear.h
  3
  4#include "contactmodel/src/contactmodelmechanical.h"
  5
  6#ifdef rrlinear_LIB
  7#  define rrlinear_EXPORT EXPORT_TAG
  8#elif defined(NO_MODEL_IMPORT)
  9#  define rrlinear_EXPORT
 10#else
 11#  define rrlinear_EXPORT IMPORT_TAG
 12#endif
 13
 14namespace cmodelsxd {
 15    using namespace itasca;
 16
 17    class ContactModelRRLinear : public ContactModelMechanical {
 18    public:
 19        // Constructor: Set default values for contact model properties.
 20        rrlinear_EXPORT ContactModelRRLinear();
 21        rrlinear_EXPORT ContactModelRRLinear(const ContactModelRRLinear &) noexcept;
 22        rrlinear_EXPORT const ContactModelRRLinear & operator=(const ContactModelRRLinear &);
 23        rrlinear_EXPORT void   addToStorage(poly::vector<ContactModelMechanical> *s,int ww = -1) override;
 24        // Destructor, called when contact is deleted: free allocated memory, etc.
 25        rrlinear_EXPORT virtual ~ContactModelRRLinear();
 26        // Contact model name (used as keyword for commands and FISH).
 27        QString  getName() const override { return "rrlinear"; }
 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            , kwUserArea
 63        };
 64        // Contact model property names in a comma separated list. The order corresponds with
 65        // the order of the PropertyKeys enumerator above. One can visualize any of these 
 66        // properties in PFC automatically. 
 67        QString  getProperties() const override {
 68            return "kn"
 69                   ",ks"
 70                   ",fric"
 71                   ",lin_force"
 72                   ",lin_slip"
 73                   ",lin_mode"
 74                   ",rgap"
 75                   ",emod"
 76                   ",kratio"
 77                   ",dp_nratio"
 78                   ",dp_sratio"
 79                   ",dp_mode"
 80                   ",dp_force"
 81                   ",rr_fric"
 82                   ",rr_moment"
 83                   ",rr_slip"
 84                   ",rr_kr"
 85                   ",user_area" ;
 86        }
 87        // Enumerator for the energies.
 88        enum EnergyKeys { 
 89            kwEStrain=1
 90          , kwERRStrain
 91          , kwESlip
 92          , kwERRSlip
 93          , kwEDashpot
 94        };
 95        // Contact model energy names in a comma separated list. The order corresponds with
 96        // the order of the EnergyKeys enumerator above. 
 97        QString  getEnergies() const override {
 98            return "energy-strain"
 99                   ",energy-rrstrain"
100                   ",energy-slip"
101                   ",energy-rrslip"
102                   ",energy-dashpot";
103        }
104        // Returns the value of the energy (base 1 - getEnergy(1) returns the estrain energy).
105        double   getEnergy(uint32 i) const override;
106        // Returns whether or not each energy is accumulated (base 1 - getEnergyAccumulate(1) 
107        // returns wther or not the estrain energy is accumulated which is false).
108        bool     getEnergyAccumulate(uint32 i) const override;
109        // Set an energy value (base 1 - setEnergy(1) sets the estrain energy).
110        void     setEnergy(uint32 i,const double &d) override; // Base 1
111        // Activate the energy. This is only called if the energy tracking is enabled. 
112        void     activateEnergy() override { if (energies_) return; energies_ = NEW Energies();}
113        // Returns whether or not the energy tracking has been enabled for this contact.
114        bool     getEnergyActivated() const override {return (energies_ != 0);}
115
116        // Enumerator for contact model related FISH callback events. 
117        enum FishCallEvents {
118            fActivated=0
119            ,fSlipChange
120        };
121        // Contact model FISH callback event names in a comma separated list. The order corresponds with
122        // the order of the FishCallEvents enumerator above. 
123        QString  getFishCallEvents() const override {
124            return 
125                "contact_activated"
126                ",slip_change"; 
127        }
128
129        // Return the specified contact model property.
130        QVariant getProperty(uint32 i,const IContact *) const override;
131        // The return value denotes whether or not the property corresponds to the global
132        // or local coordinate system (TRUE: global system, FALSE: local system). The
133        // local system is the contact-plane system (nst) defined as follows.
134        // If a vector V is expressed in the local system as (Vn, Vs, Vt), then V is
135        // expressed in the global system as {Vn*nc + Vs*sc + Vt*tc} where where nc, sc
136        // and tc are unit vectors in directions of the nst axes.
137        // This is used when rendering contact model properties that are vectors.
138        bool     getPropertyGlobal(uint32 i) const override;
139        // Set the specified contact model property, ensuring that it is of the correct type
140        // and within the correct range --- if not, then throw an exception.
141        // The return value denotes whether or not the update has affected the timestep
142        // computation (by having modified the translational or rotational tangent stiffnesses).
143        // If true is returned, then the timestep will be recomputed.
144        bool     setProperty(uint32 i,const QVariant &v,IContact *) override;
145        // The return value denotes whether or not the property is read-only
146        // (TRUE: read-only, FALSE: read-write).
147        bool     getPropertyReadOnly(uint32 i) const override;
148
149        // The return value denotes whether or not the property is inheritable
150        // (TRUE: inheritable, FALSE: not inheritable). Inheritance is provided by
151        // the endPropertyUpdated method.
152        bool     supportsInheritance(uint32 i) const override;
153        // Return whether or not inheritance is enabled for the specified property.
154        bool     getInheritance(uint32 i) const override { assert(i<32); uint32 mask = to<uint32>(1 << i);  return (inheritanceField_ & mask) ? true : false; }
155        // Set the inheritance flag for the specified property.
156        void     setInheritance(uint32 i,bool b) override { assert(i<32); uint32 mask = to<uint32>(1 << i);  if (b) inheritanceField_ |= mask;  else inheritanceField_ &= ~mask; }
157
158        // Enumerator for contact model methods.
159        enum MethodKeys { kwDeformability=1,kwArea};
160        // Contact model methoid names in a comma separated list. The order corresponds with
161        // the order of the MethodKeys enumerator above.  
162        QString  getMethods() const override { return "deformability,area";}
163        // Return a comma seprated list of the contact model method arguments (base 1).
164        QString  getMethodArguments(uint32 i) const override;
165        // Set contact model method arguments (base 1). 
166        // The return value denotes whether or not the update has affected the timestep
167        // computation (by having modified the translational or rotational tangent stiffnesses).
168        // If true is returned, then the timestep will be recomputed.
169        bool     setMethod(uint32 i,const QVector<QVariant> &vl,IContact *con=0) override;
170
171        // Prepare for entry into ForceDispLaw. The validate function is called when:
172        // (1) the contact is created, (2) a property of the contact that returns a true via
173        // the setProperty method has been modified and (3) when a set of cycles is executed
174        // via the {cycle N} command.
175        // Return value indicates contact activity (TRUE: active, FALSE: inactive).
176        bool    validate(ContactModelMechanicalState *state,const double &timestep) override;
177        // The endPropertyUpdated method is called whenever a surface property (with a name
178        // that matches an inheritable contact model property name) of one of the contacting
179        // pieces is modified. This allows the contact model to update its associated
180        // properties. The return value denotes whether or not the update has affected
181        // the time step computation (by having modified the translational or rotational
182        // tangent stiffnesses). If true is returned, then the time step will be recomputed.  
183        bool    endPropertyUpdated(const QString &name,const IContactMechanical *c) override;
184        // The forceDisplacementLaw function is called during each cycle. Given the relative
185        // motion of the two contacting pieces (via
186        //   state->relativeTranslationalIncrement_ (Ddn, Ddss, Ddst)
187        //   state->relativeAngularIncrement_       (Dtt, Dtbs, Dtbt)
188        //     Ddn  : relative normal-displacement increment, Ddn > 0 is opening
189        //     Ddss : relative  shear-displacement increment (s-axis component)
190        //     Ddst : relative  shear-displacement increment (t-axis component)
191        //     Dtt  : relative twist-rotation increment
192        //     Dtbs : relative  bend-rotation increment (s-axis component)
193        //     Dtbt : relative  bend-rotation increment (t-axis component)
194        //       The relative displacement and rotation increments:
195        //         Dd = Ddn*nc + Ddss*sc + Ddst*tc
196        //         Dt = Dtt*nc + Dtbs*sc + Dtbt*tc
197        //       where nc, sc and tc are unit vectors in direc. of the nst axes, respectively.
198        //       [see {Table 1: Contact State Variables} in PFC Model Components:
199        //       Contacts and Contact Models: Contact Resolution]
200        // ) and the contact properties, this function must update the contact force and
201        // moment.
202        //   The force_ is acting on piece 2, and is expressed in the local coordinate system
203        //   (defined in getPropertyGlobal) such that the first component positive denotes
204        //   compression. If we define the moment acting on piece 2 by Mc, and Mc is expressed
205        //   in the local coordinate system (defined in getPropertyGlobal), then we must use the getMechanicalContact()->updateResultingTorquesLocal(...) method to 
206        //   get the total moment. 
207        // The return value indicates the contact activity status (TRUE: active, FALSE:
208        // inactive) during the next cycle.
209        // Additional information:
210        //   * If state->activated() is true, then the contact has just become active (it was
211        //     inactive during the previous time step).
212        //   * Fully elastic behavior is enforced during the SOLVE ELASTIC command by having
213        //     the forceDispLaw handle the case of {state->canFail_ == true}.
214        bool    forceDisplacementLaw(ContactModelMechanicalState *state,const double &timestep) override;
215        bool    thermalCoupling(ContactModelMechanicalState*, ContactModelThermalState*, IContactThermal*, const double&) override;
216        // The getEffectiveXStiffness functions return the translational and rotational
217        // tangent stiffnesses used to compute a stable time step. When a contact is sliding,
218        // the translational tangent shear stiffness is zero (but this stiffness reduction
219        // is typically ignored when computing a stable time step). If the contact model
220        // includes a dashpot, then the translational stiffnesses must be increased (see
221        // Potyondy (2009)).
222        //   [Potyondy, D. 'Stiffness Matrix at a Contact Between Two Clumps,' Itasca
223        //   Consulting Group, Inc., Minneapolis, MN, Technical Memorandum ICG6863-L,
224        //   December 7, 2009.]
225        DVect2  getEffectiveTranslationalStiffness() const override { return effectiveTranslationalStiffness_; }
226        DAVect  getEffectiveRotationalStiffness() const override { return effectiveRotationalStiffness_;}
227
228        // Return a new instance of the contact model. This is used in the CMAT
229        // when a new contact is created. 
230        ContactModelRRLinear *clone() const override { return NEW ContactModelRRLinear(); }
231        // The getActivityDistance function is called by the contact-resolution logic when
232        // the CMAT is modified. Return value is the activity distance used by the
233        // checkActivity function.
234        double              getActivityDistance() const override {return rgap_;}
235        // The isOKToDelete function is called by the contact-resolution logic when...
236        // Return value indicates whether or not the contact may be deleted.
237        // If TRUE, then the contact may be deleted when it is inactive.
238        // If FALSE, then the contact may not be deleted (under any condition).
239        bool                isOKToDelete() const override { return !isBonded(); }
240        // Zero the forces and moments stored in the contact model. This function is called
241        // when the contact becomes inactive.
242        void                resetForcesAndMoments() override {
243            lin_F(DVect(0.0)); 
244            dp_F(DVect(0.0)); 
245            res_M(DAVect(0.0));
246            if (energies_) {
247                energies_->estrain_ = 0.0;
248                energies_->errstrain_ = 0.0;
249            }
250        }
251        void                setForce(const DVect &v,IContact *c) override;
252        void                setArea(const double &d) override { userArea_ = d; }
253        double              getArea() const override { return userArea_; }
254        // The checkActivity function is called by the contact-resolution logic when...
255        // Return value indicates contact activity (TRUE: active, FALSE: inactive).
256        bool     checkActivity(const double &gap) override { return  gap <= rgap_; }
257        // Returns the sliding state (FALSE is returned if not implemented).
258        bool     isSliding() const override { return lin_S_; }
259        // Returns the bonding state (FALSE is returned if not implemented).
260        bool     isBonded() const override { return false; }
261        // Both of these methods are called only for contacts with facets where the wall 
262        // resolution scheme is set the full. In such cases one might wish to propagate 
263        // contact state information (e.g., shear force) from one active contact to another. 
264        // See the Faceted Wall section in the documentation. 
265        void     propagateStateInformation(IContactModelMechanical* oldCm,const CAxes &oldSystem=CAxes(),const CAxes &newSystem=CAxes()) override;
266        void     setNonForcePropsFrom(IContactModel *oldCM) override;
267        /// Return the total force that the contact model holds.
268        DVect    getForce() const override;
269        /// Return the total moment on 1 that the contact model holds
270        DAVect   getMomentOn1(const IContactMechanical *) const override;
271        /// Return the total moment on 1 that the contact model holds
272        DAVect   getMomentOn2(const IContactMechanical *) const override;
273        
274        DAVect getModelMomentOn1() const override;
275        DAVect getModelMomentOn2() const override;
276
277        // Used to efficiently get properties from the contact model for the object pane.
278        // List of properties for the object pane, comma separated.
279        // All properties will be cast to doubles for comparison. No other comparisons
280        // are supported. This may not be the same as the entire property list.
281        // Return property name and type for plotting.
282        void objectPropsTypes(std::vector<std::pair<QString,InfoTypes>> *) const override;
283        // All properties cast to doubles - this is what can be compared. 
284        void objectPropValues(std::vector<double> *,const IContact *) const override;
285   
286        // Methods to get and set properties. 
287        const double & kn() const {return kn_;}
288        void           kn(const double &d) {kn_=d;}
289        const double & ks() const {return ks_;}
290        void           ks(const double &d) {ks_=d;}
291        const double & fric() const {return fric_;}
292        void           fric(const double &d) {fric_=d;}
293        const DVect &  lin_F() const {return lin_F_;}
294        void           lin_F(const DVect &f) { lin_F_=f;}
295        bool           lin_S() const {return lin_S_;}
296        void           lin_S(bool b) { lin_S_=b;}
297        uint32           lin_mode() const {return lin_mode_;}
298        void           lin_mode(uint32 i) { lin_mode_= i;}
299        const double & rgap() const {return rgap_;}
300        void           rgap(const double &d) {rgap_=d;}
301
302        bool     hasDamping() const {return dpProps_ ? true : false;}
303        double   dp_nratio() const {return (hasDamping() ? (dpProps_->dp_nratio_) : 0.0);}
304        void     dp_nratio(const double &d) { if(!hasDamping()) return; dpProps_->dp_nratio_=d;}
305        double   dp_sratio() const {return hasDamping() ? dpProps_->dp_sratio_: 0.0;}
306        void     dp_sratio(const double &d) { if(!hasDamping()) return; dpProps_->dp_sratio_=d;}
307        int      dp_mode() const {return hasDamping() ? dpProps_->dp_mode_: -1;}
308        void     dp_mode(int i) { if(!hasDamping()) return; dpProps_->dp_mode_=i;}
309        DVect    dp_F() const {return hasDamping() ? dpProps_->dp_F_: DVect(0.0);}
310        void     dp_F(const DVect &f) { if(!hasDamping()) return; dpProps_->dp_F_=f;}
311
312        bool    hasEnergies() const {return energies_ ? true:false;}
313        double  estrain() const {return hasEnergies() ? energies_->estrain_: 0.0;}
314        void    estrain(const double &d) { if(!hasEnergies()) return; energies_->estrain_=d;}
315        double  errstrain() const {return hasEnergies() ? energies_->errstrain_: 0.0;}
316        void    errstrain(const double &d) { if(!hasEnergies()) return; energies_->errstrain_=d;}
317        double  eslip() const {return hasEnergies() ? energies_->eslip_: 0.0;}
318        void    eslip(const double &d) { if(!hasEnergies()) return; energies_->eslip_=d;}
319        double  errslip() const {return hasEnergies() ? energies_->errslip_: 0.0;}
320        void    errslip(const double &d) { if(!hasEnergies()) return; energies_->errslip_=d;}
321        double  edashpot() const {return hasEnergies() ? energies_->edashpot_: 0.0;}
322        void    edashpot(const double &d) { if(!hasEnergies()) return; energies_->edashpot_=d;}
323
324        uint32 inheritanceField() const {return inheritanceField_;}
325        void inheritanceField(uint32 i) {inheritanceField_ = i;}
326
327        const DVect2 & effectiveTranslationalStiffness()  const          {return effectiveTranslationalStiffness_;}
328        void           effectiveTranslationalStiffness(const DVect2 &v ) {effectiveTranslationalStiffness_=v;}
329        const DAVect & effectiveRotationalStiffness()  const             {return effectiveRotationalStiffness_;}
330        void           effectiveRotationalStiffness(const DAVect &v )    {effectiveRotationalStiffness_=v;}
331
332        // Rolling resistance methods
333        const double & res_fric() const {return res_fric_;}
334        void           res_fric(const double &d) {res_fric_=d;}
335        const DAVect & res_M() const               {return res_M_;}
336        void           res_M(const DAVect &f)       { res_M_=f;}
337        bool           res_S() const {return res_S_;}
338        void           res_S(bool b) { res_S_=b;}
339        const double & kr() const {return kr_;}
340        void           kr(const double &d) {kr_=d;}
341        const double & fr() const {return fr_;}
342        void           fr(const double &d) {fr_=d;}
343
344    private:
345        // Index - used internally by PFC. Should be set to -1 in the cpp file. 
346        static int index_;
347
348        // Structure to store the energies. 
349        struct Energies {
350            Energies() : estrain_(0.0),errstrain_(0.0),eslip_(0.0),errslip_(0.0),edashpot_(0.0) {}
351            double estrain_;   // elastic energy stored in linear group 
352            double errstrain_; // elastic energy stored in rolling resistance group
353            double eslip_;     // work dissipated by friction 
354            double errslip_;   // work dissipated by rolling resistance friction 
355            double edashpot_;  // work dissipated by dashpots
356        };
357
358        // Structure to store dashpot quantities. 
359        struct dpProps {
360            dpProps() : dp_nratio_(0.0), dp_sratio_(0.0), dp_mode_(0), dp_F_(DVect(0.0)) {}
361            double dp_nratio_;     // normal viscous critical damping ratio
362            double dp_sratio_;     // shear  viscous critical damping ratio
363            int    dp_mode_;      // for viscous mode (0-4) 0 = dashpots, 1 = tensile limit, 2 = shear limit, 3 = limit both
364            DVect  dp_F_;  // Force in the dashpots
365        };
366
367        bool   updateKn(const IContactMechanical *con);
368        bool   updateKs(const IContactMechanical *con);
369        bool   updateFric(const IContactMechanical *con);
370        bool   updateResFric(const IContactMechanical *con);
371
372        void   updateStiffness(ContactModelMechanicalState *state);
373
374        void   setDampCoefficients(const double &mass,double *vcn,double *vcs);
375
376        // Contact model inheritance fields.
377        uint32 inheritanceField_;
378
379        // Effective translational stiffness.
380        DVect2  effectiveTranslationalStiffness_ = DVect2(0.0);
381        DAVect  effectiveRotationalStiffness_ = DAVect(0.0);      // (Twisting,Bending,Bending) Rotational stiffness (twisting always 0)
382
383        // linear model properties
384        double      kn_ = 0.0;        // Normal stiffness
385        double      ks_ = 0.0;        // Shear stiffness
386        double      fric_ = 0.0;      // Coulomb friction coefficient
387        DVect       lin_F_ = DVect(0.0);     // Force carried in the linear model
388        bool        lin_S_ = false;     // The current slip state
389        uint32        lin_mode_ = 0;  // Specifies absolute (0) or incremental (1) calculation mode 
390        double      rgap_ = 0.0;      // Reference gap 
391        dpProps *   dpProps_ = nullptr;   // The viscous properties
392
393        // rolling resistance properties
394        double res_fric_ = 0.0;       // rolling friction coefficient
395        DAVect res_M_ = DAVect(0.0);          // moment (bending only)         
396        bool   res_S_ = false;          // The current rolling resistance slip state
397        double kr_ = 0.0;             // bending rotational stiffness (read-only, calculated internaly) 
398        double fr_ = 0.0;             // rolling friction coefficient (rbar*res_fric_) (calculated internaly, not a property) 
399
400        double      userArea_ = 0.0;  // Area as specified by the user 
401
402        Energies *   energies_ = nullptr; // The energies
403
404    };
405} // namespace cmodelsxd
406// EoF

Top

contactmodelrrlinear.cpp

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

Top