Linear Contact Model Implementation

See this page for the documentation of this contact model.

contactmodellinear.h

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

Top

contactmodellinear.cpp

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

Top