Spring Network Model Implementation

See this page for the documentation of this contact model.

contactmodelrbsn.h

  1#pragma once
  2// contactmodelrbsn.h
  3
  4#include "contactmodel/src/contactmodelmechanical.h"
  5
  6#ifdef RBSN_LIB
  7#  define RBSN_EXPORT EXPORT_TAG
  8#elif defined(NO_MODEL_IMPORT)
  9#  define RBSN_EXPORT
 10#else
 11#  define RBSN_EXPORT IMPORT_TAG
 12#endif
 13
 14namespace cmodelsxd {
 15    using namespace itasca;
 16
 17    class ContactModelRBSN : public ContactModelMechanical {
 18    public:
 19        // Constructor: Set default values for contact model properties.
 20        RBSN_EXPORT ContactModelRBSN();
 21        RBSN_EXPORT ContactModelRBSN(const ContactModelRBSN &) noexcept;
 22        RBSN_EXPORT const ContactModelRBSN & operator=(const ContactModelRBSN &);
 23        RBSN_EXPORT void   addToStorage(poly::vector<ContactModelMechanical> *s,int ww = -1) override;
 24        // Destructor, called when contact is deleted: free allocated memory, etc.
 25        RBSN_EXPORT virtual ~ContactModelRBSN();
 26        // Contact model name (used as keyword for commands and FISH).
 27        QString  getName() const override { return "springnetwork"; }
 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            , kwKrot
 48            , kwFric 
 49            , kwBMul
 50            , kwTMul
 51            , kwSNMode
 52            , kwSNF
 53            , kwSNM
 54            , kwSNS
 55            , kwSNBS
 56            , kwSNTS
 57            , kwSNRMul
 58            , kwSNRadius
 59            , kwEmod
 60            , kwKRatio
 61            , kwDpNRatio 
 62            , kwDpSRatio
 63            , kwDpMode 
 64            , kwDpF
 65            , kwSNState
 66            , kwSNTStr
 67            , kwSNSStr
 68            , kwSNCoh
 69            , kwSNFa
 70            , kwSNMCF
 71            , kwSNSig
 72            , kwSNTau
 73            , kwSNArea
 74            , kwUserArea
 75            , kwRGap
 76            , kwPForce
 77            , kwPois
 78            , kwPoisDiag
 79            , kwSnCohRes
 80            , kwSnDil
 81            , kwSnDilZ
 82            , kwSnNormDisp
 83            , kwSnShearDisp
 84            , kwSnCohDc
 85            , kwSnHeal
 86            , kwSnTenDc
 87            , kwTenTable
 88            , kwCohTable
 89            , kwTablePos
 90            , kwPorP
 91            , kwStressNorm
 92        };
 93        // Contact model property names in a comma separated list. The order corresponds with
 94        // the order of the PropertyKeys enumerator above. One can visualize any of these 
 95        // properties in PFC automatically. 
 96        QString  getProperties() const override { 
 97            return "kn"
 98                   ",ks"
 99                   ",krot"
100                   ",fric"
101                   ",sn_bmul"
102                   ",sn_tmul"
103                   ",sn_mode"
104                   ",sn_force"
105                   ",sn_moment"
106                   ",sn_slip"
107                   ",sn_slipb"
108                   ",sn_slipt"
109                   ",sn_rmul"
110                   ",sn_radius"
111                   ",emod"
112                   ",kratio"
113                   ",dp_nratio"
114                   ",dp_sratio"
115                   ",dp_mode"
116                   ",dp_force"
117                   ",sn_state"
118                   ",sn_ten"
119                   ",sn_shear"
120                   ",sn_coh"
121                   ",sn_fa"
122                   ",sn_mcf"
123                   ",sn_sigma"
124                   ",sn_tau"
125                   ",sn_area"
126                   ",user_area"
127                   ",rgap"
128                   ",sn_pois_force"
129                   ",sn_pois"
130                   ",sn_non_diag"
131                   ",sn_cohres"
132                   ",sn_dil"
133                   ",sn_dilzero"
134                   ",sn_ndisp"
135                   ",sn_sdisp"
136                   ",sn_cohdc"
137                   ",sn_heal"
138                   ",sn_tendc"
139                   ",sn_tentab"
140                   ",sn_cohtab"
141                   ",sn_tabpos"
142                   ",sn_porp"
143                   ",sn_esigma"
144                ;
145        }
146        // Enumerator for the energies.
147        enum EnergyKeys { 
148            kwEStrain=1
149          , kwESlip
150          , kwEDashpot
151        };
152        // Contact model energy names in a comma separated list. The order corresponds with
153        // the order of the EnergyKeys enumerator above. 
154        QString  getEnergies() const override { 
155            return "energy-strain"
156                   ",energy-slip"
157                   ",energy-dashpot";
158        }
159        // Returns the value of the energy (base 1 - getEnergy(1) returns the estrain energy).
160        double   getEnergy(uint32 i) const override; 
161        // Returns whether or not each energy is accumulated (base 1 - getEnergyAccumulate(1) 
162        // returns wther or not the estrain energy is accumulated which is false).
163        bool     getEnergyAccumulate(uint32 i) const override;
164        // Set an energy value (base 1 - setEnergy(1) sets the estrain energy).
165        void     setEnergy(uint32 i,const double &d) override; // Base 1
166        // Activate the energy. This is only called if the energy tracking is enabled. 
167        void     activateEnergy()  override { if (energies_) return; energies_ = NEW Energies();}
168        // Returns whether or not the energy tracking has been enabled for this contact.
169        bool     getEnergyActivated() const override {return (energies_ != 0);}
170
171        // Enumerator for contact model related FISH callback events. 
172        enum FishCallEvents {
173             fActivated=0
174            ,fSlipChange
175            ,fBondBreak
176        };
177        // Contact model FISH callback event names in a comma separated list. The order corresponds with
178        // the order of the FishCallEvents enumerator above. 
179        QString  getFishCallEvents() const override { 
180            return 
181                "contact_activated"
182                ",slip_change"
183                ",bond_break"; 
184        }
185
186        // Return the specified contact model property.
187        QVariant getProperty(uint32 i,const IContact *) const override;
188        // The return value denotes whether or not the property corresponds to the global
189        // or local coordinate system (TRUE: global system, FALSE: local system). The
190        // local system is the contact-plane system (nst) defined as follows.
191        // If a vector V is expressed in the local system as (Vn, Vs, Vt), then V is
192        // expressed in the global system as {Vn*nc + Vs*sc + Vt*tc} where where nc, sc
193        // and tc are unit vectors in directions of the nst axes.
194        // This is used when rendering contact model properties that are vectors.
195        bool     getPropertyGlobal(uint32 i) const override;
196        // Set the specified contact model property, ensuring that it is of the correct type
197        // and within the correct range --- if not, then throw an exception.
198        // The return value denotes whether or not the update has affected the timestep
199        // computation (by having modified the translational or rotational tangent stiffnesses).
200        // If true is returned, then the timestep will be recomputed.
201        bool     setProperty(uint32 i,const QVariant &v,IContact *) override;
202        // The return value denotes whether or not the property is read-only
203        // (TRUE: read-only, FALSE: read-write).
204        bool     getPropertyReadOnly(uint32 i) const override;
205
206        // The return value denotes whether or not the property is inheritable
207        // (TRUE: inheritable, FALSE: not inheritable). Inheritance is provided by
208        // the endPropertyUpdated method.
209        bool     supportsInheritance(uint32 i) const override; 
210        // Return whether or not inheritance is enabled for the specified property.
211        bool     getInheritance(uint32 i) const override { assert(i<32); uint32 mask = to<uint32>(1 << i);  return (inheritanceField_ & mask) ? true : false; }
212        // Set the inheritance flag for the specified property.
213        void     setInheritance(uint32 i,bool b) override { assert(i<32); uint32 mask = to<uint32>(1 << i);  if (b) inheritanceField_ |= mask;  else inheritanceField_ &= ~mask; }
214
215        // Enumerator for contact model methods.
216        enum MethodKeys { kwAssignStiffness=1, kwStiffness, kwBond, kwUnbond, kwArea, kwResetDisp};
217        // Contact model methoid names in a comma separated list. The order corresponds with
218        // the order of the MethodKeys enumerator above.  
219        QString  getMethods() const override { return "assign-stiffness,compute-stiffness,bond,unbond,area,reset-disp";}
220        // Return a comma seprated list of the contact model method arguments (base 1).
221        QString  getMethodArguments(uint32 i) const override; 
222        // Set contact model method arguments (base 1). 
223        // The return value denotes whether or not the update has affected the timestep
224        // computation (by having modified the translational or rotational tangent stiffnesses).
225        // If true is returned, then the timestep will be recomputed.
226        bool     setMethod(uint32 i,const QVector<QVariant> &vl,IContact *con=0) override; 
227
228        // Prepare for entry into ForceDispLaw. The validate function is called when:
229        // (1) the contact is created, (2) a property of the contact that returns a true via
230        // the setProperty method has been modified and (3) when a set of cycles is executed
231        // via the {cycle N} command.
232        // Return value indicates contact activity (TRUE: active, FALSE: inactive).
233        bool    validate(ContactModelMechanicalState *state,const double &timestep) override;
234        // The endPropertyUpdated method is called whenever a surface property (with a name
235        // that matches an inheritable contact model property name) of one of the contacting
236        // pieces is modified. This allows the contact model to update its associated
237        // properties. The return value denotes whether or not the update has affected
238        // the time step computation (by having modified the translational or rotational
239        // tangent stiffnesses). If true is returned, then the time step will be recomputed.  
240        bool    endPropertyUpdated(const QString &name,const IContactMechanical *c) override;
241        // The forceDisplacementLaw function is called during each cycle. Given the relative
242        // motion of the two contacting pieces (via
243        //   state->relativeTranslationalIncrement_ (Ddn, Ddss, Ddst)
244        //   state->relativeAngularIncrement_       (Dtt, Dtbs, Dtbt)
245        //     Ddn  : relative normal-displacement increment, Ddn > 0 is opening
246        //     Ddss : relative  shear-displacement increment (s-axis component)
247        //     Ddst : relative  shear-displacement increment (t-axis component)
248        //     Dtt  : relative twist-rotation increment
249        //     Dtbs : relative  bend-rotation increment (s-axis component)
250        //     Dtbt : relative  bend-rotation increment (t-axis component)
251        //       The relative displacement and rotation increments:
252        //         Dd = Ddn*nc + Ddss*sc + Ddst*tc
253        //         Dt = Dtt*nc + Dtbs*sc + Dtbt*tc
254        //       where nc, sc and tc are unit vectors in direc. of the nst axes, respectively.
255        //       [see {Table 1: Contact State Variables} in PFC Model Components:
256        //       Contacts and Contact Models: Contact Resolution]
257        // ) and the contact properties, this function must update the contact force and
258        // moment.
259        //   The force_ is acting on piece 2, and is expressed in the local coordinate system
260        //   (defined in getPropertyGlobal) such that the first component positive denotes
261        //   compression. If we define the moment acting on piece 2 by Mc, and Mc is expressed
262        //   in the local coordinate system (defined in getPropertyGlobal), then we must use the getMechanicalContact()->updateResultingTorquesLocal(...) method to 
263        //   get the total moment. 
264        // The return value indicates the contact activity status (TRUE: active, FALSE:
265        // inactive) during the next cycle.
266        // Additional information:
267        //   * If state->activated() is true, then the contact has just become active (it was
268        //     inactive during the previous time step).
269        //   * Fully elastic behavior is enforced during the SOLVE ELASTIC command by having
270        //     the forceDispLaw handle the case of {state->canFail_ == true}.
271        bool    forceDisplacementLaw(ContactModelMechanicalState *state,const double &timestep) override;
272        bool    thermalCoupling(ContactModelMechanicalState*, ContactModelThermalState*, IContactThermal*, const double&) override;
273        
274        // The getEffectiveXStiffness functions return the translational and rotational
275        // tangent stiffnesses used to compute a stable time step. When a contact is sliding,
276        // the translational tangent shear stiffness is zero (but this stiffness reduction
277        // is typically ignored when computing a stable time step). If the contact model
278        // includes a dashpot, then the translational stiffnesses must be increased (see
279        // Potyondy (2009)).
280        //   [Potyondy, D. 'Stiffness Matrix at a Contact Between Two Clumps,' Itasca
281        //   Consulting Group, Inc., Minneapolis, MN, Technical Memorandum ICG6863-L,
282        //   December 7, 2009.]
283        DVect2  getEffectiveTranslationalStiffness() const override { return effectiveTranslationalStiffness_; }
284        DAVect  getEffectiveRotationalStiffness() const override { return effectiveRotationalStiffness_;}
285
286        // Return a new instance of the contact model. This is used in the CMAT
287        // when a new contact is created. 
288        ContactModelRBSN *clone() const override { return NEW ContactModelRBSN(); }
289        // The getActivityDistance function is called by the contact-resolution logic when
290        // the CMAT is modified. Return value is the activity distance used by the
291        // checkActivity function.
292        double              getActivityDistance() const override {return rgap_;}
293        // The isOKToDelete function is called by the contact-resolution logic when...
294        // Return value indicates whether or not the contact may be deleted.
295        // If TRUE, then the contact may be deleted when it is inactive.
296        // If FALSE, then the contact may not be deleted (under any condition).
297        bool                isOKToDelete() const override { return !isBonded(); }
298        // Zero the forces and moments stored in the contact model. This function is called
299        // when the contact becomes inactive.
300        void                resetForcesAndMoments() override { 
301            sn_F_ = DVect(0.0);
302            fictForce_ = DVect(0.0);
303            sn_M_ = DAVect(0.0);
304            dp_F(DVect(0.0)); 
305            if (energies_) {
306                energies_->estrain_ = 0.0;
307            }
308        }
309        void     setForce(const DVect &v,IContact *c) override;
310        void     setArea(const double &d) override { userArea_ = d; }
311        double   getArea() const override { return userArea_; }
312
313        // The checkActivity function is called by the contact-resolution logic when...
314        // Return value indicates contact activity (TRUE: active, FALSE: inactive).
315        bool     checkActivity(const double &gap) override { return  gap <= rgap_ || isBonded();}
316
317        // Returns the sliding state (FALSE is returned if not implemented).
318        bool     isSliding() const override { return sn_S_; }
319        // Returns the bonding state (FALSE is returned if not implemented).
320        bool     isBonded() const override { return sn_state_ >= 3; }
321        void     unbond() override { sn_state_ = 0; }
322
323        // Both of these methods are called only for contacts with facets where the wall 
324        // resolution scheme is set the full. In such cases one might wish to propagate 
325        // contact state information (e.g., shear force) from one active contact to another. 
326        // See the Faceted Wall section in the documentation. 
327        void     propagateStateInformation(IContactModelMechanical* oldCm,const CAxes &oldSystem=CAxes(),const CAxes &newSystem=CAxes()) override;
328        void     setNonForcePropsFrom(IContactModel *oldCM) override;
329           
330        /// Return the total force that the contact model holds.
331        DVect    getForce() const override;
332
333        /// Return the total moment on 1 that the contact model holds
334        DAVect   getMomentOn1(const IContactMechanical *) const override;
335
336        /// Return the total moment on 1 that the contact model holds
337        DAVect   getMomentOn2(const IContactMechanical *) const override;
338        
339        DAVect getModelMomentOn1() const override;
340        DAVect getModelMomentOn2() const override;
341
342        // Used to efficiently get properties from the contact model for the object pane.
343        // List of properties for the object pane, comma separated.
344        // All properties will be cast to doubles for comparison. No other comparisons
345        // are supported. This may not be the same as the entire property list.
346        // Return property name and type for plotting.
347        void objectPropsTypes(std::vector<std::pair<QString,InfoTypes>> *) const override;
348        // All properties cast to doubles - this is what can be compared. 
349        void objectPropValues(std::vector<double> *,const IContact *) const override;
350        
351        // Methods to get and set properties. 
352        double         sn_Ten() const { return tenTable_[0].x(); }
353        void           sn_Ten(const double &d) { tenTable_[0].rx() = d; }
354        double         sn_Coh() const { return cohTable_[0].x(); }
355        void           sn_Coh(const double &d) { cohTable_[0].rx() = d; }
356        void           sn_MCF(const double &d) { sn_mcf_=d;}
357        double         sn_cohdc() const           {return cohTable_.back().y(); }
358        double         sn_tendc() const           {return tenTable_.back().y(); }
359
360        bool     hasDamping() const {return dpProps_ ? true : false;}
361        double   dp_nratio() const {return (hasDamping() ? (dpProps_->dp_nratio_) : 0.0);}
362        void     dp_nratio(const double &d) { if(!hasDamping()) return; dpProps_->dp_nratio_=d;}
363        double   dp_sratio() const {return hasDamping() ? dpProps_->dp_sratio_: 0.0;}
364        void     dp_sratio(const double &d) { if(!hasDamping()) return; dpProps_->dp_sratio_=d;}
365        int      dp_mode() const {return hasDamping() ? dpProps_->dp_mode_: -1;}
366        void     dp_mode(int i) { if(!hasDamping()) return; dpProps_->dp_mode_=i;}
367        DVect    dp_F() const {return hasDamping() ? dpProps_->dp_F_: DVect(0.0);}
368        void     dp_F(const DVect &f) { if(!hasDamping()) return; dpProps_->dp_F_=f;}
369
370        bool    hasEnergies() const {return energies_ ? true:false;}
371        double  estrain() const {return hasEnergies() ? energies_->estrain_: 0.0;}
372        void    estrain(const double &d) { if(!hasEnergies()) return; energies_->estrain_=d;}
373        double  eslip() const {return hasEnergies() ? energies_->eslip_: 0.0;}
374        void    eslip(const double &d) { if(!hasEnergies()) return; energies_->eslip_=d;}
375        double  edashpot() const {return hasEnergies() ? energies_->edashpot_: 0.0;}
376        void    edashpot(const double &d) { if(!hasEnergies()) return; energies_->edashpot_=d;}
377
378        uint32 inheritanceField() const {return inheritanceField_;}
379        void inheritanceField(uint32 i) {inheritanceField_ = i;}
380
381        const DVect2 & effectiveTranslationalStiffness()  const          {return effectiveTranslationalStiffness_;}
382        void           effectiveTranslationalStiffness(const DVect2 &v ) {effectiveTranslationalStiffness_=v;}
383        const DAVect & effectiveRotationalStiffness()  const             {return effectiveRotationalStiffness_;}
384        void           effectiveRotationalStiffness(const DAVect &v )    {effectiveRotationalStiffness_=v;}
385
386    private:
387        // Index - used internally by PFC. Should be set to -1 in the cpp file. 
388        static int index_;
389
390        bool  FDLawBonded(ContactModelMechanicalState *state, const double &timestep);
391        bool  FDLawUnBonded(ContactModelMechanicalState *state, const double &timestep);
392
393        // Structure to compute stiffness
394        struct StiffData {
395            DVect2 trans_ = DVect2(0.0);
396            DAVect ang_   = DAVect(0.0);
397            double reff_ = 0.0;
398        };
399
400        // Structure to store the energies. 
401        struct Energies {
402            double estrain_  = 0.0;   // elastic energy  
403            double eslip_    = 0.0;   // work dissipated by friction 
404            double edashpot_ = 0.0;   // work dissipated by dashpots
405        };
406
407        // Structure to store dashpot quantities. 
408        struct dpProps {
409            double dp_nratio_ = 0.0;         // normal viscous critical damping ratio
410            double dp_sratio_ = 0.0;         // shear  viscous critical damping ratio
411            int    dp_mode_   = 0;           // for viscous mode (0-4) 0 = dashpots, 1 = tensile limit, 2 = shear limit, 3 = limit both
412            DVect  dp_F_      = DVect(0.0);  // Force in the dashpots
413        };
414
415        bool   updateKn(const IContactMechanical *con);
416        bool   updateKs(const IContactMechanical *con);
417        bool   updateFric(const IContactMechanical *con);
418
419        StiffData computeStiffData(ContactModelMechanicalState *state) const;
420        DVect3    computeGeomData(const DVect2 &e1c,const DVect2 &e2c) const;
421        DVect2    SMax(const DVect2 &e1c,const DVect2 &e2c) const; // Maximum stress (tensile,shear) at bond periphery
422        double    shearStrength(const double &pbArea) const;      // Bond shear strength
423        double    strainEnergy(double kn, double ks, double kb, double kt) const;
424
425        void      updateStiffness(ContactModelMechanicalState *state);
426
427        // Contact model inheritance fields.
428        uint32 inheritanceField_;
429
430        // Effective translational stiffness.
431        DVect2  effectiveTranslationalStiffness_ = DVect2(0.0); 
432        DAVect  effectiveRotationalStiffness_ = DAVect(0.0);      // (Twisting,Bending,Bending) Rotational stiffness (twisting always 0)
433
434        // linear model properties
435        DVect       fictForce_ = DVect(0.0);// Ficticous force to be added 
436        DVect       sn_F_ = DVect(0.0);     // Force carried in the model
437        DVect2      sn_sdisp_ = DVect2(0.0); // Accumulated total shear displacement
438                                            // The x component holds the current slip
439        DAVect      sn_M_ = DAVect(0.0);    // moment (bending + twisting in 3D)         
440        DAVect      kRot_ = DAVect(0.0);    // Translational degrees of freedom
441        double      kTran_ = 0.0;           // Translational degrees of freedom
442        double      kRatio_ = 1.0;          // Ratio of normal to shear stiffness
443        double      E_ = 0.0;               // Young's modulus
444        double      poisson_ = 0.0;         // Poisson ratio
445        double      fric_ = 0.0;            // Coulomb friction coefficient
446        double      sn_bmul_ = 1.0;         // Bending friction multiplier
447        double      sn_tmul_ = 1.0;         // Twisting friction  multiplier
448        double      sn_rmul_ = 1.0;         // Radius multiplier
449        double      userArea_ = 0.0;        // Area as specified by the user 
450        double      rgap_ = 0.0;            // Reference gap
451        double      sn_fa_  = 0.0;          // friction angle (stored as tan(dDegrad*fa))
452        double      sn_mcf_ = 1.0;          // moment contribution factor
453        double      sn_dil_ = 0.0;          // Dilation (stored as tan(dDegrad*dil))
454        double      sn_dilzero_ = 0.0;      // Dilation zero
455        double      transTen_ = 0.0;        // Force for transition from tensile to compression
456        double      sn_elong_ = 0.0;        // Elongation (or normal displacement since softening)
457        double      sn_ndisp_ = 0.0;        // Accumulated normal displacement
458        double      sn_cohres_ = 0.0;       // Residual cohesion
459        double      sn_por_ = 0.0;          // Pore Pressure
460        uint        sn_mode_ = 0;           // specifies absolute (0) or incremental (1) behavior for the the normal force
461        uint        sn_state_  = 0;         // bond mode - 0 (NBNF), 1 (NBFT), 2 (NBFS), 3 (B)
462        int         sn_tabPos_ = 0;         // Position in the table for query
463        bool        poisOffDiag_ = false;   // Add the off diagonal terms 
464        bool        sn_S_ = false;          // The current slip state
465        bool        sn_BS_ = false;         // The bending  slip state
466        bool        sn_TS_ = false;         // The twisting slip state
467        bool        forceSet_ = false;      // About setting the force
468        bool        sn_heal_ = false;       // Healing behavior
469
470        std::vector<DVect2> tenTable_ = { DVect2(0) };     
471        std::vector<DVect2> cohTable_ = { DVect2(0) };
472
473        dpProps *   dpProps_ = nullptr;     // The viscous properties
474
475        Energies *   energies_ = nullptr;   // The energies
476
477    };
478} // namespace cmodelsxd
479// EoF

Top

contactmodelrbsn.cpp

   1// contactmodelrbsn.cpp
   2#include "contactmodelrbsn.h"
   3
   4#include "module/interface/icontactmechanical.h"
   5#include "module/interface/icontact.h"
   6#include "module/interface/ipiece.h"
   7#include "module/interface/ibody.h"
   8#include "module/interface/ifishcalllist.h"
   9
  10#include "utility/src/tptr.h"
  11#include "shared/src/mathutil.h"
  12
  13#include "kernel/interface/iprogram.h"
  14#include "module/interface/icontactthermal.h"
  15#include "contactmodel/src/contactmodelthermal.h"
  16#include "../version.txt"
  17#include "fish/src/parameter.h"
  18
  19#ifdef RBSN_LIB
  20    int __stdcall DllMain(void *,unsigned, void *) {
  21        return 1;
  22    }
  23
  24    extern "C" EXPORT_TAG const char *getName() {
  25#if DIM==3
  26        return "contactmodelmechanical3drbsn";
  27#else
  28        return "contactmodelmechanical2drbsn";
  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::ContactModelRBSN *m = NEW cmodelsxd::ContactModelRBSN();
  42        return (void *)m;
  43    }
  44#endif
  45
  46namespace cmodelsxd {
  47    static const uint32 KnMask      = 0x00000002; // Base 1!
  48    static const uint32 KsMask      = 0x00000004;
  49    static const uint32 FricMask    = 0x00000008;
  50
  51    using namespace itasca;
  52
  53    int ContactModelRBSN::index_ = -1;
  54    uint32 ContactModelRBSN::getMinorVersion() const { return MINOR_VERSION;  }
  55
  56    ContactModelRBSN::ContactModelRBSN() : inheritanceField_(KnMask|KsMask|FricMask) {
  57    }
  58    
  59    ContactModelRBSN::ContactModelRBSN(const ContactModelRBSN &m) noexcept {
  60        inheritanceField(KnMask|KsMask|FricMask);
  61        this->copy(&m);
  62    }
  63    
  64    const ContactModelRBSN & ContactModelRBSN::operator=(const ContactModelRBSN &m) {
  65        inheritanceField(KnMask|KsMask|FricMask);
  66        this->copy(&m);
  67        return *this;
  68    }
  69    
  70    void ContactModelRBSN::addToStorage(poly::vector<ContactModelMechanical> *s,int ww) { 
  71        s->addToStorage<ContactModelRBSN>(*this,ww);
  72    }
  73    
  74    ContactModelRBSN::~ContactModelRBSN() {
  75        // Make sure to clean up after yourself!
  76        if (dpProps_)
  77            delete dpProps_;
  78        if (energies_)
  79            delete energies_;
  80    }
  81
  82    void ContactModelRBSN::archive(ArchiveStream &stream) {
  83        if (stream.getRestoreVersion() > 4) {
  84            // New version
  85            stream & fictForce_;
  86            stream & sn_F_;
  87            stream & sn_sdisp_;
  88            stream & sn_M_;
  89            stream & kRot_;
  90            stream & kTran_;
  91            stream & kRatio_;
  92            stream & E_;
  93            stream & poisson_;
  94            stream & fric_;
  95            stream & sn_bmul_;
  96            stream & sn_tmul_;
  97            stream & sn_rmul_;
  98            stream & userArea_;
  99            stream & rgap_;
 100            stream & sn_fa_;
 101            stream & sn_mcf_;
 102            stream & sn_dil_;
 103            stream & sn_dilzero_;
 104            stream & transTen_;
 105            stream & sn_elong_;
 106            stream & sn_ndisp_;
 107            stream & sn_mode_;
 108            stream & sn_state_;
 109            stream & poisOffDiag_;
 110            stream & sn_S_;
 111            stream & sn_BS_;
 112            stream & sn_TS_;
 113            stream & forceSet_;
 114            stream & sn_heal_;
 115            stream & tenTable_;
 116            stream & cohTable_;
 117            stream & inheritanceField_;
 118            stream & effectiveTranslationalStiffness_;
 119            stream & effectiveRotationalStiffness_;
 120            if (stream.getArchiveState()==ArchiveStream::Save) {
 121                bool b = false;
 122                if (dpProps_) {
 123                    b = true;
 124                    stream & b;
 125                    stream & dpProps_->dp_nratio_;
 126                    stream & dpProps_->dp_sratio_;
 127                    stream & dpProps_->dp_mode_;
 128                    stream & dpProps_->dp_F_;
 129                }
 130                else
 131                    stream & b;
 132                b = false;
 133                if (energies_) {
 134                    b = true;
 135                    stream & b;
 136                    stream & energies_->estrain_;
 137                    stream & energies_->eslip_;
 138                    stream & energies_->edashpot_;
 139                }
 140                else
 141                    stream & b;
 142            } else {
 143                bool b(false);
 144                stream & b;
 145                if (b) {
 146                    if (!dpProps_)
 147                        dpProps_ = NEW dpProps();
 148                    stream & dpProps_->dp_nratio_;
 149                    stream & dpProps_->dp_sratio_;
 150                    stream & dpProps_->dp_mode_;
 151                    stream & dpProps_->dp_F_;
 152                }
 153                stream & b;
 154                if (b) {
 155                    if (!energies_)
 156                        energies_ = NEW Energies();
 157                    stream & energies_->estrain_;
 158                    stream & energies_->eslip_;
 159                    stream & energies_->edashpot_;
 160                }
 161            }
 162
 163            if (stream.getArchiveState() == ArchiveStream::Save || stream.getRestoreVersion() > 5) {
 164                stream & sn_tabPos_;
 165                stream & sn_cohres_;
 166            }
 167
 168            if (stream.getArchiveState() == ArchiveStream::Save || stream.getRestoreVersion() > 6)
 169                stream & sn_por_;
 170                
 171        } else {
 172            // Backward compatibility
 173            stream & kTran_;
 174            stream & E_;
 175            stream & kRot_;
 176            stream & fictForce_;
 177            stream & poisson_;
 178            stream & fric_;
 179            stream & sn_mode_;
 180            stream & sn_F_;
 181            stream & sn_M_;
 182            stream & sn_S_;
 183            stream & sn_BS_;
 184            stream & sn_TS_;
 185            stream & sn_rmul_;
 186
 187            bool b(false);
 188            stream & b;
 189            if (b) {
 190                if (!dpProps_)
 191                    dpProps_ = NEW dpProps();
 192                stream & dpProps_->dp_nratio_;
 193                stream & dpProps_->dp_sratio_;
 194                stream & dpProps_->dp_mode_;
 195                stream & dpProps_->dp_F_;
 196            }
 197            stream & b;
 198            if (b) {
 199                if (!energies_)
 200                    energies_ = NEW Energies();
 201                stream & energies_->estrain_;
 202                stream & energies_->eslip_;
 203                stream & energies_->edashpot_;
 204            }
 205            stream & b;
 206            if (b) {
 207                int vi = 0;
 208                stream & vi;
 209                sn_state_ = abs(vi);
 210                double val = 0.0;
 211                stream & val;
 212                tenTable_[0].rx() = val;
 213                val = 0.0;
 214                stream & val;
 215                cohTable_[0].rx() = val;
 216                stream & sn_fa_;
 217                stream & sn_mcf_;
 218                stream & val;
 219                stream & val;
 220                stream & val;
 221                stream & val;
 222                Quat q;
 223                stream & q;
 224                stream & val;
 225                stream & val;
 226            }
 227
 228            stream & inheritanceField_;
 229            stream & effectiveTranslationalStiffness_;
 230            stream & effectiveRotationalStiffness_;
 231
 232            stream & sn_bmul_;
 233            stream & sn_tmul_;
 234            stream & userArea_;
 235            stream & rgap_;
 236
 237            if (stream.getRestoreVersion() > 1)
 238                stream & kRatio_;
 239
 240            if (stream.getRestoreVersion() > 2) {
 241                uint32 v;
 242                stream & v;
 243                poisOffDiag_ = v == 0 ? false : true;
 244            }
 245
 246            if (stream.getArchiveState() == ArchiveStream::Save || stream.getRestoreVersion() > 3)
 247                stream & forceSet_;
 248
 249        }
 250    }
 251
 252    void ContactModelRBSN::copy(const ContactModel *cm) {
 253        // Copy all of the contact model properties. Used in the CMAT
 254        // when a new contact is created.
 255        ContactModelMechanical::copy(cm);
 256        const ContactModelRBSN *in = dynamic_cast<const ContactModelRBSN*>(cm);
 257        if (!in) throw std::runtime_error("Internal error: contact model dynamic cast failed.");
 258        fictForce_ = in->fictForce_;
 259        sn_F_ = in->sn_F_;
 260        sn_sdisp_ = in->sn_sdisp_;
 261        sn_M_ = in->sn_M_;
 262        kRot_ = in->kRot_;
 263        kTran_ = in->kTran_;
 264        kRatio_ = in->kRatio_;
 265        E_ = in->E_;
 266        poisson_ = in->poisson_;
 267        fric_ = in->fric_;
 268        sn_bmul_ = in->sn_bmul_;
 269        sn_tmul_ = in->sn_tmul_;
 270        sn_rmul_ = in->sn_rmul_;
 271        userArea_ = in->userArea_;
 272        rgap_ = in->rgap_;
 273        sn_fa_ = in->sn_fa_;
 274        sn_mcf_ = in->sn_mcf_;
 275        sn_dil_ = in->sn_dil_;
 276        sn_dilzero_ = in->sn_dilzero_;
 277        transTen_ = in->transTen_;
 278        sn_elong_ = in->sn_elong_;
 279        sn_ndisp_ = in->sn_ndisp_;
 280        sn_mode_ = in->sn_mode_;
 281        sn_state_ = in->sn_state_;
 282        poisOffDiag_ = in->poisOffDiag_;
 283        sn_S_ = in->sn_S_;
 284        sn_BS_ = in->sn_BS_;
 285        sn_TS_ = in->sn_TS_;
 286        forceSet_ = in->forceSet_;
 287        sn_heal_ = in->sn_heal_;
 288        tenTable_ = in->tenTable_;
 289        cohTable_ = in->cohTable_;
 290        sn_por_ = in->sn_por_;
 291        if (in->hasDamping()) {
 292            if (!dpProps_)
 293                dpProps_ = NEW dpProps();
 294            dp_nratio(in->dp_nratio());
 295            dp_sratio(in->dp_sratio());
 296            dp_mode(in->dp_mode());
 297            dp_F(in->dp_F());
 298        }
 299        if (in->hasEnergies()) {
 300            if (!energies_)
 301                energies_ = NEW Energies();
 302            estrain(in->estrain());
 303            eslip(in->eslip());
 304            edashpot(in->edashpot());
 305        }
 306        inheritanceField(in->inheritanceField());
 307        effectiveTranslationalStiffness(in->effectiveTranslationalStiffness());
 308        effectiveRotationalStiffness(in->effectiveRotationalStiffness());
 309    }
 310
 311
 312    QVariant ContactModelRBSN::getProperty(uint32 i,const IContact *con) const {
 313        // Return the property. The IContact pointer is provided so that
 314        // more complicated properties, depending on contact characteristics,
 315        // can be calcualted.
 316        // The IContact pointer may be a nullptr!
 317        const IContactMechanical *c(convert_getcast<IContactMechanical>(con));
 318        QVariant var;
 319        switch (i) {
 320        case kwKn:       return kTran_;
 321        case kwKs:       return kTran_ / kRatio_;
 322        case kwKrot:     var.setValue(kRot_); return var;
 323        case kwFric:     return fric_;
 324        case kwBMul:     return sn_bmul_;
 325        case kwTMul:     return sn_tmul_;
 326        case kwSNMode:   return sn_mode_;
 327        case kwSNF:      var.setValue(sn_F_); return var;
 328        case kwSNM:      var.setValue(sn_M_); return var;
 329        case kwSNS:      return sn_S_;
 330        case kwSNBS:     return sn_BS_;
 331        case kwSNTS:     return sn_TS_;
 332        case kwPoisDiag: return poisOffDiag_ == false ? 0 : 1;
 333        case kwSNRMul:   return sn_rmul_;
 334        case kwSNRadius: {
 335            if (!c) return 0.0;
 336            double Cmax1 = c->getEnd1Curvature().y();
 337            double Cmax2 = c->getEnd2Curvature().y();
 338            if (!userArea_)
 339                return sn_rmul_ * 1.0 / std::max(Cmax1, Cmax2);
 340            else {
 341#ifdef THREED
 342                double rad = std::sqrt(userArea_ / dPi);
 343#else
 344                double rad = userArea_ / 2.0;
 345#endif
 346                return rad;
 347            }
 348
 349        }
 350        case kwEmod:      return E_;
 351        case kwKRatio:    return kRatio_;
 352        case kwDpNRatio:  return dpProps_ ? dpProps_->dp_nratio_ : 0;
 353        case kwDpSRatio:  return dpProps_ ? dpProps_->dp_sratio_ : 0;
 354        case kwDpMode:    return dpProps_ ? dpProps_->dp_mode_ : 0;
 355        case kwDpF: {
 356                dpProps_ ? var.setValue(dpProps_->dp_F_) : var.setValue(DVect(0.0));
 357                return var;
 358            }
 359        case kwSNState:     return sn_state_;
 360        case kwSNTStr:      return sn_Ten();
 361        case kwSNSStr: {
 362            if (!c) return 0.0;
 363            double area = computeGeomData(c->getEnd1Curvature(),c->getEnd2Curvature()).x();
 364            return shearStrength(area);
 365        }
 366        case kwSNCoh:       return cohTable_[0].x();
 367        case kwSNFa:        return std::atan(sn_fa_)/dDegrad;
 368        case kwSNMCF:       return sn_mcf_;
 369        case kwSNSig: {
 370            if (!c) return 0.0;
 371            return SMax(c->getEnd1Curvature(),c->getEnd2Curvature()).x();
 372        }
 373        case kwSNTau: {
 374            if (!c) return 0.0;
 375            return SMax(c->getEnd1Curvature(),c->getEnd2Curvature()).y();
 376        }
 377        case kwSNArea: {
 378                if (userArea_) return userArea_;
 379                if (!c)
 380                    return 0.0;
 381                return computeGeomData(c->getEnd1Curvature(),c->getEnd2Curvature()).x();
 382            }
 383        case kwUserArea:
 384            return userArea_;
 385        case kwRGap:
 386            return rgap_;
 387        case kwPForce:  var.setValue(fictForce_); return var;
 388        case kwPois: return poisson_;
 389        case kwSnCohRes :   return sn_cohres_;
 390        //case kwSnTenRes :   return sn_tenres();
 391        case kwSnDil :      return std::atan(sn_dil_)/dDegrad;
 392        case kwSnDilZ :     return sn_dilzero_;
 393        case kwSnNormDisp:  return sn_ndisp_;
 394        case kwSnShearDisp:  var.setValue(sn_sdisp_); return var;
 395        case kwSnCohDc :    return sn_cohdc();
 396        case kwSnTenDc :    return sn_tendc();
 397        case kwSnHeal :     return sn_heal_;
 398        case kwTenTable:
 399            if (sn_tabPos_ < tenTable_.size())
 400                if (sn_tabPos_ == 0)
 401                    var.setValue(DVect2(1,0));
 402                else
 403                    var.setValue(tenTable_[sn_tabPos_]);
 404            else
 405                var.setValue(DVect2(0,0));
 406            return var;
 407        case kwCohTable:
 408            if (sn_tabPos_ < cohTable_.size())
 409                if (sn_tabPos_ == 0)
 410                    var.setValue(DVect2(1,0));
 411                else
 412                    var.setValue(cohTable_[sn_tabPos_]);
 413            else
 414                var.setValue(DVect2(0,0));
 415            return var;
 416        case kwTablePos: return sn_tabPos_+1;
 417        case kwPorP: return sn_por_;
 418        case kwStressNorm: {
 419                if (!c)
 420                    return 0.0;
 421                return SMax(c->getEnd1Curvature(),c->getEnd2Curvature()).x() + sn_por_;
 422            }
 423        }
 424        assert(0);
 425        return QVariant();
 426    }
 427
 428    bool ContactModelRBSN::getPropertyGlobal(uint32 i) const {
 429        // Returns whether or not a property is held in the global axis system (TRUE)
 430        // or the local system (FALSE). Used by the plotting logic.
 431        switch (i) {
 432        case kwSNF:
 433        case kwSNM:
 434        case kwDpF:
 435            return false;
 436        }
 437        return true;
 438    }
 439
 440    bool ContactModelRBSN::setProperty(uint32 i,const QVariant &v,IContact *) {
 441        // Set a contact model property. Return value indicates that the timestep
 442        // should be recalculated.
 443        dpProps dp;
 444        switch (i) {
 445        case kwKn: {
 446                if (!v.canConvert<double>())
 447                    throw Exception("kn must be a double.");
 448                double val(v.toDouble());
 449                if (val<0.0)
 450                    throw Exception("Negative kn not allowed.");
 451                kTran_ = val;
 452                return true;
 453            }
 454        case kwKs: {
 455                if (!v.canConvert<double>())
 456                    throw Exception("ks must be a double.");
 457                double val(v.toDouble());
 458                if (val<0.0)
 459                    throw Exception("Negative ks not allowed.");
 460                if (!kTran_)
 461                    kTran_ = val;
 462                else
 463                    kRatio_ = kTran_ / val;
 464                return true;
 465            }
 466        case kwKrot: {
 467                DAVect val(0.0);
 468#ifdef TWOD
 469                if (!v.canConvert<DAVect>() && !v.canConvert<double>())
 470                    throw Exception("krot must be an angular vector.");
 471                if (v.canConvert<DAVect>())
 472                    val = DAVect(v.value<DAVect>());
 473                else
 474                    val = DAVect(v.toDouble());
 475#else
 476                if (!v.canConvert<DAVect>() && !v.canConvert<DVect>())
 477                    throw Exception("krot must be an angular vector.");
 478                if (v.canConvert<DAVect>())
 479                    val = DAVect(v.value<DAVect>());
 480                else
 481                    val = DAVect(v.value<DVect>());
 482#endif
 483                kRot_ = val;
 484                return false;
 485            }
 486        case kwFric: {
 487                if (!v.canConvert<double>())
 488                    throw Exception("fric must be a double.");
 489                double val(v.toDouble());
 490                if (val<0.0)
 491                    throw Exception("Negative fric not allowed.");
 492                fric_ = val;
 493                //if (!sn_fa_)
 494                //    sn_fa_ = fric_;
 495                return false;
 496            }
 497        case kwBMul: {
 498                if (!v.canConvert<double>())
 499                    throw Exception("sn_bmul must be a double.");
 500                double val(v.toDouble());
 501                if (val<0.0)
 502                    throw Exception("Negative sn_bmul not allowed.");
 503                sn_bmul_ = val;
 504                return false;
 505            }
 506        case kwTMul: {
 507                if (!v.canConvert<double>())
 508                    throw Exception("sn_tmul must be a double.");
 509                double val(v.toDouble());
 510                if (val<0.0)
 511                    throw Exception("Negative st_bmul not allowed.");
 512                sn_tmul_ = val;
 513                return false;
 514            }
 515        case kwSNMode: {
 516                if (!v.canConvert<uint32>())
 517                    throw Exception("sn_mode must be 0 (absolute) or 1 (incremental).");
 518                double val(v.toUInt());
 519                if (val>1)
 520                    throw Exception("sn_mode must be 0 (absolute) or 1 (incremental).");
 521                sn_mode_ = val;
 522                return false;
 523            }
 524        case kwSNRMul: {
 525                if (!v.canConvert<double>())
 526                    throw Exception("rmul must be a double.");
 527                double val(v.toDouble());
 528                if (val<0.0)
 529                    throw Exception("Negative rmul not allowed.");
 530                sn_rmul_ = val;
 531                return false;
 532            }
 533        case kwSNF: {
 534                if (!v.canConvert<DVect>())
 535                    throw Exception("sn_force must be a vector.");
 536                DVect val(v.value<DVect>());
 537                sn_F_ = val;
 538                return false;
 539            }
 540        case kwSNM: {
 541                DAVect val(0.0);
 542#ifdef TWOD
 543                if (!v.canConvert<DAVect>() && !v.canConvert<double>())
 544                    throw Exception("res_moment must be an angular vector.");
 545                if (v.canConvert<DAVect>())
 546                    val = DAVect(v.value<DAVect>());
 547                else
 548                    val = DAVect(v.toDouble());
 549#else
 550                if (!v.canConvert<DAVect>() && !v.canConvert<DVect>())
 551                    throw Exception("res_moment must be an angular vector.");
 552                if (v.canConvert<DAVect>())
 553                    val = DAVect(v.value<DAVect>());
 554                else
 555                    val = DAVect(v.value<DVect>());
 556#endif
 557                sn_M_ = val;
 558                return false;
 559            }
 560        case kwDpNRatio: {
 561                if (!v.canConvert<double>())
 562                    throw Exception("dp_nratio must be a double.");
 563                double val(v.toDouble());
 564                if (val<0.0)
 565                    throw Exception("Negative dp_nratio not allowed.");
 566                if (val == 0.0 && !dpProps_)
 567                    return false;
 568                if (!dpProps_)
 569                    dpProps_ = NEW dpProps();
 570                dpProps_->dp_nratio_ = val;
 571                return true;
 572            }
 573        case kwDpSRatio: {
 574                if (!v.canConvert<double>())
 575                    throw Exception("dp_sratio must be a double.");
 576                double val(v.toDouble());
 577                if (val<0.0)
 578                    throw Exception("Negative dp_sratio not allowed.");
 579                if (val == 0.0 && !dpProps_)
 580                    return false;
 581                if (!dpProps_)
 582                    dpProps_ = NEW dpProps();
 583                dpProps_->dp_sratio_ = val;
 584                return true;
 585            }
 586        case kwDpMode: {
 587                if (!v.canConvert<int>())
 588                    throw Exception("The viscous mode dp_mode must be 0, 1, 2, or 3.");
 589                int val(v.toInt());
 590                if (val == 0 && !dpProps_)
 591                    return false;
 592                if (val < 0 || val > 3)
 593                    throw Exception("The viscous mode dp_mode must be 0, 1, 2, or 3.");
 594                if (!dpProps_)
 595                    dpProps_ = NEW dpProps();
 596                dpProps_->dp_mode_ = val;
 597                return false;
 598            }
 599        case kwDpF: {
 600                if (!v.canConvert<DVect>())
 601                    throw Exception("dp_force must be a vector.");
 602                DVect val(v.value<DVect>());
 603                if (val.fsum() == 0.0 && !dpProps_)
 604                    return false;
 605                if (!dpProps_)
 606                    dpProps_ = NEW dpProps();
 607                dpProps_->dp_F_ = val;
 608                return false;
 609            }
 610        case kwSNTStr: {
 611                if (!v.canConvert<double>())
 612                    throw Exception("sn_ten must be a double.");
 613                double val(v.toDouble());
 614                if (val < 0.0)
 615                    throw Exception("Negative sn_ten not allowed.");
 616                tenTable_[0].rx() = val;
 617                return false;
 618            }
 619        case kwSNCoh: {
 620                if (!v.canConvert<double>())
 621                    throw Exception("sn_coh must be a double.");
 622                double val(v.toDouble());
 623                if (val<0.0)
 624                    throw Exception("Negative pb_coh not allowed.");
 625                cohTable_[0].rx() = val;
 626                return false;
 627            }
 628        case kwSNFa: {
 629                if (!v.canConvert<double>())
 630                    throw Exception("sn_fa must be a double.");
 631                double val(v.toDouble());
 632                if (val<0.0)
 633                    throw Exception("Negative sn_fa not allowed.");
 634                if (val >= 90.0)
 635                    throw Exception("sn_fa must be lower than 90.0 degrees.");
 636                sn_fa_ = std::tan(val*dDegrad);
 637                if (!fric_)
 638                    fric_ = sn_fa_;
 639                return false;
 640            }
 641        case kwSNMCF: {
 642                if (!v.canConvert<double>())
 643                    throw Exception("sn_mcf must be a double.");
 644                double val(v.toDouble());
 645                if (val<0.0)
 646                    throw Exception("Negative sn_mcf not allowed.");
 647                if (val > 1.0)
 648                    throw Exception("sn_mcf must be lower or equal to 1.0.");
 649                sn_mcf_ = val;
 650                return false;
 651            }
 652        case kwSNArea:
 653        case kwUserArea: {
 654                if (!v.canConvert<double>())
 655                    throw Exception("area must be a double.");
 656                double val(v.toDouble());
 657                if (val < 0.0)
 658                    throw Exception("Negative area not allowed.");
 659                if (userArea_ && val) {
 660                    double rat = userArea_ / val;
 661                    kTran_ *=  rat;
 662                    kRot_ *= rat;
 663                }
 664                userArea_ = val;
 665                return true;
 666            }
 667        case kwRGap: {
 668                if (!v.canConvert<double>())
 669                    throw Exception("Reference gap must be a double.");
 670                double val(v.toDouble());
 671                rgap_ = val;
 672                return false;
 673            }
 674        case kwPois: {
 675                if (!v.canConvert<double>())
 676                    throw Exception("Reference poisson must be a double.");
 677                double val(v.toDouble());
 678                poisson_ = val;
 679                return false;
 680            }
 681        case kwPoisDiag: {
 682                if (!v.canConvert<uint32>())
 683                    throw Exception("Reference diagonal must be an integer.");
 684                uint32 b(v.toUInt());
 685                if (b > 1)
 686                    throw Exception("diagonal must be 0 (diagonal terms only) or 1 (all terms).");
 687                poisOffDiag_ = b == 0 ? false : true;
 688                return false;
 689            }
 690        case kwSnCohRes: {
 691                bool ok;
 692                double val(v.toDouble(&ok));
 693                if (!ok || val<0.0)
 694                    throw Exception("sn_cohres must be a positive double.");
 695                sn_cohres_ = val;
 696                return false;
 697            }
 698        case kwSnDil: {
 699                bool ok;
 700                double val(v.toDouble(&ok));
 701                if (!ok || val<0.0)
 702                    throw Exception("sn_dil must be a positive double.");
 703                sn_dil_ = std::tan(val*dDegrad);
 704                return false;
 705            }
 706        case kwSnDilZ: {
 707                bool ok;
 708                double val(v.toDouble(&ok));
 709                if (!ok || val<0.0)
 710                    throw Exception("sn_dil_zero must be a positive double.");
 711                sn_dilzero_ = val;
 712                return false;
 713            }
 714        case kwSnNormDisp: {
 715                bool ok;
 716                double val(v.toDouble(&ok));
 717                if (!ok)
 718                    throw Exception("sn_ndisp must be a positive double.");
 719                sn_ndisp_ = val;
 720                return false;
 721            }
 722        case kwSnShearDisp: {
 723                if (!v.canConvert<DVect2>())
 724                    throw Exception("sn_sdisp must be a vector.");
 725                DVect2 val(v.value<DVect2>());
 726                sn_sdisp_ = val;
 727                return false;
 728            }
 729        case kwSnCohDc: {
 730                bool ok;
 731                double val(v.toDouble(&ok));
 732                if (!ok || val<0.0)
 733                    throw Exception("sn_cohdc must be a positive double.");
 734                if (cohTable_.size() == 1)
 735                    cohTable_.push_back(DVect2(0,val));
 736                else {
 737                    cohTable_.back().ry() = val;
 738                    std::sort(cohTable_.begin(),cohTable_.end(), [](const DVect2& a, const DVect2& b) { return a.y() < b.y(); } );
 739                    while (cohTable_.back().y() > val)
 740                        cohTable_.pop_back();
 741                }
 742                if (sn_state_ == 0)
 743                    sn_state_ = 6;
 744                return false;
 745            }
 746        case kwSnTenDc: {
 747                bool ok;
 748                double val(v.toDouble(&ok));
 749                if (!ok || val<0.0)
 750                    throw Exception("sn_tendc must be a positive double.");
 751                if (tenTable_.size() == 1)
 752                    tenTable_.push_back(DVect2(0,val));
 753                else {
 754                    tenTable_.back().ry() = val;
 755                    std::sort(tenTable_.begin(),tenTable_.end(), [](const DVect2& a, const DVect2& b) { return a.y() < b.y(); } );
 756                    while (tenTable_.back().y() > val)
 757                        tenTable_.pop_back();
 758                }
 759                return false;
 760            }
 761        case kwSnHeal: {
 762                bool ok;
 763                int val(v.toInt(&ok));
 764                if (!ok || (val != 0 && val != 1))
 765                    throw Exception("sn_heal must be 0 or 1.");
 766                sn_heal_ = val == 0 ? false : true;
 767                return false;
 768            }
 769        case kwTenTable: {
 770                if (!v.canConvert<DVect2>())
 771                    throw Exception("sn_tentab entry must be a strength and distance.");
 772                DVect2 vl(v.value<DVect2>());
 773                if (vl.x() < 0 || vl.y() < 0)
 774                    throw Exception("The sn_tentab entries must be positive.");
 775                if (vl.y() == 0)
 776                    throw Exception("Use sn_ten to set the tensile strength at 0 elongation.");
 777                tenTable_.push_back(vl);
 778                std::sort(tenTable_.begin(),tenTable_.end(), [](const DVect2& a, const DVect2& b) { return a.y() < b.y(); } );
 779            }
 780            return false;
 781        case kwCohTable: {
 782                if (!v.canConvert<DVect2>())
 783                    throw Exception("sn_cohtab entry must be a strength and distance.");
 784                DVect2 vl(v.value<DVect2>());
 785                if (vl.x() < 0 || vl.y() < 0)
 786                    throw Exception("The sn_cohtab entries must be positive.");
 787                if (vl.y() == 0)
 788                    throw Exception("Use sn_coh to set the cohesive strength at 0 elongation.");
 789                cohTable_.push_back(vl);
 790                std::sort(cohTable_.begin(),cohTable_.end(), [](const DVect2& a, const DVect2& b) { return a.y() < b.y(); } );
 791            }
 792            return false;
 793        case kwTablePos: {
 794                bool ok;
 795                int val(v.toInt(&ok));
 796                if (!ok || val < 1)
 797                    throw Exception("sn_tabpos must be 1 or greater.");
 798                sn_tabPos_ = val - 1;
 799                return false;
 800            }
 801        case kwPorP: {
 802                if (!v.canConvert<double>())
 803                    throw Exception("sn_porp must be a double.");
 804                double val = v.toDouble();
 805                sn_por_ = val;
 806                return true;
 807            }
 808        }
 809        return false;
 810    }
 811
 812    bool ContactModelRBSN::getPropertyReadOnly(uint32 i) const {
 813        // Returns TRUE if a property is read only or FALSE otherwise.
 814        switch (i) {
 815        case kwDpF:
 816        case kwSNS:
 817        case kwSNBS:
 818        case kwSNTS:
 819        case kwEmod:
 820        case kwKRatio:
 821        case kwSNState:
 822        case kwSNRadius:
 823        case kwSNSStr:
 824        case kwSNSig:
 825        case kwSNTau:
 826        case kwPForce:
 827        case kwStressNorm:
 828            return true;
 829        default:
 830            break;
 831        }
 832        return false;
 833    }
 834
 835    bool ContactModelRBSN::supportsInheritance(uint32 i) const {
 836        // Returns TRUE if a property supports inheritance or FALSE otherwise.
 837        switch (i) {
 838        case kwKn:
 839        case kwKs:
 840        case kwFric:
 841            return true;
 842        default:
 843            break;
 844        }
 845        return false;
 846    }
 847
 848    QString  ContactModelRBSN::getMethodArguments(uint32 i) const {
 849        // Return a list of contact model method argument names.
 850        switch (i) {
 851        case kwAssignStiffness:
 852            return "kn,kratio";
 853        case kwStiffness:
 854            return "emod,poisson";
 855        case kwBond:
 856            return "gap";
 857        case kwUnbond:
 858            return "gap";
 859        case kwArea:
 860        case kwResetDisp:
 861            return QString();
 862        }
 863        assert(0);
 864        return QString();
 865    }
 866
 867    bool ContactModelRBSN::setMethod(uint32 i,const QVector<QVariant> &vl,IContact *con) {
 868        // Apply the specified method.
 869        IContactMechanical *c(convert_getcast<IContactMechanical>(con));
 870        switch (i) {
 871        case kwAssignStiffness: {
 872                poisson_ = 0.0;
 873                if (vl.at(0).isNull())
 874                    throw Exception("Argument kn must be specified with method assign-stiffness in contact model {0}.",getName());
 875                double knpa = vl.at(0).toDouble();
 876                if (knpa<=0.0)
 877                    throw Exception("Negative or zero kn not allowed in contact model {0}.",getName());
 878                if (vl.at(1).isNull())
 879                    throw Exception("Argument kratio must be specified with method assign-stiffness in contact model {0}.",getName());
 880                kRatio_ = vl.at(1).toDouble();
 881                if (kRatio_<0.0) {
 882                    kRatio_ = 0.0;
 883                    throw Exception("Negative kratio not allowed in contact model {0}.",getName());
 884                }
 885                std::vector<DVect> pts;
 886                c->getJointGeometry(&pts);
 887                double area = 0.0;
 888#ifdef THREED
 889                // Step 1: get centroid and area
 890                for (int i=1; i<pts.size()-1; ++i) {
 891                    double a = (pts[0] - pts[i]).mag();
 892                    double b = (pts[0] - pts[i+1]).mag();
 893                    double c = (pts[i] - pts[i+1]).mag();
 894                    double la = 0.0;
 895                    if (b > a)
 896                        std::swap(a,b);
 897                    if (c > a)
 898                        std::swap(a,c);
 899                    if (c > b)
 900                        std::swap(b,c);
 901                    if (c - (a - b) >= 0)
 902                        la = 0.25 * sqrt((a+(b+c))*(c-(a-b))*(c+(a-b))*(a+(b-c)));
 903                    area += la;
 904                }
 905#else
 906                // Assume unit thickness in the out of plane direction
 907                area = (pts[1] - pts[0]).mag();
 908#endif
 909                userArea_ = area;
 910                kTran_ = knpa * area;
 911                E_ = kTran_ / area;
 912                kRot_ = DAVect(0.0);
 913                setInheritance(1,false);
 914                setInheritance(2,false);
 915                sn_mode_ = 1.0;
 916                return true;
 917            }
 918        case kwStiffness: {
 919                FP_S;
 920                poisson_ = 0.0;
 921                if (vl.at(0).isNull())
 922                    throw Exception("Argument emod must be specified with method compute-stiffness in contact model {0}.",getName());
 923                E_ = vl.at(0).toDouble();
 924                if (E_<=0.0)
 925                    throw Exception("Negative or zero emod not allowed in contact model {0}.",getName());
 926                if (vl.at(1).isNull())
 927                    throw Exception("Argument poisson must be specified with method compute-stiffness in contact model {0}.",getName());
 928                poisson_ = vl.at(1).toDouble();
 929                if (poisson_ < 0.0) {
 930                    poisson_ = 0.0;
 931                    throw Exception("Negative poisson not allowed in contact model {0}.",getName());
 932                }
 933                const IBody *b1 = con->getEnd1()->getIBody();
 934                const IBody *b2 = con->getEnd2()->getIBody();
 935                //double vol1 = b1->getVolume();
 936                //double vol2 = b2->getVolume();
 937                //if (std::max(vol1,vol2) > 10.*std::min(vol1,vol2))
 938                //    poisson_ = 0.0;
 939                DVect pos1 = toDVect(b1->getIThing()->getLocation());
 940                DVect pos2 = toDVect(b2->getIThing()->getLocation()) + con->getOffSet();
 941                double dist = (pos1-pos2).mag();
 942                if (con->withWall())
 943                    dist = (pos1 - con->getPosition()).mag();
 944                double tol = std::max(pos1.abs().maxComp(),pos2.abs().maxComp())*limits<double>::epsilon()*1000;
 945                if (dist < tol) {
 946                    poisson_ = 0;
 947                    userArea_ = 0;
 948                    kTran_ = 0;
 949                    kRot_.fill(0);
 950                    return true;
 951                }
 952                std::vector<DVect> pts;
 953                FP_S;
 954                c->getJointGeometry(&pts);
 955                FP_S;
 956                double area = 0.0;
 957                DAVect inertia(0.0);
 958#ifdef THREED
 959                // Step 1: get centroid and area
 960                DVect cm(0.0);
 961                for (int i=1; i<pts.size()-1; ++i) {
 962                    DVect lcm = (pts[0] + pts[i] + pts[i+1])/3.0;
 963                    double a = (pts[0] - pts[i]).mag();
 964                    double b = (pts[0] - pts[i+1]).mag();
 965                    double c = (pts[i] - pts[i+1]).mag();
 966                    double la = 0.0;
 967                    if (b > a)
 968                        std::swap(a,b);
 969                    if (c > a)
 970                        std::swap(a,c);
 971                    if (c > b)
 972                        std::swap(b,c);
 973                    if (c - (a - b) >= 0)
 974                        la = 0.25 * sqrt((a+(b+c))*(c-(a-b))*(c+(a-b))*(a+(b-c)));
 975                    cm += lcm * la;
 976                    area += la;
 977                }
 978                FP_S;
 979                if (area == 0.0) {
 980                    poisson_ = 0;
 981                    userArea_ = 0;
 982                    kTran_ = 0;
 983                    kRot_.fill(0);
 984                    return true;
 985                }
 986                cm /= area;
 987                FP_S;
 988                // Step 2 - center it and put in the local system
 989                for (int i=0; i<pts.size(); ++i) {
 990                    pts[i] -= cm;
 991                    pts[i] = con->getLocalSystem().toLocal(pts[i]);
 992                }
 993                // Step 3: compute the polar inertia
 994                for (int i=0; i<pts.size(); ++i) {
 995                    int j = i < pts.size()-1 ? i+1 : 0;
 996                    double xi = pts[i].y();
 997                    double xip1 = pts[j].y();
 998                    double yi = pts[i].z();
 999                    double yip1 = pts[j].z();
1000                    double frnt = (xi*yip1-xip1*yi);
1001                    inertia.ry() += frnt*(xi*xi+xi*xip1+xip1*xip1);
1002                    inertia.rz() += frnt*(yi*yi+yi*yip1+yip1*yip1);
1003                }
1004                inertia.ry() = std::abs(inertia.y() / 12.);
1005                inertia.rz() = std::abs(inertia.z() / 12.);
1006                inertia.rx() = inertia.y() + inertia.z();
1007
1008#else
1009                // Assume unit thickness in the out of plane direction
1010                area = (pts[1] - pts[0]).mag();
1011                inertia.rz() = area*area*area/12.;
1012#endif
1013                userArea_ = area;
1014                kTran_ = E_ * area / dist;
1015                kRot_ = inertia *E_ / dist;
1016                setInheritance(1,false);
1017                setInheritance(2,false);
1018                sn_mode_ = 1.0;
1019                return true;
1020            }
1021        case kwBond: {
1022                if (sn_state_ == 3) return false;
1023                double mingap = -1.0 * limits<double>::max();
1024                double maxgap = 0;
1025                if (vl.at(0).canConvert<double>())
1026                    maxgap = vl.at(0).toDouble();
1027                else if (vl.at(0).canConvert<DVect2>()) {
1028                    DVect2 value = vl.at(0).value<DVect2>();
1029                    mingap = value.minComp();
1030                    maxgap = value.maxComp();
1031                }
1032                else if (!vl.at(0).isNull())
1033                    throw Exception("gap value {0} not recognized in method bond in contact model {1}.", vl.at(1), getName());
1034                double gap = c->getGap();
1035                if (gap >= mingap && gap <= maxgap) {
1036                    sn_state_ = 3;
1037                    sn_mode_ = 1;
1038                    return true;
1039                }
1040                return false;
1041            }
1042        case kwUnbond: {
1043                if (sn_state_ == 0) return false;
1044                double mingap = -1.0 * limits<double>::max();
1045                double maxgap = 0;
1046                if (vl.at(0).canConvert<double>())
1047                    maxgap = vl.at(0).toDouble();
1048                else if (vl.at(0).canConvert<DVect2>()) {
1049                    DVect2 value = vl.at(0).value<DVect2>();
1050                    mingap = value.minComp();
1051                    maxgap = value.maxComp();
1052                }
1053                else if (!vl.at(0).isNull())
1054                    throw Exception("gap value {0} not recognized in method unbond in contact model {1}.", vl.at(0), getName());
1055                double gap = c->getGap();
1056                if (gap >= mingap && gap <= maxgap) {
1057                    sn_state_ = 0;
1058                    return true;
1059                }
1060                return false;
1061            }
1062        case kwArea: {
1063                if (!userArea_) {
1064                    double rsq(1./std::max(c->getEnd1Curvature().y(),c->getEnd2Curvature().y()));
1065#ifdef THREED
1066                    userArea_ = rsq * rsq * dPi;
1067#else
1068                    userArea_ = rsq * 2.0;
1069#endif
1070                }
1071                return true;
1072            }
1073        case kwResetDisp:
1074            sn_ndisp_ = 0.0;
1075            for (int i=1; i<dim; ++i)
1076                sn_sdisp_.rdof(i) = 0;
1077            break;
1078        }
1079        return false;
1080    }
1081
1082    double ContactModelRBSN::getEnergy(uint32 i) const {
1083        // Return an energy value.
1084        double ret(0.0);
1085        if (!energies_)
1086            return ret;
1087        switch (i) {
1088        case kwEStrain:    return energies_->estrain_;
1089        case kwESlip:      return energies_->eslip_;
1090        case kwEDashpot:   return energies_->edashpot_;
1091        }
1092        assert(0);
1093        return ret;
1094    }
1095
1096    bool ContactModelRBSN::getEnergyAccumulate(uint32 i) const {
1097        // Returns TRUE if the corresponding energy is accumulated or FALSE otherwise.
1098        switch (i) {
1099        case kwEStrain:   return false;
1100        case kwESlip:     return true;
1101        case kwEDashpot:  return true;
1102        }
1103        assert(0);
1104        return false;
1105    }
1106
1107    void ContactModelRBSN::setEnergy(uint32 i,const double &d) {
1108        // Set an energy value.
1109        if (!energies_) return;
1110        switch (i) {
1111        case kwEStrain:    energies_->estrain_ = d;   return;
1112        case kwESlip:      energies_->eslip_   = d;   return;
1113        case kwEDashpot:   energies_->edashpot_= d;   return;
1114        }
1115        assert(0);
1116        return;
1117    }
1118
1119    bool ContactModelRBSN::validate(ContactModelMechanicalState *state,const double &) {
1120        // Validate the / Prepare for entry into ForceDispLaw. The validate function is called when:
1121        // (1) the contact is created, (2) a property of the contact that returns a true via
1122        // the setProperty method has been modified and (3) when a set of cycles is executed
1123        // via the {cycle N} command.
1124        // Return value indicates contact activity (TRUE: active, FALSE: inactive).
1125        assert(state);
1126        const IContactMechanical *c = state->getMechanicalContact();
1127        assert(c);
1128
1129        if (state->trackEnergy_)
1130            activateEnergy();
1131
1132        if (inheritanceField_ & KnMask)
1133            updateKn(c);
1134        if (inheritanceField_ & KsMask)
1135            updateKs(c);
1136        if (inheritanceField_ & FricMask)
1137            updateFric(c);
1138
1139        updateStiffness(state);
1140        return checkActivity(state->gap_);
1141    }
1142
1143    static const QString knstr("kn");
1144    bool ContactModelRBSN::updateKn(const IContactMechanical *con) {
1145        assert(con);
1146        QVariant v1 = con->getEnd1()->getProperty(knstr);
1147        QVariant v2 = con->getEnd2()->getProperty(knstr);
1148        if (!v1.isValid() || !v2.isValid())
1149            return false;
1150        double kn1 = v1.toDouble();
1151        double kn2 = v2.toDouble();
1152        double val = kTran_;
1153        if (kn1 && kn2)
1154            kTran_ = kn1*kn2/(kn1+kn2);
1155        else if (kn1)
1156            kTran_ = kn1;
1157        else if (kn2)
1158            kTran_ = kn2;
1159        return ( (kTran_ != val) );
1160    }
1161
1162    static const QString ksstr("ks");
1163    bool ContactModelRBSN::updateKs(const IContactMechanical *con) {
1164        assert(con);
1165        QVariant v1 = con->getEnd1()->getProperty(ksstr);
1166        QVariant v2 = con->getEnd2()->getProperty(ksstr);
1167        if (!v1.isValid() || !v2.isValid())
1168            return false;
1169        double ks1 = v1.toDouble();
1170        double ks2 = v2.toDouble();
1171        double val = kTran_;
1172        if (ks1 && ks2)
1173            kTran_ = ks1*ks2/(ks1+ks2);
1174        else if (ks1)
1175            kTran_ = ks1;
1176        else if (ks2)
1177            kTran_ = ks2;
1178        return ( (kTran_ != val) );
1179    }
1180
1181    static const QString fricstr("fric");
1182    bool ContactModelRBSN::updateFric(const IContactMechanical *con) {
1183        assert(con);
1184        QVariant v1 = con->getEnd1()->getProperty(fricstr);
1185        QVariant v2 = con->getEnd2()->getProperty(fricstr);
1186        if (!v1.isValid() || !v2.isValid())
1187            return false;
1188        double fric1 = std::max(0.0,v1.toDouble());
1189        double fric2 = std::max(0.0,v2.toDouble());
1190        double val = fric_;
1191        fric_ = std::min(fric1,fric2);
1192        return ( (fric_ != val) );
1193    }
1194
1195    bool ContactModelRBSN::endPropertyUpdated(const QString &name,const IContactMechanical *c) {
1196        // The endPropertyUpdated method is called whenever a surface property (with a name
1197        // that matches an inheritable contact model property name) of one of the contacting
1198        // pieces is modified. This allows the contact model to update its associated
1199        // properties. The return value denotes whether or not the update has affected
1200        // the time step computation (by having modified the translational or rotational
1201        // tangent stiffnesses). If true is returned, then the time step will be recomputed.
1202        assert(c);
1203        QStringList availableProperties = getProperties().simplified().replace(" ","").split(",",Qt::SkipEmptyParts);
1204        QRegularExpression rx(name, QRegularExpression::CaseInsensitiveOption);
1205        int idx = availableProperties.indexOf(rx)+1;
1206        bool ret=false;
1207
1208        if (idx<=0)
1209            return ret;
1210
1211        switch(idx) {
1212        case kwKn:  { //kn
1213                if (inheritanceField_ & KnMask)
1214                    ret = updateKn(c);
1215                break;
1216            }
1217        case kwKs:  { //ks
1218                if (inheritanceField_ & KsMask)
1219                    ret =updateKs(c);
1220                break;
1221            }
1222        case kwFric:  { //fric
1223                if (inheritanceField_ & FricMask)
1224                    updateFric(c);
1225                break;
1226            }
1227        }
1228        return ret;
1229    }
1230
1231    ContactModelRBSN::StiffData ContactModelRBSN::computeStiffData(ContactModelMechanicalState *state) const {
1232        // Update contact data
1233        //double Cmin1 = state->end1Curvature_.x();
1234        double Cmax1 = state->end1Curvature_.y();
1235        double Cmax2 = state->end2Curvature_.y();
1236        double br = sn_rmul_ * 1.0 / std::max(Cmax1, Cmax2);
1237        if (userArea_)
1238#ifdef THREED
1239            br = std::sqrt(userArea_ / dPi);
1240#else
1241            br = userArea_ / 2.0;
1242#endif
1243        StiffData ret;
1244        ret.reff_ = br;
1245        ret.trans_ = DVect2(kTran_,kTran_/kRatio_);
1246        ret.ang_ = kRot_;
1247        return ret;
1248    }
1249
1250    void ContactModelRBSN::updateStiffness(ContactModelMechanicalState *state) {
1251        // first compute stiffness data
1252        StiffData stiff = computeStiffData(state);
1253        // Now calculate effective stiffness
1254        DVect2 retT = stiff.trans_;
1255        // correction if viscous damping active
1256        if (dpProps_) {
1257            DVect2 correct(1.0);
1258            if (dpProps_->dp_nratio_)
1259                correct.rx() = sqrt(1.0+dpProps_->dp_nratio_*dpProps_->dp_nratio_) - dpProps_->dp_nratio_;
1260            if (dpProps_->dp_sratio_)
1261                correct.ry() = sqrt(1.0+dpProps_->dp_sratio_*dpProps_->dp_sratio_) - dpProps_->dp_sratio_;
1262            retT /= (correct*correct);
1263        }
1264        effectiveTranslationalStiffness_ = retT;
1265        // Effective rotational stiffness (bending and twisting)
1266        effectiveRotationalStiffness_ = stiff.ang_;
1267    }
1268
1269    bool ContactModelRBSN::forceDisplacementLaw(ContactModelMechanicalState *state,const double &timestep) {
1270        assert(state);
1271
1272        if (state->activated()) {
1273            // The contact was just activated from an inactive state
1274            // Trigger the FISH callback if one is hooked up to the
1275            // contact_activated event.
1276            if (cmEvents_[fActivated] >= 0) {
1277                auto c = state->getContact();
1278                std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()) };
1279                IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1280                fi->setCMFishCallArguments(c,arg,cmEvents_[fActivated]);
1281            }
1282        }
1283        updateStiffness(state);
1284        // accumulate shear displacement for dilation
1285        sn_ndisp_ += state->relativeTranslationalIncrement_.x();
1286        DVect shearInc =  state->relativeTranslationalIncrement_;
1287        shearInc.rx() = 0;
1288        sn_sdisp_.ry() += shearInc.mag();
1289
1290        if (isBonded()) return FDLawBonded(state, timestep);
1291        else return FDLawUnBonded(state, timestep);
1292
1293    }
1294    
1295    bool ContactModelRBSN::thermalCoupling(ContactModelMechanicalState*, ContactModelThermalState* ts, IContactThermal*, const double&) {
1296        // Account for thermal expansion in incremental mode
1297        if (sn_mode_ == 0 || ts->gapInc_ == 0.0) return false;
1298        DVect finc(0.0);
1299        finc.rx() = kTran_ * ts->gapInc_;
1300        sn_F_ -= finc;
1301        return true;
1302    }
1303
1304    bool ContactModelRBSN::FDLawBonded(ContactModelMechanicalState *state, const double &timestep) {
1305        // initialize ... get the geometry information
1306        DVect3 geom = computeGeomData(state->end1Curvature_,state->end2Curvature_);
1307        double area = geom.x();
1308        double bi = geom.y();
1309        double br = geom.z();
1310        double kn = kTran_;
1311        double ks = kn / kRatio_;
1312        double kb = kRot_.z();
1313#if DIM==3
1314        kb = std::sqrt(kb*kb + kRot_.y()*kRot_.y());
1315        double kt = kRot_.x();
1316#else
1317        double kt = 0.0;
1318#endif
1319
1320        DVect totalForce = sn_F_ + fictForce_;
1321
1322        //double nsmax0 = -(totalForce.x() / area) + sn_mcf_* sqrt(sn_M_.y()*sn_M_.y() + sn_M_.z()*sn_M_.z()) * br / bi;
1323
1324        // Relative translational/rotational displacement increments
1325        DVect  trans = state->relativeTranslationalIncrement_;
1326        DAVect ang = state->relativeAngularIncrement_;
1327
1328        // Store previous force and moment
1329        DVect  sn_F_old = totalForce;
1330        sn_F_old.rx() -= sn_por_ * area;
1331        DAVect sn_M_old = sn_M_;
1332
1333        DVect theStiff(ks);
1334        theStiff.rx() = kn;
1335        sn_F_ -= trans * theStiff;
1336        sn_M_ -= ang * kRot_;
1337        if (poisson_ != 0.0 and (state->end1Volume_ or state->end2Volume_)) {
1338            //const IContact *con = state->getContact();
1339            //const IBody *b1 = con->getEnd1()->getIBody();
1340            //const IBody *b2 = con->getEnd2()->getIBody();
1341            auto stress11 = state->end1Stress_;
1342            auto stress22 = state->end2Stress_;
1343            double vol1 = state->end1Volume_;
1344            double vol2 = state->end2Volume_;
1345            double ms = 0.0;
1346            for (int i=0; i<stress11.size(); ++i)  {
1347                stress11[i] = (stress11[i]*vol1 + stress22[i]*vol2)/(vol1 + vol2);
1348                ms = std::max(ms,abs(stress11[i]));
1349            }
1350            DMatrix<dim,dim> stresst(0.0);
1351#ifdef THREED
1352            stresst.get(0,0) = -poisson_*stress11[1] - poisson_*stress11[2];
1353            stresst.get(1,1) = -poisson_*stress11[0] - poisson_*stress11[2];
1354#else
1355            stresst.get(0,0) = -poisson_*stress11[1];
1356            stresst.get(1,1) = -poisson_*stress11[0];
1357#endif
1358#ifdef THREED
1359            stresst.get(2,2) = -poisson_*stress11[0] - poisson_*stress11[1];
1360#endif
1361            if (poisOffDiag_) {
1362#ifdef THREED
1363                double sxy = stress11[3];
1364                double szx = stress11[4];
1365                double syz = stress11[5];
1366                stresst.get(0,1) = poisson_ * sxy;
1367                stresst.get(1,0) = stresst.get(0,1);
1368                stresst.get(0,2) = poisson_ * szx;
1369                stresst.get(2,0) = stresst.get(0,2);
1370                stresst.get(1,2) = poisson_ * syz;
1371                stresst.get(2,1) = stresst.get(1,2);
1372#else
1373                double sxy = stress11[2];
1374                stresst.get(0,1) = poisson_ * sxy;
1375                stresst.get(1,0) = stresst.get(0,1);
1376#endif
1377            }
1378            
1379            
1380            DVect norm = state->axes_.e1();
1381            DVect traction = stresst * norm * userArea_;
1382            DVect shear(0.0);
1383            shear.ry() = 1.0;
1384            shear = state->axes_.toGlobal(shear);
1385#ifdef THREED
1386            DVect ns = state->axes_.toGlobal(DVect(0.,0.,1.));
1387            fictForce_ = DVect(norm|traction,shear|traction,ns|traction);
1388#else
1389            fictForce_ = DVect(norm|traction,shear|traction);
1390#endif
1391            if (forceSet_ && ms) {
1392                forceSet_ = false;
1393                sn_F_ -= fictForce_;
1394            }
1395        }
1396        FP_S;
1397        double porForce = sn_por_ * area;
1398        sn_F_.rx() -= porForce;
1399        totalForce = sn_F_ + fictForce_;
1400
1401        if (state->canFail_) {
1402            double dbend = sqrt(sn_M_.y()*sn_M_.y() + sn_M_.z()*sn_M_.z());
1403            double nsmax = -(totalForce.x() / area) + sn_mcf_*dbend * br / bi;
1404            bool failed = false;
1405            if (sn_state_ == 3 || sn_state_ == 5) {
1406                double compVal = sn_state_ == 3 ? tenTable_[0].x() : transTen_;
1407                if (nsmax >= compVal ) {
1408                    if (tenTable_.back().y() < limits<double>::epsilon()*100)
1409                        failed = true;
1410                    else {
1411                        if (sn_state_ == 3)
1412                            sn_elong_ = 0;
1413                        transTen_ = compVal;
1414                        sn_state_ = 4;
1415                    }
1416                }
1417            }
1418            FP_S;
1419
1420            if (sn_state_ == 4) {
1421                sn_elong_ += trans.x();
1422                sn_elong_ = std::max(0.0,sn_elong_);
1423                int ww = -1;
1424                if (sn_elong_ <= tenTable_.back().y()) {
1425                    for (int i=0; i<tenTable_.size(); ++i) {
1426                        if (tenTable_[i].y() >= sn_elong_) {
1427                            ww = i;
1428                            break;
1429                        }
1430                    }
1431                } else
1432                    ww = tenTable_.size() - 1;
1433                if (ww > 0) {
1434                    //double factor = std::min(1.0, sn_elong_ / tenTable_[ww].y());
1435                    double prevVal = ww == 1 ? 1 : tenTable_[ww-1].x();
1436                    double curVal =  tenTable_[ww].x();
1437                    double prevElong = tenTable_[ww-1].y();
1438                    double curElong = tenTable_[ww].y();
1439                    double slope = (curVal - prevVal)/(curElong - prevElong);
1440                    FP_S;
1441                    //y-y0 = m(x-x0)
1442                    double nstren = slope * (sn_elong_ - prevElong) + prevVal;
1443                    if (nstren <= 0)
1444                        failed = true;
1445                    else {
1446                        nstren *= tenTable_[0].x();
1447                        if (nsmax >= nstren || slope > 0) {
1448                            double fac = nstren / nsmax;
1449                            sn_F_.rx() *= fac;
1450#if DIM==3
1451                            sn_M_.ry() *= fac;
1452#endif
1453                            sn_M_.rz() *= fac;
1454                            fictForce_.rx() *= fac;
1455                        } else {
1456                            sn_state_ = 5;
1457                            transTen_ = -(sn_F_old.x() / area) + sn_mcf_* sqrt(sn_M_old.y()*sn_M_old.y() + sn_M_old.z()*sn_M_old.z()) * br / bi;
1458                        }
1459                    }
1460                }
1461            }
1462            if (sn_state_ == 6 && nsmax >= 0)
1463                failed = true;
1464            FP_S;
1465            if (failed) {
1466                // Failed in tension
1467                double se = strainEnergy(kn, ks, kb, kt); // bond strain energy at the onset of failure
1468                sn_state_ = 1;
1469                sn_F_.fill(0.0);
1470                sn_M_.fill(0.0);
1471                failed = true;
1472                fictForce_ = DVect(0.0);
1473                //sn_F_.rx() = -sn_tenres_ * area;
1474                if (cmEvents_[fBondBreak] >= 0) {
1475                    auto c = state->getContact();
1476                    std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
1477                                                         fish::Parameter((qint64)sn_state_),
1478                                                         fish::Parameter(nsmax),
1479                                                         fish::Parameter(se)
1480                                                       };
1481                    IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1482                    fi->setCMFishCallArguments(c,arg,cmEvents_[fBondBreak]);
1483                }
1484            }
1485            FP_S;
1486
1487            if (!failed) {
1488                /* check for shear failure */
1489                double dtwist = sn_M_.x();
1490                DVect bfs(totalForce);
1491                bfs.rx() = 0.0;
1492                double dbfs = bfs.mag();
1493                double ssmax = dbfs / area + sn_mcf_*std::abs(dtwist) * 0.5* br / bi;
1494                double ss = shearStrength(area);
1495                FP_S;
1496                if (ss < 0)
1497                    ss = 0;
1498                if (ss <= ssmax) {
1499                    // strength when it breaks for
1500                    // Failed in shear
1501                    double se = strainEnergy(kn, ks, kb, kt); // bond strain energy at the onset of failure
1502                    sn_state_ = 2;
1503                    fictForce_ = DVect(0.0);
1504                    FP_S;
1505                    sn_F_ = totalForce;
1506                    if (cmEvents_[fBondBreak] >= 0) {
1507                        auto c = state->getContact();
1508                        std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
1509                                                             fish::Parameter((qint64)sn_state_),
1510                                                             fish::Parameter(ss),
1511                                                             fish::Parameter(se)
1512                                                           };
1513                        IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1514                        fi->setCMFishCallArguments(c,arg,cmEvents_[fBondBreak]);
1515                    }
1516                    double mm = 0.0;
1517                    for (int i=1; i<dim; ++i)
1518                        mm += trans[i]*trans[i];
1519                    sn_sdisp_.rx() = std::sqrt(mm);
1520                    double crit = sn_F_.x() * fric_ + sn_cohres_ * area;
1521                    if (sn_cohdc() > std::numeric_limits<double>::epsilon()) {
1522                        int ww = -1;
1523                        if (sn_sdisp_.x() <= sn_cohdc()) {
1524                            for (int i=0; i<cohTable_.size(); ++i) {
1525                                if (cohTable_[i].y() >= sn_sdisp_.x()) {
1526                                    ww = i;
1527                                    break;
1528                                }
1529                            }
1530                        } else
1531                            ww = cohTable_.size() - 1;
1532                        if (ww > 0) {
1533                            double prevVal = ww == 1 ? 1: cohTable_[ww-1].x() ;//critBreak_ : cohTable_[ww-1].x() + sn_F_.x() * fric_;
1534                            double curVal =  cohTable_[ww].x();//cohTable_[ww].x() + sn_F_.x() * fric_;
1535                            double prevShear = cohTable_[ww-1].y();
1536                            double curShear = cohTable_[ww].y();
1537                            double slope = (curVal - prevVal)/(curShear - prevShear);
1538                            // should go from 0 to 1
1539                            double mval = slope * (sn_sdisp_.x() - prevShear) + prevVal;
1540                            double fact = std::max(0.,std::min(1.0,1.0 - mval));
1541                            assert(fact >= 0 && fact <= 1.0);
1542                            double theFric = fric_;
1543                            if (sn_fa_)
1544                                theFric = sn_fa_ + (fric_ - sn_fa_)*fact;
1545                            double theCoh = sn_Coh();
1546                            if (theCoh)
1547                                theCoh = sn_Coh() + (sn_cohres_ - sn_Coh())*fact;
1548                            crit = sn_F_.x() * theFric + theCoh * geom.x();
1549                        }
1550                    }
1551
1552                    // Resolve sliding.
1553                    FP_S;
1554                    if (crit < 0)
1555                        crit = 0;
1556                    DVect sforce = sn_F_; sforce.rx() = 0.0;
1557                    // The is the magnitude of the shear force.
1558                    double sfmag = sforce.mag();
1559                    // Sliding occurs when the magnitude of the shear force is greater than the
1560                    // critical value.
1561                    if (sfmag > crit) {
1562                        // Lower the shear force to the critical value for sliding.
1563                        double rat = crit / sfmag;
1564                        sforce *= rat;
1565                        sforce.rx() = sn_F_.x();
1566                        sn_F_ = sforce;
1567                        sn_S_ = true;
1568                    }
1569                    if (sn_S_) {
1570                        if (sn_dil_ > 0) {
1571                            double zdd = sn_dilzero_ != 0 ? sn_dilzero_ : limits<double>::max();
1572                            double usm = sn_sdisp_.y();
1573                            if (usm < zdd) {
1574                                double sInc = 0.0;
1575                                for (int i=1; i<dim; ++i)
1576                                    sInc += trans.dof(i)*trans.dof(i);
1577                                sInc = std::sqrt(sInc);
1578                                sn_F_.rx() += kTran_ * sn_dil_ * sInc;
1579                            }
1580                        }
1581                    }
1582
1583                    // Resolve bending
1584                    crit = sn_bmul_*2.1*0.25*br*std::abs(sn_F_.x()); // Jiang 2015
1585                    FP_S;
1586                    DAVect test = sn_M_;
1587#if DIM==3
1588                    test.rx() = 0.0;
1589#endif
1590                    double tmag = test.mag();
1591                    if (tmag > crit) {
1592                        // Lower the bending moment to the critical value for sliding.
1593                        double rat = crit / tmag;
1594                        test *= rat;
1595                        sn_BS_ = true;
1596                    }
1597                    sn_M_.rz() = test.z();
1598#if DIM==3
1599                    sn_M_.ry() = test.y();
1600                    // Resolve twisting
1601                    crit = sn_tmul_ * 0.65*fric_* br*std::abs(sn_F_.x()) ; // Jiang 2015
1602                    tmag = std::abs(sn_M_.x());
1603                    if (tmag > crit) {
1604                        // Lower the shear force to the critical value for sliding.
1605                        double rat = crit / tmag;
1606                        tmag = sn_M_.x() * rat;
1607                        sn_TS_ = true;
1608                    }
1609                    sn_M_.rx() = tmag;
1610                    FP_S;
1611#endif
1612                }
1613            }
1614        }
1615        sn_F_old.rx() += porForce;
1616        sn_F_.rx() += porForce;
1617        totalForce = sn_F_ + fictForce_;
1618        FP_S;
1619
1620        // Account for dashpot forces if the dashpot structure has been defined.
1621        if (dpProps_) {
1622            dpProps_->dp_F_.fill(0.0);
1623            double vcn(0.0), vcs(0.0);
1624            // Calculate the damping coefficients.
1625            vcn = dpProps_->dp_nratio_ * 2.0 * sqrt((state->inertialMass_*(kn)));
1626            vcs = dpProps_->dp_sratio_ * 2.0 * sqrt((state->inertialMass_*(ks)));
1627            // First damp the shear components
1628            for (int i = 1; i<dim; ++i)
1629                dpProps_->dp_F_.rdof(i) = trans.dof(i) * (-1.0* vcs) / timestep;
1630            // Damp the normal component
1631            dpProps_->dp_F_.rx() -= trans.x() * vcn / timestep;
1632            // Need to change behavior based on the dp_mode.
1633            if ((dpProps_->dp_mode_ == 1 || dpProps_->dp_mode_ == 3)) {
1634                // Limit in tension if not bonded.
1635                if (sn_state_ < 3 && (dpProps_->dp_F_.x() + totalForce.x() < 0))
1636                    dpProps_->dp_F_.rx() = -totalForce.rx();
1637            }
1638            if (sn_S_ && dpProps_->dp_mode_ > 1) {
1639                // Limit in shear if sliding.
1640                double dfn = dpProps_->dp_F_.rx();
1641                dpProps_->dp_F_.fill(0.0);
1642                dpProps_->dp_F_.rx() = dfn;
1643            }
1644        }
1645        FP_S;
1646
1647        //Compute energies if energy tracking has been enabled.
1648        if (state->trackEnergy_) {
1649            assert(energies_);
1650            energies_->estrain_ = 0.0;
1651            if (kn)
1652                // Calculate the strain energy.
1653                energies_->estrain_ = 0.5*totalForce.x()*totalForce.x() / kn;
1654            if (ks) {
1655                DVect s = totalForce;
1656                s.rx() = 0.0;
1657                double smag2 = s.mag2();
1658                // Add the shear component of the strain energy.
1659                energies_->estrain_ += 0.5*smag2 / ks;
1660
1661                if (sn_S_) {
1662                    // If sliding calculate the slip energy and accumulate it.
1663                    sn_F_old.rx() = 0.0;
1664                    DVect avg_F_s = (s + sn_F_old)*0.5;
1665                    DVect u_s_el = (s - sn_F_old) / ks;
1666                    DVect u_s(0.0);
1667                    for (int i = 1; i < dim; ++i)
1668                        u_s.rdof(i) = trans.dof(i);
1669                    energies_->eslip_ -= std::min(0.0, (avg_F_s | (u_s + u_s_el)));
1670                }
1671            }
1672            // Add the bending/twisting resistance energy contributions.
1673            if (kb) {
1674                DAVect tmp = sn_M_;
1675#ifdef THREED
1676                tmp.rx() = 0.0;
1677#endif
1678                energies_->estrain_ += 0.5*tmp.mag2() / kb;
1679                if (sn_BS_) {
1680                    //  accumulate bending slip energy.
1681                    DAVect tmp_old = sn_M_old;
1682#ifdef THREED
1683                    tmp_old.rx() = 0.0;
1684#endif
1685                    DAVect avg_M = (tmp + tmp_old)*0.5;
1686                    DAVect t_s_el = (tmp - tmp_old) / kb;
1687                    energies_->eslip_ -= std::min(0.0, (avg_M | (ang + t_s_el)));
1688                }
1689            }
1690#ifdef THREED
1691            if (kt) {
1692                double mt = std::abs(sn_M_.x());
1693                energies_->estrain_ += 0.5*mt*mt / kt;
1694                if (sn_TS_) {
1695                    //  accumulate twisting slip energy.
1696                    DAVect tmp(0.0);
1697                    DAVect tmp_old(0.0);
1698                    tmp.rx() = sn_M_.x();
1699                    tmp_old.rx() = sn_M_old.x();
1700                    DAVect avg_M = (tmp + tmp_old)*0.5;
1701                    DAVect t_s_el = (tmp - tmp_old) / kt;
1702                    energies_->eslip_ -= std::min(0.0, (avg_M | (ang + t_s_el)));
1703                }
1704            }
1705#endif
1706
1707            if (dpProps_) {
1708                // Calculate damping energy (accumulated) if the dashpots are active.
1709                energies_->edashpot_ -= dpProps_->dp_F_ | trans;
1710            }
1711        }
1712
1713        // This is just a sanity check to ensure, in debug mode, that the force/moment aren't wonky.
1714        assert(sn_F_ == sn_F_);
1715        assert(sn_M_ == sn_M_);
1716        assert(fictForce_ == fictForce_);
1717        FP_S;
1718        return true;
1719    }
1720
1721    bool ContactModelRBSN::FDLawUnBonded(ContactModelMechanicalState *state, const double &timestep) {
1722        DVect3 geom = computeGeomData(state->end1Curvature_,state->end2Curvature_);
1723        double br = geom.z();
1724        // Relative translational/rotational displacement increments
1725        DVect  trans = state->relativeTranslationalIncrement_;
1726        DAVect ang   = state->relativeAngularIncrement_;
1727        double overlap = rgap_ - state->gap_;
1728        double correction = 1.0;
1729        if (state->activated() && sn_mode_ == 0 && trans.x()) {
1730                correction = -1.0*overlap / trans.x();
1731                if (correction < 0)
1732                    correction = 1.0;
1733        }
1734
1735        // Store previous force and moment
1736        DVect  sn_F_old = sn_F_;
1737        double porForce = sn_por_ * geom.x();
1738        sn_F_old.rx() -= porForce;
1739        DAVect sn_M_old = sn_M_;
1740
1741        double kb = kRot_.z();
1742#if DIM==3
1743        double kt = kRot_.x();
1744        //kb = std::sqrt(kb * kb + kRot_.y() * kRot_.y());
1745#endif
1746        // absolute/incremental normal force calculation
1747        DVect theStiff(kTran_/kRatio_);
1748        theStiff.rx() = kTran_;
1749
1750        if (sn_mode_==0)
1751            sn_F_.rx() = overlap * theStiff.x();
1752        else
1753            sn_F_.rx() -= trans.x() * theStiff.x();
1754        // Normal force can only be positive if unbonded
1755        sn_F_.rx() = std::max(0.0, sn_F_.x()) - porForce;
1756
1757        // Calculate the trial shear force.
1758        DVect sforce(0.0);
1759        // dim holds the dimension (e.g., 2 for 2D and 3 for 3D)
1760        // Loop over the shear components (note: the 0 component is the normal component)
1761        // and calculate the shear force.
1762        for (int i = 1; i<dim; ++i)
1763            sforce.rdof(i) = sn_F_.dof(i) - trans.dof(i) * theStiff.dof(i);
1764
1765        // Calculate the trial moment.
1766        DAVect mom = sn_M_ - ang*kRot_;
1767
1768        // If the SOLVE ELASTIC command is given then the
1769        // canFail state is set to FALSE. Otherwise it is always TRUE.
1770        if (state->canFail_) {
1771            bool changed = false;
1772            // Resolve sliding. This is the normal force multiplied by the coefficient of friction.
1773            bool slip_changed = false;
1774
1775            double crit = sn_F_.x() * fric_ + sn_cohres_ * geom.x();
1776            if (sn_state_ != 0) {
1777                bool doComp = true; 
1778                if (!sn_S_) {
1779                    if (sn_heal_) {
1780                        sn_sdisp_.rx() = 0;
1781                        crit = sn_F_.x() * sn_fa_ + cohTable_[0].x() * geom.x();
1782                        doComp = false;
1783                    }
1784                }
1785                if (doComp) {
1786                    double mm = 0.0;
1787                    for (int i=1; i<dim; ++i)
1788                        mm += trans[i]*trans[i];
1789                    sn_sdisp_.rx() += std::sqrt(mm);
1790                    if (sn_cohdc() > std::numeric_limits<double>::epsilon()) {
1791                        int ww = -1;
1792                        if (sn_sdisp_.x() <= sn_cohdc()) {
1793                            for (int i=0; i<cohTable_.size(); ++i) {
1794                                if (cohTable_[i].y() >= sn_sdisp_.x()) {
1795                                    ww = i;
1796                                    break;
1797                                }
1798                            }
1799                        } else
1800                            ww = cohTable_.size() - 1;
1801                        if (ww > 0) {
1802                            double prevVal = ww == 1 ? 1: cohTable_[ww-1].x() ;//critBreak_ : cohTable_[ww-1].x() + sn_F_.x() * fric_;
1803                            double curVal =  cohTable_[ww].x();//cohTable_[ww].x() + sn_F_.x() * fric_;
1804                            double prevShear = cohTable_[ww-1].y();
1805                            double curShear = cohTable_[ww].y();
1806                            double slope = (curVal - prevVal)/(curShear - prevShear);
1807                            // should go from 0 to 1
1808                            double mval = slope * (sn_sdisp_.x() - prevShear) + prevVal;
1809                            double fact = std::max(0.,std::min(1.0,1.0 - mval));
1810                            assert(fact >= 0 && fact <= 1.0);
1811                            double theFric = fric_;
1812                            if (sn_fa_)
1813                                theFric = sn_fa_ + (fric_ - sn_fa_)*fact;
1814                            double theCoh = sn_Coh();
1815                            if (theCoh)
1816                                theCoh = sn_Coh() + (sn_cohres_ - sn_Coh())*fact;
1817                            crit = sn_F_.x() * theFric + theCoh * geom.x();
1818                        }
1819                    }
1820                }
1821            }
1822
1823            // Resolve sliding.
1824            if (crit < 0)
1825                crit = 0.0;
1826            // The is the magnitude of the shear force.
1827            double sfmag = sforce.mag();
1828            if (sfmag > crit) {
1829                // Lower the shear force to the critical value for sliding.
1830                double rat = crit / sfmag;
1831                sforce *= rat;
1832                if (!sn_S_) {
1833                    slip_changed = true;
1834                    changed = true;
1835                }
1836                sn_S_ = true;
1837            } else {
1838                if (sn_S_) {
1839                    slip_changed = true;
1840                    changed = true;
1841                }
1842                sn_S_ = false;
1843            }
1844            if (sn_S_) {
1845                if (sn_dil_ > 0) {
1846                    double zdd = sn_dilzero_ != 0 ? sn_dilzero_ : limits<double>::max();
1847                    double usm = sn_sdisp_.y();
1848                    if (usm < zdd) {
1849                        double sInc = 0.0;
1850                        for (int i=1; i<dim; ++i)
1851                            sInc += trans.dof(i)*trans.dof(i);
1852                        sInc = std::sqrt(sInc);
1853                        sn_F_.rx() += kTran_ * sn_dil_ * sInc;
1854                    }
1855                }
1856            } else {
1857                if (sn_heal_) {
1858                    if (shearStrength(geom.x()))
1859                        sn_state_ = 6;
1860                }
1861            }
1862            // Resolve bending
1863            bool bslip_changed = false;
1864            crit = sn_bmul_ * 2.1*0.25*sn_F_.x() * br; // Jiang 2015
1865            DAVect test = mom;
1866#if DIM==3
1867            test.rx() = 0.0;
1868#endif
1869            double tmag = test.mag();
1870            if (tmag > crit) {
1871                // Lower the bending moment to the critical value for sliding.
1872                double rat = crit / tmag;
1873                test *= rat;
1874                if (!sn_BS_) {
1875                    bslip_changed = true;
1876                    changed = true;
1877                }
1878                sn_BS_ = true;
1879            }
1880            else {
1881                if (sn_BS_) {
1882                    bslip_changed = true;
1883                    changed = true;
1884                }
1885                sn_BS_ = false;
1886            }
1887            mom.rz() = test.z();
1888#if DIM==3
1889            mom.ry() = test.y();
1890            // Resolve twisting
1891            bool tslip_changed = false;
1892            crit = sn_tmul_ * 0.65*fric_*sn_F_.x() * br; // Jiang 2015
1893            tmag = std::abs(mom.x());
1894            if (tmag > crit) {
1895                // Lower the twisting moment to the critical value for sliding.
1896                double rat = crit / tmag;
1897                mom.rx() *= rat;
1898                if (!sn_TS_) {
1899                    tslip_changed = true;
1900                    changed = true;
1901                }
1902                sn_TS_ = true;
1903            }
1904            else {
1905                if (sn_TS_) {
1906                    tslip_changed = true;
1907                    changed = true;
1908                }
1909                sn_TS_ = false;
1910            }
1911#endif
1912            if (changed && cmEvents_[fSlipChange] >= 0) {
1913                qint64 code = 0;
1914                if (slip_changed) {
1915                    code = 1;
1916                    if (bslip_changed) {
1917                        code = 4;
1918#if DIM==3
1919                        if (tslip_changed)
1920                            code = 7;
1921#endif
1922                    }
1923                }
1924                else if (bslip_changed) {
1925                    code = 2;
1926#if DIM==3
1927                    if (tslip_changed)
1928                        code = 6;
1929#endif
1930                }
1931#if DIM==3
1932                else if (tslip_changed) {
1933                    code = 3;
1934                    if (slip_changed)
1935                        code = 5;
1936                }
1937#endif
1938                auto c = state->getContact();
1939                std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
1940                                                     fish::Parameter(code),
1941                                                     fish::Parameter(sn_S_),
1942                                                     fish::Parameter(sn_BS_)
1943#ifdef THREED
1944                                                     ,fish::Parameter(sn_TS_)
1945#endif
1946                                                   };
1947                IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1948                fi->setCMFishCallArguments(c,arg,cmEvents_[fSlipChange]);
1949            }
1950        }
1951
1952        sn_F_.rx() += porForce;
1953        sn_F_old.rx() += porForce;
1954
1955        // Set the shear components of the total force.
1956        for (int i = 1; i<dim; ++i)
1957            sn_F_.rdof(i) = sforce.dof(i);
1958
1959        // Set the moment.
1960        sn_M_ = mom;
1961
1962        // Account for dashpot forces if the dashpot structure has been defined.
1963        if (dpProps_) {
1964            dpProps_->dp_F_.fill(0.0);
1965            double vcn(0.0), vcs(0.0);
1966            // Calculate the damping coefficients.
1967            vcn = dpProps_->dp_nratio_ * 2.0 * sqrt((state->inertialMass_*(kTran_)));
1968            vcs = dpProps_->dp_sratio_ * 2.0 * sqrt((state->inertialMass_*(kTran_/kRatio_)));
1969            // First damp the shear components
1970            for (int i = 1; i<dim; ++i)
1971                dpProps_->dp_F_.rdof(i) = trans.dof(i) * (-1.0* vcs) / timestep;
1972            // Damp the normal component
1973            dpProps_->dp_F_.rx() -= trans.x() * vcn / timestep;
1974            // Need to change behavior based on the dp_mode.
1975            if ((dpProps_->dp_mode_ == 1 || dpProps_->dp_mode_ == 3)) {
1976                // Limit in tension if not bonded.
1977                if (dpProps_->dp_F_.x() + sn_F_.x() < 0)
1978                    dpProps_->dp_F_.rx() = -sn_F_.rx();
1979            }
1980            if (sn_S_ && dpProps_->dp_mode_ > 1) {
1981                // Limit in shear if not sliding.
1982                double dfn = dpProps_->dp_F_.rx();
1983                dpProps_->dp_F_.fill(0.0);
1984                dpProps_->dp_F_.rx() = dfn;
1985            }
1986        }
1987
1988        //Compute energies if energy tracking has been enabled.
1989        if (state->trackEnergy_) {
1990            assert(energies_);
1991            energies_->estrain_ = 0.0;
1992            if (kTran_)
1993                // Calculate the strain energy.
1994                energies_->estrain_ = 0.5*sn_F_.x()*sn_F_.x() / kTran_;
1995            if (kTran_) {
1996                DVect s = sn_F_;
1997                s.rx() = 0.0;
1998                double smag2 = s.mag2();
1999                // Add the shear component of the strain energy.
2000                energies_->estrain_ += 0.5*smag2 / (kTran_/kRatio_);
2001
2002                if (sn_S_) {
2003                    // If sliding calculate the slip energy and accumulate it.
2004                    sn_F_old.rx() = 0.0;
2005                    DVect avg_F_s = (s + sn_F_old)*0.5;
2006                    DVect u_s_el = (s - sn_F_old) / (kTran_/kRatio_);
2007                    DVect u_s(0.0);
2008                    for (int i = 1; i < dim; ++i)
2009                        u_s.rdof(i) = trans.dof(i);
2010                    energies_->eslip_ -= std::min(0.0, (avg_F_s | (u_s + u_s_el)));
2011                }
2012            }
2013            // Add the bending/twisting resistance energy contributions.
2014            if (kb) {
2015                DAVect tmp = sn_M_;
2016#ifdef THREED
2017                tmp.rx() = 0.0;
2018#endif
2019                energies_->estrain_ += 0.5*tmp.mag2() / kb;
2020                if (sn_BS_) {
2021                    //  accumulate bending slip energy.
2022                    DAVect tmp_old = sn_M_old;
2023#ifdef THREED
2024                    tmp_old.rx() = 0.0;
2025#endif
2026                    DAVect avg_M = (tmp + tmp_old)*0.5;
2027                    DAVect t_s_el = (tmp - tmp_old) / kb;
2028                    energies_->eslip_ -= std::min(0.0, (avg_M | (ang + t_s_el)));
2029                }
2030            }
2031#ifdef THREED
2032            if (kt) {
2033                double mt = std::abs(sn_M_.x());
2034                energies_->estrain_ += 0.5*mt*mt / kt;
2035                if (sn_TS_) {
2036                    //  accumulate twisting slip energy.
2037                    DAVect tmp(0.0);
2038                    DAVect tmp_old(0.0);
2039                    tmp.rx() = sn_M_.x();
2040                    tmp_old.rx() = sn_M_old.x();
2041                    DAVect avg_M = (tmp + tmp_old)*0.5;
2042                    DAVect t_s_el = (tmp - tmp_old) / kt;
2043                    energies_->eslip_ -= std::min(0.0, (avg_M | (ang + t_s_el)));
2044                }
2045            }
2046#endif
2047
2048            if (dpProps_) {
2049                // Calculate damping energy (accumulated) if the dashpots are active.
2050                energies_->edashpot_ -= dpProps_->dp_F_ | trans;
2051            }
2052        }
2053
2054        // This is just a sanity check to ensure, in debug mode, that the force/moment aren't wonky.
2055        assert(sn_F_ == sn_F_);
2056        assert(fictForce_ == fictForce_);
2057        assert(sn_M_ == sn_M_);
2058        return true;
2059    }
2060
2061    void ContactModelRBSN::setForce(const DVect &v,IContact *c) {
2062        sn_F_ = v;
2063        fictForce_ = DVect(0.0);
2064        forceSet_ = true;
2065        if (v.x() > 0) 
2066            rgap_ = c->getGap() + v.x() / kTran_;
2067    }
2068
2069    void ContactModelRBSN::propagateStateInformation(IContactModelMechanical* old,const CAxes &oldSystem,const CAxes &newSystem) {
2070        // Only called for contacts with wall facets when the wall resolution scheme
2071        // is set to full!
2072        // Only do something if the contact model is of the same type
2073        if (old->getContactModel()->getName().compare("springnetwork",Qt::CaseInsensitive) == 0 && !isBonded()) {
2074            ContactModelRBSN *oldCm = (ContactModelRBSN *)old;
2075#ifdef THREED
2076            // Need to rotate just the shear component from oldSystem to newSystem
2077
2078            // Step 1 - rotate oldSystem so that the normal is the same as the normal of newSystem
2079            DVect axis = oldSystem.e1() & newSystem.e1();
2080            double c, ang, s;
2081            DVect re2;
2082            if (!checktol(axis.abs().maxComp(),0.0,1.0,1000)) {
2083                axis = axis.unit();
2084                c = oldSystem.e1()|newSystem.e1();
2085                if (c > 0)
2086                    c = std::min(c,1.0);
2087                else
2088                    c = std::max(c,-1.0);
2089                ang = acos(c);
2090                s = sin(ang);
2091                double t = 1. - c;
2092                DMatrix<3,3> rm;
2093                rm.get(0,0) = t*axis.x()*axis.x() + c;
2094                rm.get(0,1) = t*axis.x()*axis.y() - axis.z()*s;
2095                rm.get(0,2) = t*axis.x()*axis.z() + axis.y()*s;
2096                rm.get(1,0) = t*axis.x()*axis.y() + axis.z()*s;
2097                rm.get(1,1) = t*axis.y()*axis.y() + c;
2098                rm.get(1,2) = t*axis.y()*axis.z() - axis.x()*s;
2099                rm.get(2,0) = t*axis.x()*axis.z() - axis.y()*s;
2100                rm.get(2,1) = t*axis.y()*axis.z() + axis.x()*s;
2101                rm.get(2,2) = t*axis.z()*axis.z() + c;
2102                re2 = rm*oldSystem.e2();
2103            }
2104            else
2105                re2 = oldSystem.e2();
2106            // Step 2 - get the angle between the oldSystem rotated shear and newSystem shear
2107            axis = re2 & newSystem.e2();
2108            DVect2 tpf;
2109            DVect2 tpm;
2110            DMatrix<2,2> m;
2111            if (!checktol(axis.abs().maxComp(),0.0,1.0,1000)) {
2112                axis = axis.unit();
2113                c = re2|newSystem.e2();
2114                if (c > 0)
2115                    c = std::min(c,1.0);
2116                else
2117                    c = std::max(c,-1.0);
2118                ang = acos(c);
2119                if (!checktol(axis.x(),newSystem.e1().x(),1.0,100))
2120                    ang *= -1;
2121                s = sin(ang);
2122                m.get(0,0) = c;
2123                m.get(1,0) = s;
2124                m.get(0,1) = -m.get(1,0);
2125                m.get(1,1) = m.get(0,0);
2126                tpf = m*DVect2(oldCm->sn_F_.y(),oldCm->sn_F_.z());
2127                tpm = m*DVect2(oldCm->sn_M_.y(),oldCm->sn_M_.z());
2128            } else {
2129                m.get(0,0) = 1.;
2130                m.get(0,1) = 0.;
2131                m.get(1,0) = 0.;
2132                m.get(1,1) = 1.;
2133                tpf = DVect2(oldCm->sn_F_.y(),oldCm->sn_F_.z());
2134                tpm = DVect2(oldCm->sn_M_.y(),oldCm->sn_M_.z());
2135            }
2136            DVect pforce = DVect(0,tpf.x(),tpf.y());
2137            //DVect pm     = DVect(0,tpm.x(),tpm.y());
2138#else
2139            oldSystem;
2140            newSystem;
2141            DVect pforce = DVect(0,oldCm->sn_F_.y());
2142            //DVect pm     = DVect(0,oldCm->sn_M_.y());
2143#endif
2144            for (int i=1; i<dim; ++i)
2145                sn_F_.rdof(i) += pforce.dof(i);
2146            if (sn_mode_ && oldCm->sn_mode_)
2147                sn_F_.rx() = oldCm->sn_F_.x();
2148            oldCm->sn_F_ = DVect(0.0);
2149            oldCm->sn_M_ = DAVect(0.0);
2150            if (dpProps_ && oldCm->dpProps_) {
2151#ifdef THREED
2152                tpf = m*DVect2(oldCm->dpProps_->dp_F_.y(),oldCm->dpProps_->dp_F_.z());
2153                pforce = DVect(oldCm->dpProps_->dp_F_.x(),tpf.x(),tpf.y());
2154#else
2155                pforce = oldCm->dpProps_->dp_F_;
2156#endif
2157                dpProps_->dp_F_ += pforce;
2158                oldCm->dpProps_->dp_F_ = DVect(0.0);
2159            }
2160            if(oldCm->getEnergyActivated()) {
2161                activateEnergy();
2162                energies_->estrain_ = oldCm->energies_->estrain_;
2163                energies_->edashpot_ = oldCm->energies_->edashpot_;
2164                energies_->eslip_ = oldCm->energies_->eslip_;
2165                oldCm->energies_->estrain_ = 0.0;
2166                oldCm->energies_->edashpot_ = 0.0;
2167                oldCm->energies_->eslip_ = 0.0;
2168            }
2169            rgap_ = oldCm->rgap_;
2170        }
2171        assert(sn_F_ == sn_F_);
2172    }
2173
2174    void ContactModelRBSN::setNonForcePropsFrom(IContactModel *old) {
2175        // Only called for contacts with wall facets when the wall resolution scheme
2176        // is set to full!
2177        // Only do something if the contact model is of the same type
2178        if (old->getName().compare("springnetwork",Qt::CaseInsensitive) == 0 && !isBonded()) {
2179            ContactModelRBSN *oldCm = (ContactModelRBSN *)old;
2180
2181            fictForce_ = oldCm->fictForce_;
2182            sn_F_ = oldCm->sn_F_;
2183            sn_sdisp_ = oldCm->sn_sdisp_;
2184            sn_M_ = oldCm->sn_M_;
2185            kRot_ = oldCm->kRot_;
2186            kTran_ = oldCm->kTran_;
2187            kRatio_ = oldCm->kRatio_;
2188            E_ = oldCm->E_;
2189            poisson_ = oldCm->poisson_;
2190            fric_ = oldCm->fric_;
2191            sn_bmul_ = oldCm->sn_bmul_;
2192            sn_tmul_ = oldCm->sn_tmul_;
2193            sn_rmul_ = oldCm->sn_rmul_;
2194            userArea_ = oldCm->userArea_;
2195            rgap_ = oldCm->rgap_;
2196            sn_fa_ = oldCm->sn_fa_;
2197            sn_mcf_ = oldCm->sn_mcf_;
2198            sn_dil_ = oldCm->sn_dil_;
2199            sn_dilzero_ = oldCm->sn_dilzero_;
2200            transTen_ = oldCm->transTen_;
2201            sn_elong_ = oldCm->sn_elong_;
2202            sn_ndisp_ = oldCm->sn_ndisp_;
2203            sn_mode_ = oldCm->sn_mode_;
2204            sn_state_ = oldCm->sn_state_;
2205            poisOffDiag_ = oldCm->poisOffDiag_;
2206            sn_S_ = oldCm->sn_S_;
2207            sn_BS_ = oldCm->sn_BS_;
2208            sn_TS_ = oldCm->sn_TS_;
2209            forceSet_ = oldCm->forceSet_;
2210            sn_heal_ = oldCm->sn_heal_;
2211            tenTable_ = oldCm->tenTable_;
2212            cohTable_ = oldCm->cohTable_;
2213            if (oldCm->hasDamping()) {
2214                if (!dpProps_)
2215                    dpProps_ = NEW dpProps();
2216                dp_nratio(oldCm->dp_nratio());
2217                dp_sratio(oldCm->dp_sratio());
2218                dp_mode(oldCm->dp_mode());
2219                dp_F(oldCm->dp_F());
2220            }
2221        }
2222    }
2223
2224    DVect ContactModelRBSN::getForce() const {
2225        DVect ret(sn_F_);
2226        ret += fictForce_;
2227        if (dpProps_)
2228            ret += dpProps_->dp_F_;
2229        return ret;
2230    }
2231
2232    DAVect ContactModelRBSN::getMomentOn1(const IContactMechanical *c) const {
2233        DAVect ret(sn_M_);
2234        if (c) {
2235            DVect force = getForce();
2236            c->updateResultingTorqueOn1Local(force,&ret);
2237        }
2238        return ret;
2239    }
2240
2241    DAVect ContactModelRBSN::getMomentOn2(const IContactMechanical *c) const {
2242        DAVect ret(sn_M_);
2243        if (c) {
2244            DVect force = getForce();
2245            c->updateResultingTorqueOn2Local(force,&ret);
2246        }
2247        return ret;
2248    }
2249    
2250    DAVect ContactModelRBSN::getModelMomentOn1() const {
2251        DAVect ret(sn_M_);
2252        return ret;
2253    }
2254    
2255    DAVect ContactModelRBSN::getModelMomentOn2() const {
2256        DAVect ret(sn_M_);
2257        return ret;
2258    }
2259    
2260    void ContactModelRBSN::objectPropsTypes(std::vector<std::pair<QString,InfoTypes>> *ret) const {
2261        ret->clear();
2262        ret->push_back({"kn",ScalarInfo});
2263        ret->push_back({"ks",ScalarInfo});
2264        ret->push_back({"krot",VectorInfo});
2265        ret->push_back({"fric",ScalarInfo});
2266        ret->push_back({"sn_bmul",ScalarInfo});
2267        ret->push_back({"sn_tmul",ScalarInfo});
2268        ret->push_back({"sn_mode",ScalarInfo});
2269        ret->push_back({"sn_force",VectorInfo});
2270        ret->push_back({"sn_moment",VectorInfo});
2271        ret->push_back({"sn_slip",ScalarInfo});
2272        ret->push_back({"sn_slipb",ScalarInfo});
2273        ret->push_back({"sn_slipt",ScalarInfo});
2274        ret->push_back({"sn_rmul",ScalarInfo});
2275        ret->push_back({"sn_radius",ScalarInfo});
2276        ret->push_back({"emod",ScalarInfo});
2277        ret->push_back({"kratio",ScalarInfo});
2278        ret->push_back({"dp_nratio",ScalarInfo});
2279        ret->push_back({"dp_sratio",ScalarInfo});
2280        ret->push_back({"dp_mode",ScalarInfo});
2281        ret->push_back({"dp_force",VectorInfo});
2282        ret->push_back({"sn_state",ScalarInfo});
2283        ret->push_back({"sn_ten",ScalarInfo});
2284        ret->push_back({"sn_shear",ScalarInfo});
2285        ret->push_back({"sn_coh",ScalarInfo});
2286        ret->push_back({"sn_fa",ScalarInfo});
2287        ret->push_back({"sn_mcf",ScalarInfo});
2288        ret->push_back({"sn_sigma",ScalarInfo});
2289        ret->push_back({"sn_tau",ScalarInfo});
2290        ret->push_back({"sn_area",ScalarInfo});
2291        ret->push_back({"user_area",ScalarInfo});
2292        ret->push_back({"rgap",ScalarInfo});
2293        ret->push_back({"sn_pois_force",VectorInfo});
2294        ret->push_back({"sn_pois",ScalarInfo});
2295        ret->push_back({"sn_non_diag",ScalarInfo});
2296        ret->push_back({"sn_cohres",ScalarInfo});
2297        ret->push_back({"sn_dil",ScalarInfo});
2298        ret->push_back({"sn_dilzero",ScalarInfo});
2299        ret->push_back({"sn_ndisp",ScalarInfo});
2300        ret->push_back({"sn_sdisp",VectorInfo});
2301        ret->push_back({"sn_cohdc",ScalarInfo});
2302        ret->push_back({"sn_heal",ScalarInfo});
2303        ret->push_back({"sn_tendc",ScalarInfo});
2304        ret->push_back({"sn_tabpos",ScalarInfo});
2305        ret->push_back({"sn_porp",ScalarInfo});
2306        ret->push_back({"sn_esigma",ScalarInfo});
2307        
2308    }
2309    
2310    void ContactModelRBSN::objectPropValues(std::vector<double> *ret,const IContact *con) const {
2311        const IContactMechanical *c(convert_getcast<IContactMechanical>(con));
2312        ret->clear();
2313        ret->push_back(kTran_);
2314        ret->push_back(kTran_/kRatio_);
2315        ret->push_back(kRot_.mag());
2316        ret->push_back(fric_);
2317        ret->push_back(sn_bmul_);
2318        ret->push_back(sn_tmul_);
2319        ret->push_back(sn_mode_);
2320        ret->push_back(sn_F_.mag());
2321        ret->push_back(sn_M_.mag());
2322        ret->push_back(sn_S_);
2323        ret->push_back(sn_BS_);
2324        ret->push_back(sn_TS_);
2325        ret->push_back(sn_rmul_);
2326        double Cmax1 = c->getEnd1Curvature().y();
2327        double Cmax2 = c->getEnd2Curvature().y();
2328        double d;
2329        if (!userArea_)
2330            d= sn_rmul_ * 1.0 / std::max(Cmax1, Cmax2);
2331        else {
2332#ifdef THREED
2333            d= std::sqrt(userArea_ / dPi);
2334#else
2335            d= userArea_ / 2.0;
2336#endif
2337        }
2338        ret->push_back(d);
2339        ret->push_back(E_);
2340        ret->push_back(kRatio_);
2341        ret->push_back(dpProps_ ? dpProps_->dp_nratio_ : 0);
2342        ret->push_back(dpProps_ ? dpProps_->dp_sratio_ : 0);
2343        ret->push_back(dpProps_ ? dpProps_->dp_mode_ : 0);
2344        ret->push_back(dpProps_ ? dpProps_->dp_F_.mag() : 0);
2345        ret->push_back(sn_state_);
2346        ret->push_back(sn_Ten());
2347        ret->push_back(shearStrength(computeGeomData(c->getEnd1Curvature(),c->getEnd2Curvature()).x()));
2348        ret->push_back(cohTable_[0].x());
2349        ret->push_back(std::atan(sn_fa_)/dDegrad);
2350        ret->push_back(sn_mcf_);
2351        ret->push_back(SMax(c->getEnd1Curvature(),c->getEnd2Curvature()).x());
2352        ret->push_back(SMax(c->getEnd1Curvature(),c->getEnd2Curvature()).y());
2353        ret->push_back(userArea_ ? userArea_ : computeGeomData(c->getEnd1Curvature(),c->getEnd2Curvature()).x());
2354        ret->push_back(userArea_);
2355        ret->push_back(rgap_);
2356        ret->push_back(fictForce_.mag());
2357        ret->push_back(poisson_);
2358        ret->push_back(poisOffDiag_ == false ? 0 : 1);
2359        ret->push_back(sn_cohres_);
2360        ret->push_back(std::atan(sn_dil_)/dDegrad);
2361        ret->push_back(sn_dilzero_);
2362        ret->push_back(sn_ndisp_);
2363        ret->push_back(sn_sdisp_.mag());
2364        ret->push_back(sn_cohdc());
2365        ret->push_back(sn_heal_);
2366        ret->push_back(sn_tendc());
2367        ret->push_back(sn_tabPos_+1);
2368        ret->push_back(sn_por_);
2369        ret->push_back(SMax(c->getEnd1Curvature(),c->getEnd2Curvature()).x() + sn_por_);
2370    }
2371
2372    DVect3 ContactModelRBSN::computeGeomData(const DVect2 &end1Curvature,const DVect2 &end2Curvature) const {
2373        double Cmax1 = end1Curvature.y();
2374        double Cmax2 = end2Curvature.y();
2375        double br = sn_rmul_ * 1.0 / std::max(Cmax1, Cmax2);
2376        if (userArea_)
2377#ifdef THREED
2378            br = std::sqrt(userArea_ / dPi);
2379#else
2380            br = userArea_ / 2.0;
2381#endif
2382        double br2 = br * br;
2383#ifdef THREED
2384        double area = dPi * br2;
2385        double bi = 0.25*area*br2;
2386#else
2387        double area = 2.0*br;
2388        double bi = 2.0*br*br2 / 3.0;
2389#endif
2390        return DVect3(area, bi, br);
2391    }
2392
2393    DVect2 ContactModelRBSN::SMax(const DVect2 &e1c,const DVect2 &e2c) const {
2394        DVect3 data = computeGeomData(e1c,e2c);
2395        double area = data.x();
2396        double bi = data.y();
2397        double br = data.z();
2398        /* maximum stresses */
2399        //double nsmax0 = -(totalForce.x() / area) + sn_mcf_* sqrt(sn_M_.y()*sn_M_.y() + sn_M_.z()*sn_M_.z()) * br / bi;
2400        double dbend = sqrt(sn_M_.y()*sn_M_.y() + sn_M_.z()*sn_M_.z());
2401        dbend *= sn_mcf_;
2402        double dtwist = sn_M_.x();
2403        DVect bfs(sn_F_);
2404        bfs.rx() = 0.0;
2405        double dbfs = bfs.mag();
2406        double nsmax = -((sn_F_.x()+fictForce_.x()) / area) + dbend * br / bi;
2407        double ssmax = dbfs / area + std::abs(dtwist) * 0.5* br / bi;
2408        return DVect2(nsmax, ssmax);
2409    }
2410
2411    double ContactModelRBSN::shearStrength(const double &area) const {
2412        double sig = -1.0*(sn_F_.x() + fictForce_.x()) / area;
2413        double nstr = (sn_state_ > 2 && sn_state_ != 6) ? tenTable_[0].x() : 0.0;
2414        return sig <= nstr ? cohTable_[0].x() - sn_fa_*sig
2415            : cohTable_[0].x() - sn_fa_*nstr;
2416    }
2417
2418
2419    double ContactModelRBSN::strainEnergy(double kn,double ks,double kb,double kt) const {
2420        double ret(0.0);
2421        if (kn)
2422            ret = 0.5 * (sn_F_.x()+fictForce_.x()) * (sn_F_.x()+fictForce_.x()) / kn;
2423        if (ks) {
2424            DVect tmp = sn_F_ + fictForce_;
2425            tmp.rx() = 0.0;
2426            double smag2 = tmp.mag2();
2427            ret += 0.5 * smag2 / ks;
2428        }
2429
2430        if (kt)
2431            ret += 0.5 * sn_M_.x() * sn_M_.x() / kt;
2432        if (kb) {
2433            DAVect tmp = sn_M_;
2434#ifdef THREED
2435            tmp.rx() = 0.0;
2436            double smag2 = tmp.mag2();
2437#else
2438            double smag2 = tmp.z() * tmp.z();
2439#endif
2440            ret += 0.5 * smag2 / kb;
2441        }
2442        return ret;
2443    }
2444
2445} // namespace cmodelsxd
2446// EoF

Top