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        string  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        string  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        string  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        string  getFishCallEvents() const override { 
180            return 
181                "contact_activated"
182                ",slip_change"
183                ",bond_break"; 
184        }
185
186        // Return the specified contact model property.
187        base::Property 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 base::Property &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        string  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        string  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 std::vector<base::Property> &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 string &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<string,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        uint32      sn_mode_ = 0;           // specifies absolute (0) or incremental (1) behavior for the the normal force
461        uint32      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    base::Property 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        base::Property 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 base::Property();
 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 base::Property &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<int64>())
 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<int64>())
 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<int64>())
 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    string  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 string();
 862        }
 863        assert(0);
 864        return string();
 865    }
 866
 867    bool ContactModelRBSN::setMethod(uint32 i,const std::vector<base::Property> &vl,IContact *con) {
 868        FP_S;
 869        // Apply the specified method.
 870        IContactMechanical *c(convert_getcast<IContactMechanical>(con));
 871        switch (i) {
 872        case kwAssignStiffness: {
 873                poisson_ = 0.0;
 874                if (vl.at(0).isNull())
 875                    throw Exception("Argument kn must be specified with method assign-stiffness in contact model {0}.",getName());
 876                double knpa = vl.at(0).toDouble();
 877                if (knpa<=0.0)
 878                    throw Exception("Negative or zero kn not allowed in contact model {0}.",getName());
 879                if (vl.at(1).isNull())
 880                    throw Exception("Argument kratio must be specified with method assign-stiffness in contact model {0}.",getName());
 881                kRatio_ = vl.at(1).toDouble();
 882                if (kRatio_<0.0) {
 883                    kRatio_ = 0.0;
 884                    throw Exception("Negative kratio not allowed in contact model {0}.",getName());
 885                }
 886                std::vector<DVect> pts;
 887                c->getJointGeometry(&pts);
 888                double area = 0.0;
 889#ifdef THREED
 890                // Step 1: get centroid and area
 891                for (int i=1; i<pts.size()-1; ++i) {
 892                    double a = (pts[0] - pts[i]).mag();
 893                    double b = (pts[0] - pts[i+1]).mag();
 894                    double c = (pts[i] - pts[i+1]).mag();
 895                    double la = 0.0;
 896                    if (b > a)
 897                        std::swap(a,b);
 898                    if (c > a)
 899                        std::swap(a,c);
 900                    if (c > b)
 901                        std::swap(b,c);
 902                    if (c - (a - b) >= 0)
 903                        la = 0.25 * sqrt((a+(b+c))*(c-(a-b))*(c+(a-b))*(a+(b-c)));
 904                    area += la;
 905                }
 906#else
 907                // Assume unit thickness in the out of plane direction
 908                area = (pts[1] - pts[0]).mag();
 909#endif
 910                userArea_ = area;
 911                kTran_ = knpa * area;
 912                E_ = kTran_ / area;
 913                kRot_ = DAVect(0.0);
 914                setInheritance(1,false);
 915                setInheritance(2,false);
 916                sn_mode_ = 1.0;
 917                FP_S;
 918                return true;
 919            }
 920        case kwStiffness: {
 921                FP_S;
 922                poisson_ = 0.0;
 923                if (vl.at(0).isNull())
 924                    throw Exception("Argument emod must be specified with method compute-stiffness in contact model {0}.",getName());
 925                E_ = vl.at(0).toDouble();
 926                if (E_<=0.0)
 927                    throw Exception("Negative or zero emod not allowed in contact model {0}.",getName());
 928                if (vl.at(1).isNull())
 929                    throw Exception("Argument poisson must be specified with method compute-stiffness in contact model {0}.",getName());
 930                poisson_ = vl.at(1).toDouble();
 931                if (poisson_ < 0.0) {
 932                    poisson_ = 0.0;
 933                    throw Exception("Negative poisson not allowed in contact model {0}.",getName());
 934                }
 935                const IBody *b1 = con->getEnd1()->getIBody();
 936                const IBody *b2 = con->getEnd2()->getIBody();
 937                //double vol1 = b1->getVolume();
 938                //double vol2 = b2->getVolume();
 939                //if (std::max(vol1,vol2) > 10.*std::min(vol1,vol2))
 940                //    poisson_ = 0.0;
 941                DVect pos1 = toDVect(b1->getIThing()->getLocation());
 942                DVect pos2 = toDVect(b2->getIThing()->getLocation()) + con->getOffSet();
 943                double dist = (pos1-pos2).mag();
 944                if (con->withWall())
 945                    dist = (pos1 - con->getPosition()).mag();
 946                double tol = std::max(pos1.abs().maxComp(),pos2.abs().maxComp())*limits<double>::epsilon()*1000;
 947                if (dist < tol) {
 948                    poisson_ = 0;
 949                    userArea_ = 0;
 950                    kTran_ = 0;
 951                    kRot_.fill(0);
 952                    FP_S;
 953                    return true;
 954                }
 955                std::vector<DVect> pts;
 956                FP_S;
 957                c->getJointGeometry(&pts);
 958                FP_S;
 959                double area = 0.0;
 960                DAVect inertia(0.0);
 961#ifdef THREED
 962                // Step 1: get centroid and area
 963                DVect cm(0.0);
 964                for (int i=1; i<pts.size()-1; ++i) {
 965                    DVect lcm = (pts[0] + pts[i] + pts[i+1])/3.0;
 966                    double a = (pts[0] - pts[i]).mag();
 967                    double b = (pts[0] - pts[i+1]).mag();
 968                    double c = (pts[i] - pts[i+1]).mag();
 969                    double la = 0.0;
 970                    if (b > a)
 971                        std::swap(a,b);
 972                    if (c > a)
 973                        std::swap(a,c);
 974                    if (c > b)
 975                        std::swap(b,c);
 976                    if (c - (a - b) >= 0)
 977                        la = 0.25 * sqrt((a+(b+c))*(c-(a-b))*(c+(a-b))*(a+(b-c)));
 978                    cm += lcm * la;
 979                    area += la;
 980                }
 981                FP_S;
 982                if (area == 0.0) {
 983                    poisson_ = 0;
 984                    userArea_ = 0;
 985                    kTran_ = 0;
 986                    kRot_.fill(0);
 987                    return true;
 988                }
 989                cm /= area;
 990                FP_S;
 991                // Step 2 - center it and put in the local system
 992                for (int i=0; i<pts.size(); ++i) {
 993                    pts[i] -= cm;
 994                    pts[i] = con->getLocalSystem().toLocal(pts[i]);
 995                }
 996                // Step 3: compute the polar inertia
 997                for (int i=0; i<pts.size(); ++i) {
 998                    int j = i < pts.size()-1 ? i+1 : 0;
 999                    double xi = pts[i].y();
1000                    double xip1 = pts[j].y();
1001                    double yi = pts[i].z();
1002                    double yip1 = pts[j].z();
1003                    double frnt = (xi*yip1-xip1*yi);
1004                    inertia.ry() += frnt*(xi*xi+xi*xip1+xip1*xip1);
1005                    inertia.rz() += frnt*(yi*yi+yi*yip1+yip1*yip1);
1006                }
1007                inertia.ry() = std::abs(inertia.y() / 12.);
1008                inertia.rz() = std::abs(inertia.z() / 12.);
1009                inertia.rx() = inertia.y() + inertia.z();
1010
1011#else
1012                // Assume unit thickness in the out of plane direction
1013                area = (pts[1] - pts[0]).mag();
1014                inertia.rz() = area*area*area/12.;
1015#endif
1016                userArea_ = area;
1017                kTran_ = E_ * area / dist;
1018                kRot_ = inertia *E_ / dist;
1019                setInheritance(1,false);
1020                setInheritance(2,false);
1021                sn_mode_ = 1.0;
1022                FP_S;
1023                return true;
1024            }
1025        case kwBond: {
1026                if (sn_state_ == 3) return false;
1027                double mingap = -1.0 * limits<double>::max();
1028                double maxgap = 0;
1029                if (vl.at(0).canConvert<double>())
1030                    maxgap = vl.at(0).toDouble();
1031                else if (vl.at(0).canConvert<DVect2>()) {
1032                    DVect2 value = vl.at(0).value<DVect2>();
1033                    mingap = value.minComp();
1034                    maxgap = value.maxComp();
1035                }
1036                else if (!vl.at(0).isNull())
1037                    throw Exception("gap value {0} not recognized in method bond in contact model {1}.", vl.at(1), getName());
1038                double gap = c->getGap();
1039                if (gap >= mingap && gap <= maxgap) {
1040                    sn_state_ = 3;
1041                    sn_mode_ = 1;
1042                    FP_S;
1043                    return true;
1044                }
1045                FP_S;
1046                return false;
1047            }
1048        case kwUnbond: {
1049                if (sn_state_ == 0) return false;
1050                double mingap = -1.0 * limits<double>::max();
1051                double maxgap = 0;
1052                if (vl.at(0).canConvert<double>())
1053                    maxgap = vl.at(0).toDouble();
1054                else if (vl.at(0).canConvert<DVect2>()) {
1055                    DVect2 value = vl.at(0).value<DVect2>();
1056                    mingap = value.minComp();
1057                    maxgap = value.maxComp();
1058                }
1059                else if (!vl.at(0).isNull())
1060                    throw Exception("gap value {0} not recognized in method unbond in contact model {1}.", vl.at(0), getName());
1061                double gap = c->getGap();
1062                if (gap >= mingap && gap <= maxgap) {
1063                    sn_state_ = 0;
1064                    FP_S;
1065                    return true;
1066                }
1067                FP_S;
1068                return false;
1069            }
1070        case kwArea: {
1071                if (!userArea_) {
1072                    double rsq = calcRSQ(c);
1073#ifdef THREED
1074                    userArea_ = rsq * rsq * dPi;
1075#else
1076                    userArea_ = rsq * 2.0;
1077#endif
1078                }
1079                FP_S;
1080                return true;
1081            }
1082        case kwResetDisp:
1083            sn_ndisp_ = 0.0;
1084            for (int i=1; i<dim; ++i)
1085                sn_sdisp_.rdof(i) = 0;
1086            break;
1087        }
1088        FP_S;
1089        return false;
1090    }
1091
1092    double ContactModelRBSN::getEnergy(uint32 i) const {
1093        // Return an energy value.
1094        double ret(0.0);
1095        if (!energies_)
1096            return ret;
1097        switch (i) {
1098        case kwEStrain:    return energies_->estrain_;
1099        case kwESlip:      return energies_->eslip_;
1100        case kwEDashpot:   return energies_->edashpot_;
1101        }
1102        assert(0);
1103        return ret;
1104    }
1105
1106    bool ContactModelRBSN::getEnergyAccumulate(uint32 i) const {
1107        // Returns TRUE if the corresponding energy is accumulated or FALSE otherwise.
1108        switch (i) {
1109        case kwEStrain:   return false;
1110        case kwESlip:     return true;
1111        case kwEDashpot:  return true;
1112        }
1113        assert(0);
1114        return false;
1115    }
1116
1117    void ContactModelRBSN::setEnergy(uint32 i,const double &d) {
1118        // Set an energy value.
1119        if (!energies_) return;
1120        switch (i) {
1121        case kwEStrain:    energies_->estrain_ = d;   return;
1122        case kwESlip:      energies_->eslip_   = d;   return;
1123        case kwEDashpot:   energies_->edashpot_= d;   return;
1124        }
1125        assert(0);
1126        return;
1127    }
1128
1129    bool ContactModelRBSN::validate(ContactModelMechanicalState *state,const double &) {
1130        // Validate the / Prepare for entry into ForceDispLaw. The validate function is called when:
1131        // (1) the contact is created, (2) a property of the contact that returns a true via
1132        // the setProperty method has been modified and (3) when a set of cycles is executed
1133        // via the {cycle N} command.
1134        // Return value indicates contact activity (TRUE: active, FALSE: inactive).
1135        assert(state);
1136        const IContactMechanical *c = state->getMechanicalContact();
1137        assert(c);
1138
1139        if (state->trackEnergy_)
1140            activateEnergy();
1141
1142        if (inheritanceField_ & KnMask)
1143            updateKn(c);
1144        if (inheritanceField_ & KsMask)
1145            updateKs(c);
1146        if (inheritanceField_ & FricMask)
1147            updateFric(c);
1148
1149        updateStiffness(state);
1150        return checkActivity(state->gap_);
1151    }
1152
1153    static const string knstr("kn");
1154    bool ContactModelRBSN::updateKn(const IContactMechanical *con) {
1155        assert(con);
1156        base::Property v1 = con->getEnd1()->getProperty(knstr);
1157        base::Property v2 = con->getEnd2()->getProperty(knstr);
1158        if (!v1.isValid() || !v2.isValid())
1159            return false;
1160        double kn1 = v1.toDouble();
1161        double kn2 = v2.toDouble();
1162        double val = kTran_;
1163        if (kn1 && kn2)
1164            kTran_ = kn1*kn2/(kn1+kn2);
1165        else if (kn1)
1166            kTran_ = kn1;
1167        else if (kn2)
1168            kTran_ = kn2;
1169        return ( (kTran_ != val) );
1170    }
1171
1172    static const string ksstr("ks");
1173    bool ContactModelRBSN::updateKs(const IContactMechanical *con) {
1174        assert(con);
1175        base::Property v1 = con->getEnd1()->getProperty(ksstr);
1176        base::Property v2 = con->getEnd2()->getProperty(ksstr);
1177        if (!v1.isValid() || !v2.isValid())
1178            return false;
1179        double ks1 = v1.toDouble();
1180        double ks2 = v2.toDouble();
1181        double val = kTran_;
1182        if (ks1 && ks2)
1183            kTran_ = ks1*ks2/(ks1+ks2);
1184        else if (ks1)
1185            kTran_ = ks1;
1186        else if (ks2)
1187            kTran_ = ks2;
1188        return ( (kTran_ != val) );
1189    }
1190
1191    static const string fricstr("fric");
1192    bool ContactModelRBSN::updateFric(const IContactMechanical *con) {
1193        assert(con);
1194        base::Property v1 = con->getEnd1()->getProperty(fricstr);
1195        base::Property v2 = con->getEnd2()->getProperty(fricstr);
1196        if (!v1.isValid() || !v2.isValid())
1197            return false;
1198        double fric1 = std::max(0.0,v1.toDouble());
1199        double fric2 = std::max(0.0,v2.toDouble());
1200        double val = fric_;
1201        fric_ = std::min(fric1,fric2);
1202        return ( (fric_ != val) );
1203    }
1204
1205    bool ContactModelRBSN::endPropertyUpdated(const string &name,const IContactMechanical *c) {
1206        // The endPropertyUpdated method is called whenever a surface property (with a name
1207        // that matches an inheritable contact model property name) of one of the contacting
1208        // pieces is modified. This allows the contact model to update its associated
1209        // properties. The return value denotes whether or not the update has affected
1210        // the time step computation (by having modified the translational or rotational
1211        // tangent stiffnesses). If true is returned, then the time step will be recomputed.
1212        assert(c);
1213        StringList availableProperties = split(replace(simplified(getProperties())," ",""),",");
1214        auto idx = findRegex(availableProperties,name);
1215        if (idx==string::npos) return false;
1216        idx += 1;
1217        bool ret=false;
1218        switch(idx) {
1219        case kwKn:  { //kn
1220                if (inheritanceField_ & KnMask)
1221                    ret = updateKn(c);
1222                break;
1223            }
1224        case kwKs:  { //ks
1225                if (inheritanceField_ & KsMask)
1226                    ret =updateKs(c);
1227                break;
1228            }
1229        case kwFric:  { //fric
1230                if (inheritanceField_ & FricMask)
1231                    updateFric(c);
1232                break;
1233            }
1234        }
1235        return ret;
1236    }
1237
1238    ContactModelRBSN::StiffData ContactModelRBSN::computeStiffData(ContactModelMechanicalState *state) const {
1239        // Update contact data
1240        //double Cmin1 = state->end1Curvature_.x();
1241        double Cmax1 = state->end1Curvature_.y();
1242        double Cmax2 = state->end2Curvature_.y();
1243        double br = sn_rmul_ * 1.0 / std::max(Cmax1, Cmax2);
1244        if (userArea_)
1245#ifdef THREED
1246            br = std::sqrt(userArea_ / dPi);
1247#else
1248            br = userArea_ / 2.0;
1249#endif
1250        StiffData ret;
1251        ret.reff_ = br;
1252        ret.trans_ = DVect2(kTran_,kTran_/kRatio_);
1253        ret.ang_ = kRot_;
1254        return ret;
1255    }
1256
1257    void ContactModelRBSN::updateStiffness(ContactModelMechanicalState *state) {
1258        // first compute stiffness data
1259        StiffData stiff = computeStiffData(state);
1260        // Now calculate effective stiffness
1261        DVect2 retT = stiff.trans_;
1262        // correction if viscous damping active
1263        if (dpProps_) {
1264            DVect2 correct(1.0);
1265            if (dpProps_->dp_nratio_)
1266                correct.rx() = sqrt(1.0+dpProps_->dp_nratio_*dpProps_->dp_nratio_) - dpProps_->dp_nratio_;
1267            if (dpProps_->dp_sratio_)
1268                correct.ry() = sqrt(1.0+dpProps_->dp_sratio_*dpProps_->dp_sratio_) - dpProps_->dp_sratio_;
1269            retT /= (correct*correct);
1270        }
1271        effectiveTranslationalStiffness_ = retT;
1272        // Effective rotational stiffness (bending and twisting)
1273        effectiveRotationalStiffness_ = stiff.ang_;
1274    }
1275
1276    bool ContactModelRBSN::forceDisplacementLaw(ContactModelMechanicalState *state,const double &timestep) {
1277        assert(state);
1278
1279        if (state->activated()) {
1280            // The contact was just activated from an inactive state
1281            // Trigger the FISH callback if one is hooked up to the
1282            // contact_activated event.
1283            if (cmEvents_[fActivated] >= 0) {
1284                auto c = state->getContact();
1285                std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()) };
1286                IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1287                fi->setCMFishCallArguments(c,arg,cmEvents_[fActivated]);
1288            }
1289        }
1290        updateStiffness(state);
1291        // accumulate shear displacement for dilation
1292        sn_ndisp_ += state->relativeTranslationalIncrement_.x();
1293        DVect shearInc =  state->relativeTranslationalIncrement_;
1294        shearInc.rx() = 0;
1295        sn_sdisp_.ry() += shearInc.mag();
1296
1297        if (isBonded()) return FDLawBonded(state, timestep);
1298        else return FDLawUnBonded(state, timestep);
1299
1300    }
1301    
1302    bool ContactModelRBSN::thermalCoupling(ContactModelMechanicalState*, ContactModelThermalState* ts, IContactThermal*, const double&) {
1303        // Account for thermal expansion in incremental mode
1304        if (sn_mode_ == 0 || ts->gapInc_ == 0.0) return false;
1305        DVect finc(0.0);
1306        finc.rx() = kTran_ * ts->gapInc_;
1307        sn_F_ -= finc;
1308        return true;
1309    }
1310
1311    bool ContactModelRBSN::FDLawBonded(ContactModelMechanicalState *state, const double &timestep) {
1312        // initialize ... get the geometry information
1313        DVect3 geom = computeGeomData(state->end1Curvature_,state->end2Curvature_);
1314        double area = geom.x();
1315        double bi = geom.y();
1316        double br = geom.z();
1317        double kn = kTran_;
1318        double ks = kn / kRatio_;
1319        double kb = kRot_.z();
1320#if DIM==3
1321        kb = std::sqrt(kb*kb + kRot_.y()*kRot_.y());
1322        double kt = kRot_.x();
1323#else
1324        double kt = 0.0;
1325#endif
1326
1327        DVect totalForce = sn_F_ + fictForce_;
1328
1329        //double nsmax0 = -(totalForce.x() / area) + sn_mcf_* sqrt(sn_M_.y()*sn_M_.y() + sn_M_.z()*sn_M_.z()) * br / bi;
1330
1331        // Relative translational/rotational displacement increments
1332        DVect  trans = state->relativeTranslationalIncrement_;
1333        DAVect ang = state->relativeAngularIncrement_;
1334
1335        // Store previous force and moment
1336        DVect  sn_F_old = totalForce;
1337        sn_F_old.rx() -= sn_por_ * area;
1338        DAVect sn_M_old = sn_M_;
1339
1340        DVect theStiff(ks);
1341        theStiff.rx() = kn;
1342        sn_F_ -= trans * theStiff;
1343        sn_M_ -= ang * kRot_;
1344        if (poisson_ != 0.0 and (state->end1Volume_ or state->end2Volume_)) {
1345            //const IContact *con = state->getContact();
1346            //const IBody *b1 = con->getEnd1()->getIBody();
1347            //const IBody *b2 = con->getEnd2()->getIBody();
1348            auto stress11 = state->end1Stress_;
1349            auto stress22 = state->end2Stress_;
1350            double vol1 = state->end1Volume_;
1351            double vol2 = state->end2Volume_;
1352            double ms = 0.0;
1353            for (int i=0; i<stress11.size(); ++i)  {
1354                stress11[i] = (stress11[i]*vol1 + stress22[i]*vol2)/(vol1 + vol2);
1355                ms = std::max(ms,abs(stress11[i]));
1356            }
1357            DMatrix<dim,dim> stresst(0.0);
1358#ifdef THREED
1359            stresst.get(0,0) = -poisson_*stress11[1] - poisson_*stress11[2];
1360            stresst.get(1,1) = -poisson_*stress11[0] - poisson_*stress11[2];
1361#else
1362            stresst.get(0,0) = -poisson_*stress11[1];
1363            stresst.get(1,1) = -poisson_*stress11[0];
1364#endif
1365#ifdef THREED
1366            stresst.get(2,2) = -poisson_*stress11[0] - poisson_*stress11[1];
1367#endif
1368            if (poisOffDiag_) {
1369#ifdef THREED
1370                double sxy = stress11[3];
1371                double szx = stress11[4];
1372                double syz = stress11[5];
1373                stresst.get(0,1) = poisson_ * sxy;
1374                stresst.get(1,0) = stresst.get(0,1);
1375                stresst.get(0,2) = poisson_ * szx;
1376                stresst.get(2,0) = stresst.get(0,2);
1377                stresst.get(1,2) = poisson_ * syz;
1378                stresst.get(2,1) = stresst.get(1,2);
1379#else
1380                double sxy = stress11[2];
1381                stresst.get(0,1) = poisson_ * sxy;
1382                stresst.get(1,0) = stresst.get(0,1);
1383#endif
1384            }
1385            
1386            
1387            DVect norm = state->axes_.e1();
1388            DVect traction = stresst * norm * userArea_;
1389            DVect shear(0.0);
1390            shear.ry() = 1.0;
1391            shear = state->axes_.toGlobal(shear);
1392#ifdef THREED
1393            DVect ns = state->axes_.toGlobal(DVect(0.,0.,1.));
1394            fictForce_ = DVect(norm|traction,shear|traction,ns|traction);
1395#else
1396            fictForce_ = DVect(norm|traction,shear|traction);
1397#endif
1398            if (forceSet_ && ms) {
1399                forceSet_ = false;
1400                sn_F_ -= fictForce_;
1401            }
1402        }
1403        FP_S;
1404        double porForce = sn_por_ * area;
1405        sn_F_.rx() -= porForce;
1406        totalForce = sn_F_ + fictForce_;
1407
1408        if (state->canFail_) {
1409            double dbend = sqrt(sn_M_.y()*sn_M_.y() + sn_M_.z()*sn_M_.z());
1410            double nsmax = -(totalForce.x() / area) + sn_mcf_*dbend * br / bi;
1411            bool failed = false;
1412            if (sn_state_ == 3 || sn_state_ == 5) {
1413                double compVal = sn_state_ == 3 ? tenTable_[0].x() : transTen_;
1414                if (nsmax >= compVal ) {
1415                    if (tenTable_.back().y() < limits<double>::epsilon()*100)
1416                        failed = true;
1417                    else {
1418                        if (sn_state_ == 3)
1419                            sn_elong_ = 0;
1420                        transTen_ = compVal;
1421                        sn_state_ = 4;
1422                    }
1423                }
1424            }
1425            FP_S;
1426
1427            if (sn_state_ == 4) {
1428                sn_elong_ += trans.x();
1429                sn_elong_ = std::max(0.0,sn_elong_);
1430                int ww = -1;
1431                if (sn_elong_ <= tenTable_.back().y()) {
1432                    for (int i=0; i<tenTable_.size(); ++i) {
1433                        if (tenTable_[i].y() >= sn_elong_) {
1434                            ww = i;
1435                            break;
1436                        }
1437                    }
1438                } else
1439                    ww = tenTable_.size() - 1;
1440                if (ww > 0) {
1441                    //double factor = std::min(1.0, sn_elong_ / tenTable_[ww].y());
1442                    double prevVal = ww == 1 ? 1 : tenTable_[ww-1].x();
1443                    double curVal =  tenTable_[ww].x();
1444                    double prevElong = tenTable_[ww-1].y();
1445                    double curElong = tenTable_[ww].y();
1446                    double slope = (curVal - prevVal)/(curElong - prevElong);
1447                    FP_S;
1448                    //y-y0 = m(x-x0)
1449                    double nstren = slope * (sn_elong_ - prevElong) + prevVal;
1450                    if (nstren <= 0)
1451                        failed = true;
1452                    else {
1453                        nstren *= tenTable_[0].x();
1454                        if (nsmax >= nstren || slope > 0) {
1455                            double fac = nstren / nsmax;
1456                            sn_F_.rx() *= fac;
1457#if DIM==3
1458                            sn_M_.ry() *= fac;
1459#endif
1460                            sn_M_.rz() *= fac;
1461                            fictForce_.rx() *= fac;
1462                        } else {
1463                            sn_state_ = 5;
1464                            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;
1465                        }
1466                    }
1467                }
1468            }
1469            if (sn_state_ == 6 && nsmax >= 0)
1470                failed = true;
1471            FP_S;
1472            if (failed) {
1473                // Failed in tension
1474                double se = strainEnergy(kn, ks, kb, kt); // bond strain energy at the onset of failure
1475                sn_state_ = 1;
1476                sn_F_.fill(0.0);
1477                sn_M_.fill(0.0);
1478                failed = true;
1479                fictForce_ = DVect(0.0);
1480                //sn_F_.rx() = -sn_tenres_ * area;
1481                if (cmEvents_[fBondBreak] >= 0) {
1482                    auto c = state->getContact();
1483                    std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
1484                                                         fish::Parameter((int64)sn_state_),
1485                                                         fish::Parameter(nsmax),
1486                                                         fish::Parameter(se)
1487                                                       };
1488                    IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1489                    fi->setCMFishCallArguments(c,arg,cmEvents_[fBondBreak]);
1490                }
1491            }
1492            FP_S;
1493
1494            if (!failed) {
1495                /* check for shear failure */
1496                double dtwist = sn_M_.x();
1497                DVect bfs(totalForce);
1498                bfs.rx() = 0.0;
1499                double dbfs = bfs.mag();
1500                double ssmax = dbfs / area + sn_mcf_*std::abs(dtwist) * 0.5* br / bi;
1501                double ss = shearStrength(area);
1502                FP_S;
1503                if (ss < 0)
1504                    ss = 0;
1505                if (ss <= ssmax) {
1506                    // strength when it breaks for
1507                    // Failed in shear
1508                    double se = strainEnergy(kn, ks, kb, kt); // bond strain energy at the onset of failure
1509                    sn_state_ = 2;
1510                    fictForce_ = DVect(0.0);
1511                    FP_S;
1512                    sn_F_ = totalForce;
1513                    if (cmEvents_[fBondBreak] >= 0) {
1514                        auto c = state->getContact();
1515                        std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
1516                                                             fish::Parameter((int64)sn_state_),
1517                                                             fish::Parameter(ss),
1518                                                             fish::Parameter(se)
1519                                                           };
1520                        IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1521                        fi->setCMFishCallArguments(c,arg,cmEvents_[fBondBreak]);
1522                    }
1523                    double mm = 0.0;
1524                    for (int i=1; i<dim; ++i)
1525                        mm += trans[i]*trans[i];
1526                    sn_sdisp_.rx() = std::sqrt(mm);
1527                    double crit = sn_F_.x() * fric_ + sn_cohres_ * area;
1528                    if (sn_cohdc() > std::numeric_limits<double>::epsilon()) {
1529                        int ww = -1;
1530                        if (sn_sdisp_.x() <= sn_cohdc()) {
1531                            for (int i=0; i<cohTable_.size(); ++i) {
1532                                if (cohTable_[i].y() >= sn_sdisp_.x()) {
1533                                    ww = i;
1534                                    break;
1535                                }
1536                            }
1537                        } else
1538                            ww = cohTable_.size() - 1;
1539                        if (ww > 0) {
1540                            double prevVal = ww == 1 ? 1: cohTable_[ww-1].x() ;//critBreak_ : cohTable_[ww-1].x() + sn_F_.x() * fric_;
1541                            double curVal =  cohTable_[ww].x();//cohTable_[ww].x() + sn_F_.x() * fric_;
1542                            double prevShear = cohTable_[ww-1].y();
1543                            double curShear = cohTable_[ww].y();
1544                            double slope = (curVal - prevVal)/(curShear - prevShear);
1545                            // should go from 0 to 1
1546                            double mval = slope * (sn_sdisp_.x() - prevShear) + prevVal;
1547                            double fact = std::max(0.,std::min(1.0,1.0 - mval));
1548                            assert(fact >= 0 && fact <= 1.0);
1549                            double theFric = fric_;
1550                            if (sn_fa_)
1551                                theFric = sn_fa_ + (fric_ - sn_fa_)*fact;
1552                            double theCoh = sn_Coh();
1553                            if (theCoh)
1554                                theCoh = sn_Coh() + (sn_cohres_ - sn_Coh())*fact;
1555                            crit = sn_F_.x() * theFric + theCoh * geom.x();
1556                        }
1557                    }
1558
1559                    // Resolve sliding.
1560                    FP_S;
1561                    if (crit < 0)
1562                        crit = 0;
1563                    DVect sforce = sn_F_; sforce.rx() = 0.0;
1564                    // The is the magnitude of the shear force.
1565                    double sfmag = sforce.mag();
1566                    // Sliding occurs when the magnitude of the shear force is greater than the
1567                    // critical value.
1568                    if (sfmag > crit) {
1569                        // Lower the shear force to the critical value for sliding.
1570                        double rat = crit / sfmag;
1571                        sforce *= rat;
1572                        sforce.rx() = sn_F_.x();
1573                        sn_F_ = sforce;
1574                        sn_S_ = true;
1575                    }
1576                    if (sn_S_) {
1577                        if (sn_dil_ > 0) {
1578                            double zdd = sn_dilzero_ != 0 ? sn_dilzero_ : limits<double>::max();
1579                            double usm = sn_sdisp_.y();
1580                            if (usm < zdd) {
1581                                double sInc = 0.0;
1582                                for (int i=1; i<dim; ++i)
1583                                    sInc += trans.dof(i)*trans.dof(i);
1584                                sInc = std::sqrt(sInc);
1585                                sn_F_.rx() += kTran_ * sn_dil_ * sInc;
1586                            }
1587                        }
1588                    }
1589
1590                    // Resolve bending
1591                    crit = sn_bmul_*2.1*0.25*br*std::abs(sn_F_.x()); // Jiang 2015
1592                    FP_S;
1593                    DAVect test = sn_M_;
1594#if DIM==3
1595                    test.rx() = 0.0;
1596#endif
1597                    double tmag = test.mag();
1598                    if (tmag > crit) {
1599                        // Lower the bending moment to the critical value for sliding.
1600                        double rat = crit / tmag;
1601                        test *= rat;
1602                        sn_BS_ = true;
1603                    }
1604                    sn_M_.rz() = test.z();
1605#if DIM==3
1606                    sn_M_.ry() = test.y();
1607                    // Resolve twisting
1608                    crit = sn_tmul_ * 0.65*fric_* br*std::abs(sn_F_.x()) ; // Jiang 2015
1609                    tmag = std::abs(sn_M_.x());
1610                    if (tmag > crit) {
1611                        // Lower the shear force to the critical value for sliding.
1612                        double rat = crit / tmag;
1613                        tmag = sn_M_.x() * rat;
1614                        sn_TS_ = true;
1615                    }
1616                    sn_M_.rx() = tmag;
1617                    FP_S;
1618#endif
1619                }
1620            }
1621        }
1622        sn_F_old.rx() += porForce;
1623        sn_F_.rx() += porForce;
1624        totalForce = sn_F_ + fictForce_;
1625        FP_S;
1626
1627        // Account for dashpot forces if the dashpot structure has been defined.
1628        if (dpProps_) {
1629            dpProps_->dp_F_.fill(0.0);
1630            double vcn(0.0), vcs(0.0);
1631            // Calculate the damping coefficients.
1632            vcn = dpProps_->dp_nratio_ * 2.0 * sqrt((state->inertialMass_*(kn)));
1633            vcs = dpProps_->dp_sratio_ * 2.0 * sqrt((state->inertialMass_*(ks)));
1634            // First damp the shear components
1635            for (int i = 1; i<dim; ++i)
1636                dpProps_->dp_F_.rdof(i) = trans.dof(i) * (-1.0* vcs) / timestep;
1637            // Damp the normal component
1638            dpProps_->dp_F_.rx() -= trans.x() * vcn / timestep;
1639            // Need to change behavior based on the dp_mode.
1640            if ((dpProps_->dp_mode_ == 1 || dpProps_->dp_mode_ == 3)) {
1641                // Limit in tension if not bonded.
1642                if (sn_state_ < 3 && (dpProps_->dp_F_.x() + totalForce.x() < 0))
1643                    dpProps_->dp_F_.rx() = -totalForce.rx();
1644            }
1645            if (sn_S_ && dpProps_->dp_mode_ > 1) {
1646                // Limit in shear if sliding.
1647                double dfn = dpProps_->dp_F_.rx();
1648                dpProps_->dp_F_.fill(0.0);
1649                dpProps_->dp_F_.rx() = dfn;
1650            }
1651        }
1652        FP_S;
1653
1654        //Compute energies if energy tracking has been enabled.
1655        if (state->trackEnergy_) {
1656            assert(energies_);
1657            energies_->estrain_ = 0.0;
1658            if (kn)
1659                // Calculate the strain energy.
1660                energies_->estrain_ = 0.5*totalForce.x()*totalForce.x() / kn;
1661            if (ks) {
1662                DVect s = totalForce;
1663                s.rx() = 0.0;
1664                double smag2 = s.mag2();
1665                // Add the shear component of the strain energy.
1666                energies_->estrain_ += 0.5*smag2 / ks;
1667
1668                if (sn_S_) {
1669                    // If sliding calculate the slip energy and accumulate it.
1670                    sn_F_old.rx() = 0.0;
1671                    DVect avg_F_s = (s + sn_F_old)*0.5;
1672                    DVect u_s_el = (s - sn_F_old) / ks;
1673                    DVect u_s(0.0);
1674                    for (int i = 1; i < dim; ++i)
1675                        u_s.rdof(i) = trans.dof(i);
1676                    energies_->eslip_ -= std::min(0.0, (avg_F_s | (u_s + u_s_el)));
1677                }
1678            }
1679            // Add the bending/twisting resistance energy contributions.
1680            if (kb) {
1681                DAVect tmp = sn_M_;
1682#ifdef THREED
1683                tmp.rx() = 0.0;
1684#endif
1685                energies_->estrain_ += 0.5*tmp.mag2() / kb;
1686                if (sn_BS_) {
1687                    //  accumulate bending slip energy.
1688                    DAVect tmp_old = sn_M_old;
1689#ifdef THREED
1690                    tmp_old.rx() = 0.0;
1691#endif
1692                    DAVect avg_M = (tmp + tmp_old)*0.5;
1693                    DAVect t_s_el = (tmp - tmp_old) / kb;
1694                    energies_->eslip_ -= std::min(0.0, (avg_M | (ang + t_s_el)));
1695                }
1696            }
1697#ifdef THREED
1698            if (kt) {
1699                double mt = std::abs(sn_M_.x());
1700                energies_->estrain_ += 0.5*mt*mt / kt;
1701                if (sn_TS_) {
1702                    //  accumulate twisting slip energy.
1703                    DAVect tmp(0.0);
1704                    DAVect tmp_old(0.0);
1705                    tmp.rx() = sn_M_.x();
1706                    tmp_old.rx() = sn_M_old.x();
1707                    DAVect avg_M = (tmp + tmp_old)*0.5;
1708                    DAVect t_s_el = (tmp - tmp_old) / kt;
1709                    energies_->eslip_ -= std::min(0.0, (avg_M | (ang + t_s_el)));
1710                }
1711            }
1712#endif
1713
1714            if (dpProps_) {
1715                // Calculate damping energy (accumulated) if the dashpots are active.
1716                energies_->edashpot_ -= dpProps_->dp_F_ | trans;
1717            }
1718        }
1719
1720        // This is just a sanity check to ensure, in debug mode, that the force/moment aren't wonky.
1721        assert(sn_F_ == sn_F_);
1722        assert(sn_M_ == sn_M_);
1723        assert(fictForce_ == fictForce_);
1724        FP_S;
1725        return true;
1726    }
1727
1728    bool ContactModelRBSN::FDLawUnBonded(ContactModelMechanicalState *state, const double &timestep) {
1729        DVect3 geom = computeGeomData(state->end1Curvature_,state->end2Curvature_);
1730        double br = geom.z();
1731        // Relative translational/rotational displacement increments
1732        DVect  trans = state->relativeTranslationalIncrement_;
1733        DAVect ang   = state->relativeAngularIncrement_;
1734        double overlap = rgap_ - state->gap_;
1735        double correction = 1.0;
1736        if (state->activated() && sn_mode_ == 0 && trans.x()) {
1737                correction = -1.0*overlap / trans.x();
1738                if (correction < 0)
1739                    correction = 1.0;
1740        }
1741
1742        // Store previous force and moment
1743        DVect  sn_F_old = sn_F_;
1744        double porForce = sn_por_ * geom.x();
1745        sn_F_old.rx() -= porForce;
1746        DAVect sn_M_old = sn_M_;
1747
1748        double kb = kRot_.z();
1749#if DIM==3
1750        double kt = kRot_.x();
1751        //kb = std::sqrt(kb * kb + kRot_.y() * kRot_.y());
1752#endif
1753        // absolute/incremental normal force calculation
1754        DVect theStiff(kTran_/kRatio_);
1755        theStiff.rx() = kTran_;
1756
1757        if (sn_mode_==0)
1758            sn_F_.rx() = overlap * theStiff.x();
1759        else
1760            sn_F_.rx() -= trans.x() * theStiff.x();
1761        // Normal force can only be positive if unbonded
1762        sn_F_.rx() = std::max(0.0, sn_F_.x()) - porForce;
1763
1764        // Calculate the trial shear force.
1765        DVect sforce(0.0);
1766        // dim holds the dimension (e.g., 2 for 2D and 3 for 3D)
1767        // Loop over the shear components (note: the 0 component is the normal component)
1768        // and calculate the shear force.
1769        for (int i = 1; i<dim; ++i)
1770            sforce.rdof(i) = sn_F_.dof(i) - trans.dof(i) * theStiff.dof(i);
1771
1772        // Calculate the trial moment.
1773        DAVect mom = sn_M_ - ang*kRot_;
1774
1775        // If the SOLVE ELASTIC command is given then the
1776        // canFail state is set to FALSE. Otherwise it is always TRUE.
1777        if (state->canFail_) {
1778            bool changed = false;
1779            // Resolve sliding. This is the normal force multiplied by the coefficient of friction.
1780            bool slip_changed = false;
1781
1782            double crit = sn_F_.x() * fric_ + sn_cohres_ * geom.x();
1783            if (sn_state_ != 0) {
1784                bool doComp = true; 
1785                if (!sn_S_) {
1786                    if (sn_heal_) {
1787                        sn_sdisp_.rx() = 0;
1788                        crit = sn_F_.x() * sn_fa_ + cohTable_[0].x() * geom.x();
1789                        doComp = false;
1790                    }
1791                }
1792                if (doComp) {
1793                    double mm = 0.0;
1794                    for (int i=1; i<dim; ++i)
1795                        mm += trans[i]*trans[i];
1796                    sn_sdisp_.rx() += std::sqrt(mm);
1797                    if (sn_cohdc() > std::numeric_limits<double>::epsilon()) {
1798                        int ww = -1;
1799                        if (sn_sdisp_.x() <= sn_cohdc()) {
1800                            for (int i=0; i<cohTable_.size(); ++i) {
1801                                if (cohTable_[i].y() >= sn_sdisp_.x()) {
1802                                    ww = i;
1803                                    break;
1804                                }
1805                            }
1806                        } else
1807                            ww = cohTable_.size() - 1;
1808                        if (ww > 0) {
1809                            double prevVal = ww == 1 ? 1: cohTable_[ww-1].x() ;//critBreak_ : cohTable_[ww-1].x() + sn_F_.x() * fric_;
1810                            double curVal =  cohTable_[ww].x();//cohTable_[ww].x() + sn_F_.x() * fric_;
1811                            double prevShear = cohTable_[ww-1].y();
1812                            double curShear = cohTable_[ww].y();
1813                            double slope = (curVal - prevVal)/(curShear - prevShear);
1814                            // should go from 0 to 1
1815                            double mval = slope * (sn_sdisp_.x() - prevShear) + prevVal;
1816                            double fact = std::max(0.,std::min(1.0,1.0 - mval));
1817                            assert(fact >= 0 && fact <= 1.0);
1818                            double theFric = fric_;
1819                            if (sn_fa_)
1820                                theFric = sn_fa_ + (fric_ - sn_fa_)*fact;
1821                            double theCoh = sn_Coh();
1822                            if (theCoh)
1823                                theCoh = sn_Coh() + (sn_cohres_ - sn_Coh())*fact;
1824                            crit = sn_F_.x() * theFric + theCoh * geom.x();
1825                        }
1826                    }
1827                }
1828            }
1829
1830            // Resolve sliding.
1831            if (crit < 0)
1832                crit = 0.0;
1833            // The is the magnitude of the shear force.
1834            double sfmag = sforce.mag();
1835            if (sfmag > crit) {
1836                // Lower the shear force to the critical value for sliding.
1837                double rat = crit / sfmag;
1838                sforce *= rat;
1839                if (!sn_S_) {
1840                    slip_changed = true;
1841                    changed = true;
1842                }
1843                sn_S_ = true;
1844            } else {
1845                if (sn_S_) {
1846                    slip_changed = true;
1847                    changed = true;
1848                }
1849                sn_S_ = false;
1850            }
1851            if (sn_S_) {
1852                if (sn_dil_ > 0) {
1853                    double zdd = sn_dilzero_ != 0 ? sn_dilzero_ : limits<double>::max();
1854                    double usm = sn_sdisp_.y();
1855                    if (usm < zdd) {
1856                        double sInc = 0.0;
1857                        for (int i=1; i<dim; ++i)
1858                            sInc += trans.dof(i)*trans.dof(i);
1859                        sInc = std::sqrt(sInc);
1860                        sn_F_.rx() += kTran_ * sn_dil_ * sInc;
1861                    }
1862                }
1863            } else {
1864                if (sn_heal_) {
1865                    if (shearStrength(geom.x()))
1866                        sn_state_ = 6;
1867                }
1868            }
1869            // Resolve bending
1870            bool bslip_changed = false;
1871            crit = sn_bmul_ * 2.1*0.25*sn_F_.x() * br; // Jiang 2015
1872            DAVect test = mom;
1873#if DIM==3
1874            test.rx() = 0.0;
1875#endif
1876            double tmag = test.mag();
1877            if (tmag > crit) {
1878                // Lower the bending moment to the critical value for sliding.
1879                double rat = crit / tmag;
1880                test *= rat;
1881                if (!sn_BS_) {
1882                    bslip_changed = true;
1883                    changed = true;
1884                }
1885                sn_BS_ = true;
1886            }
1887            else {
1888                if (sn_BS_) {
1889                    bslip_changed = true;
1890                    changed = true;
1891                }
1892                sn_BS_ = false;
1893            }
1894            mom.rz() = test.z();
1895#if DIM==3
1896            mom.ry() = test.y();
1897            // Resolve twisting
1898            bool tslip_changed = false;
1899            crit = sn_tmul_ * 0.65*fric_*sn_F_.x() * br; // Jiang 2015
1900            tmag = std::abs(mom.x());
1901            if (tmag > crit) {
1902                // Lower the twisting moment to the critical value for sliding.
1903                double rat = crit / tmag;
1904                mom.rx() *= rat;
1905                if (!sn_TS_) {
1906                    tslip_changed = true;
1907                    changed = true;
1908                }
1909                sn_TS_ = true;
1910            }
1911            else {
1912                if (sn_TS_) {
1913                    tslip_changed = true;
1914                    changed = true;
1915                }
1916                sn_TS_ = false;
1917            }
1918#endif
1919            if (changed && cmEvents_[fSlipChange] >= 0) {
1920                int64 code = 0;
1921                if (slip_changed) {
1922                    code = 1;
1923                    if (bslip_changed) {
1924                        code = 4;
1925#if DIM==3
1926                        if (tslip_changed)
1927                            code = 7;
1928#endif
1929                    }
1930                }
1931                else if (bslip_changed) {
1932                    code = 2;
1933#if DIM==3
1934                    if (tslip_changed)
1935                        code = 6;
1936#endif
1937                }
1938#if DIM==3
1939                else if (tslip_changed) {
1940                    code = 3;
1941                    if (slip_changed)
1942                        code = 5;
1943                }
1944#endif
1945                auto c = state->getContact();
1946                std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
1947                                                     fish::Parameter(code),
1948                                                     fish::Parameter(sn_S_),
1949                                                     fish::Parameter(sn_BS_)
1950#ifdef THREED
1951                                                     ,fish::Parameter(sn_TS_)
1952#endif
1953                                                   };
1954                IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1955                fi->setCMFishCallArguments(c,arg,cmEvents_[fSlipChange]);
1956            }
1957        }
1958
1959        sn_F_.rx() += porForce;
1960        sn_F_old.rx() += porForce;
1961
1962        // Set the shear components of the total force.
1963        for (int i = 1; i<dim; ++i)
1964            sn_F_.rdof(i) = sforce.dof(i);
1965
1966        // Set the moment.
1967        sn_M_ = mom;
1968
1969        // Account for dashpot forces if the dashpot structure has been defined.
1970        if (dpProps_) {
1971            dpProps_->dp_F_.fill(0.0);
1972            double vcn(0.0), vcs(0.0);
1973            // Calculate the damping coefficients.
1974            vcn = dpProps_->dp_nratio_ * 2.0 * sqrt((state->inertialMass_*(kTran_)));
1975            vcs = dpProps_->dp_sratio_ * 2.0 * sqrt((state->inertialMass_*(kTran_/kRatio_)));
1976            // First damp the shear components
1977            for (int i = 1; i<dim; ++i)
1978                dpProps_->dp_F_.rdof(i) = trans.dof(i) * (-1.0* vcs) / timestep;
1979            // Damp the normal component
1980            dpProps_->dp_F_.rx() -= trans.x() * vcn / timestep;
1981            // Need to change behavior based on the dp_mode.
1982            if ((dpProps_->dp_mode_ == 1 || dpProps_->dp_mode_ == 3)) {
1983                // Limit in tension if not bonded.
1984                if (dpProps_->dp_F_.x() + sn_F_.x() < 0)
1985                    dpProps_->dp_F_.rx() = -sn_F_.rx();
1986            }
1987            if (sn_S_ && dpProps_->dp_mode_ > 1) {
1988                // Limit in shear if not sliding.
1989                double dfn = dpProps_->dp_F_.rx();
1990                dpProps_->dp_F_.fill(0.0);
1991                dpProps_->dp_F_.rx() = dfn;
1992            }
1993        }
1994
1995        //Compute energies if energy tracking has been enabled.
1996        if (state->trackEnergy_) {
1997            assert(energies_);
1998            energies_->estrain_ = 0.0;
1999            if (kTran_)
2000                // Calculate the strain energy.
2001                energies_->estrain_ = 0.5*sn_F_.x()*sn_F_.x() / kTran_;
2002            if (kTran_) {
2003                DVect s = sn_F_;
2004                s.rx() = 0.0;
2005                double smag2 = s.mag2();
2006                // Add the shear component of the strain energy.
2007                energies_->estrain_ += 0.5*smag2 / (kTran_/kRatio_);
2008
2009                if (sn_S_) {
2010                    // If sliding calculate the slip energy and accumulate it.
2011                    sn_F_old.rx() = 0.0;
2012                    DVect avg_F_s = (s + sn_F_old)*0.5;
2013                    DVect u_s_el = (s - sn_F_old) / (kTran_/kRatio_);
2014                    DVect u_s(0.0);
2015                    for (int i = 1; i < dim; ++i)
2016                        u_s.rdof(i) = trans.dof(i);
2017                    energies_->eslip_ -= std::min(0.0, (avg_F_s | (u_s + u_s_el)));
2018                }
2019            }
2020            // Add the bending/twisting resistance energy contributions.
2021            if (kb) {
2022                DAVect tmp = sn_M_;
2023#ifdef THREED
2024                tmp.rx() = 0.0;
2025#endif
2026                energies_->estrain_ += 0.5*tmp.mag2() / kb;
2027                if (sn_BS_) {
2028                    //  accumulate bending slip energy.
2029                    DAVect tmp_old = sn_M_old;
2030#ifdef THREED
2031                    tmp_old.rx() = 0.0;
2032#endif
2033                    DAVect avg_M = (tmp + tmp_old)*0.5;
2034                    DAVect t_s_el = (tmp - tmp_old) / kb;
2035                    energies_->eslip_ -= std::min(0.0, (avg_M | (ang + t_s_el)));
2036                }
2037            }
2038#ifdef THREED
2039            if (kt) {
2040                double mt = std::abs(sn_M_.x());
2041                energies_->estrain_ += 0.5*mt*mt / kt;
2042                if (sn_TS_) {
2043                    //  accumulate twisting slip energy.
2044                    DAVect tmp(0.0);
2045                    DAVect tmp_old(0.0);
2046                    tmp.rx() = sn_M_.x();
2047                    tmp_old.rx() = sn_M_old.x();
2048                    DAVect avg_M = (tmp + tmp_old)*0.5;
2049                    DAVect t_s_el = (tmp - tmp_old) / kt;
2050                    energies_->eslip_ -= std::min(0.0, (avg_M | (ang + t_s_el)));
2051                }
2052            }
2053#endif
2054
2055            if (dpProps_) {
2056                // Calculate damping energy (accumulated) if the dashpots are active.
2057                energies_->edashpot_ -= dpProps_->dp_F_ | trans;
2058            }
2059        }
2060
2061        // This is just a sanity check to ensure, in debug mode, that the force/moment aren't wonky.
2062        assert(sn_F_ == sn_F_);
2063        assert(fictForce_ == fictForce_);
2064        assert(sn_M_ == sn_M_);
2065        return true;
2066    }
2067
2068    void ContactModelRBSN::setForce(const DVect &v,IContact *c) {
2069        sn_F_ = v;
2070        fictForce_ = DVect(0.0);
2071        forceSet_ = true;
2072        if (v.x() > 0) 
2073            rgap_ = c->getGap() + v.x() / kTran_;
2074    }
2075
2076    void ContactModelRBSN::propagateStateInformation(IContactModelMechanical* old,const CAxes &oldSystem,const CAxes &newSystem) {
2077        // Only called for contacts with wall facets when the wall resolution scheme
2078        // is set to full!
2079        // Only do something if the contact model is of the same type
2080        if (equal(old->getContactModel()->getName(),"springnetwork") && !isBonded()) {
2081            ContactModelRBSN *oldCm = (ContactModelRBSN *)old;
2082#ifdef THREED
2083            // Need to rotate just the shear component from oldSystem to newSystem
2084
2085            // Step 1 - rotate oldSystem so that the normal is the same as the normal of newSystem
2086            DVect axis = oldSystem.e1() & newSystem.e1();
2087            double c, ang, s;
2088            DVect re2;
2089            if (!checktol(axis.abs().maxComp(),0.0,1.0,1000)) {
2090                axis = axis.unit();
2091                c = oldSystem.e1()|newSystem.e1();
2092                if (c > 0)
2093                    c = std::min(c,1.0);
2094                else
2095                    c = std::max(c,-1.0);
2096                ang = acos(c);
2097                s = sin(ang);
2098                double t = 1. - c;
2099                DMatrix<3,3> rm;
2100                rm.get(0,0) = t*axis.x()*axis.x() + c;
2101                rm.get(0,1) = t*axis.x()*axis.y() - axis.z()*s;
2102                rm.get(0,2) = t*axis.x()*axis.z() + axis.y()*s;
2103                rm.get(1,0) = t*axis.x()*axis.y() + axis.z()*s;
2104                rm.get(1,1) = t*axis.y()*axis.y() + c;
2105                rm.get(1,2) = t*axis.y()*axis.z() - axis.x()*s;
2106                rm.get(2,0) = t*axis.x()*axis.z() - axis.y()*s;
2107                rm.get(2,1) = t*axis.y()*axis.z() + axis.x()*s;
2108                rm.get(2,2) = t*axis.z()*axis.z() + c;
2109                re2 = rm*oldSystem.e2();
2110            }
2111            else
2112                re2 = oldSystem.e2();
2113            // Step 2 - get the angle between the oldSystem rotated shear and newSystem shear
2114            axis = re2 & newSystem.e2();
2115            DVect2 tpf;
2116            DVect2 tpm;
2117            DMatrix<2,2> m;
2118            if (!checktol(axis.abs().maxComp(),0.0,1.0,1000)) {
2119                axis = axis.unit();
2120                c = re2|newSystem.e2();
2121                if (c > 0)
2122                    c = std::min(c,1.0);
2123                else
2124                    c = std::max(c,-1.0);
2125                ang = acos(c);
2126                if (!checktol(axis.x(),newSystem.e1().x(),1.0,100))
2127                    ang *= -1;
2128                s = sin(ang);
2129                m.get(0,0) = c;
2130                m.get(1,0) = s;
2131                m.get(0,1) = -m.get(1,0);
2132                m.get(1,1) = m.get(0,0);
2133                tpf = m*DVect2(oldCm->sn_F_.y(),oldCm->sn_F_.z());
2134                tpm = m*DVect2(oldCm->sn_M_.y(),oldCm->sn_M_.z());
2135            } else {
2136                m.get(0,0) = 1.;
2137                m.get(0,1) = 0.;
2138                m.get(1,0) = 0.;
2139                m.get(1,1) = 1.;
2140                tpf = DVect2(oldCm->sn_F_.y(),oldCm->sn_F_.z());
2141                tpm = DVect2(oldCm->sn_M_.y(),oldCm->sn_M_.z());
2142            }
2143            DVect pforce = DVect(0,tpf.x(),tpf.y());
2144            //DVect pm     = DVect(0,tpm.x(),tpm.y());
2145#else
2146            oldSystem;
2147            newSystem;
2148            DVect pforce = DVect(0,oldCm->sn_F_.y());
2149            //DVect pm     = DVect(0,oldCm->sn_M_.y());
2150#endif
2151            for (int i=1; i<dim; ++i)
2152                sn_F_.rdof(i) += pforce.dof(i);
2153            if (sn_mode_ && oldCm->sn_mode_)
2154                sn_F_.rx() = oldCm->sn_F_.x();
2155            oldCm->sn_F_ = DVect(0.0);
2156            oldCm->sn_M_ = DAVect(0.0);
2157            if (dpProps_ && oldCm->dpProps_) {
2158#ifdef THREED
2159                tpf = m*DVect2(oldCm->dpProps_->dp_F_.y(),oldCm->dpProps_->dp_F_.z());
2160                pforce = DVect(oldCm->dpProps_->dp_F_.x(),tpf.x(),tpf.y());
2161#else
2162                pforce = oldCm->dpProps_->dp_F_;
2163#endif
2164                dpProps_->dp_F_ += pforce;
2165                oldCm->dpProps_->dp_F_ = DVect(0.0);
2166            }
2167            if(oldCm->getEnergyActivated()) {
2168                activateEnergy();
2169                energies_->estrain_ = oldCm->energies_->estrain_;
2170                energies_->edashpot_ = oldCm->energies_->edashpot_;
2171                energies_->eslip_ = oldCm->energies_->eslip_;
2172                oldCm->energies_->estrain_ = 0.0;
2173                oldCm->energies_->edashpot_ = 0.0;
2174                oldCm->energies_->eslip_ = 0.0;
2175            }
2176            rgap_ = oldCm->rgap_;
2177        }
2178        assert(sn_F_ == sn_F_);
2179    }
2180
2181    void ContactModelRBSN::setNonForcePropsFrom(IContactModel *old) {
2182        // Only called for contacts with wall facets when the wall resolution scheme
2183        // is set to full!
2184        // Only do something if the contact model is of the same type
2185        if (equal(old->getName(),"springnetwork") && !isBonded()) {
2186            ContactModelRBSN *oldCm = (ContactModelRBSN *)old;
2187
2188            fictForce_ = oldCm->fictForce_;
2189            sn_F_ = oldCm->sn_F_;
2190            sn_sdisp_ = oldCm->sn_sdisp_;
2191            sn_M_ = oldCm->sn_M_;
2192            kRot_ = oldCm->kRot_;
2193            kTran_ = oldCm->kTran_;
2194            kRatio_ = oldCm->kRatio_;
2195            E_ = oldCm->E_;
2196            poisson_ = oldCm->poisson_;
2197            fric_ = oldCm->fric_;
2198            sn_bmul_ = oldCm->sn_bmul_;
2199            sn_tmul_ = oldCm->sn_tmul_;
2200            sn_rmul_ = oldCm->sn_rmul_;
2201            userArea_ = oldCm->userArea_;
2202            rgap_ = oldCm->rgap_;
2203            sn_fa_ = oldCm->sn_fa_;
2204            sn_mcf_ = oldCm->sn_mcf_;
2205            sn_dil_ = oldCm->sn_dil_;
2206            sn_dilzero_ = oldCm->sn_dilzero_;
2207            transTen_ = oldCm->transTen_;
2208            sn_elong_ = oldCm->sn_elong_;
2209            sn_ndisp_ = oldCm->sn_ndisp_;
2210            sn_mode_ = oldCm->sn_mode_;
2211            sn_state_ = oldCm->sn_state_;
2212            poisOffDiag_ = oldCm->poisOffDiag_;
2213            sn_S_ = oldCm->sn_S_;
2214            sn_BS_ = oldCm->sn_BS_;
2215            sn_TS_ = oldCm->sn_TS_;
2216            forceSet_ = oldCm->forceSet_;
2217            sn_heal_ = oldCm->sn_heal_;
2218            tenTable_ = oldCm->tenTable_;
2219            cohTable_ = oldCm->cohTable_;
2220            if (oldCm->hasDamping()) {
2221                if (!dpProps_)
2222                    dpProps_ = NEW dpProps();
2223                dp_nratio(oldCm->dp_nratio());
2224                dp_sratio(oldCm->dp_sratio());
2225                dp_mode(oldCm->dp_mode());
2226                dp_F(oldCm->dp_F());
2227            }
2228        }
2229    }
2230
2231    DVect ContactModelRBSN::getForce() const {
2232        DVect ret(sn_F_);
2233        ret += fictForce_;
2234        if (dpProps_)
2235            ret += dpProps_->dp_F_;
2236        return ret;
2237    }
2238
2239    DAVect ContactModelRBSN::getMomentOn1(const IContactMechanical *c) const {
2240        DAVect ret(sn_M_);
2241        if (c) {
2242            DVect force = getForce();
2243            c->updateResultingTorqueOn1Local(force,&ret);
2244        }
2245        return ret;
2246    }
2247
2248    DAVect ContactModelRBSN::getMomentOn2(const IContactMechanical *c) const {
2249        DAVect ret(sn_M_);
2250        if (c) {
2251            DVect force = getForce();
2252            c->updateResultingTorqueOn2Local(force,&ret);
2253        }
2254        return ret;
2255    }
2256    
2257    DAVect ContactModelRBSN::getModelMomentOn1() const {
2258        DAVect ret(sn_M_);
2259        return ret;
2260    }
2261    
2262    DAVect ContactModelRBSN::getModelMomentOn2() const {
2263        DAVect ret(sn_M_);
2264        return ret;
2265    }
2266    
2267    void ContactModelRBSN::objectPropsTypes(std::vector<std::pair<string,InfoTypes>> *ret) const {
2268        ret->clear();
2269        ret->push_back({"kn",ScalarInfo});
2270        ret->push_back({"ks",ScalarInfo});
2271        ret->push_back({"krot",VectorInfo});
2272        ret->push_back({"fric",ScalarInfo});
2273        ret->push_back({"sn_bmul",ScalarInfo});
2274        ret->push_back({"sn_tmul",ScalarInfo});
2275        ret->push_back({"sn_mode",ScalarInfo});
2276        ret->push_back({"sn_force",VectorInfo});
2277        ret->push_back({"sn_moment",VectorInfo});
2278        ret->push_back({"sn_slip",ScalarInfo});
2279        ret->push_back({"sn_slipb",ScalarInfo});
2280        ret->push_back({"sn_slipt",ScalarInfo});
2281        ret->push_back({"sn_rmul",ScalarInfo});
2282        ret->push_back({"sn_radius",ScalarInfo});
2283        ret->push_back({"emod",ScalarInfo});
2284        ret->push_back({"kratio",ScalarInfo});
2285        ret->push_back({"dp_nratio",ScalarInfo});
2286        ret->push_back({"dp_sratio",ScalarInfo});
2287        ret->push_back({"dp_mode",ScalarInfo});
2288        ret->push_back({"dp_force",VectorInfo});
2289        ret->push_back({"sn_state",ScalarInfo});
2290        ret->push_back({"sn_ten",ScalarInfo});
2291        ret->push_back({"sn_shear",ScalarInfo});
2292        ret->push_back({"sn_coh",ScalarInfo});
2293        ret->push_back({"sn_fa",ScalarInfo});
2294        ret->push_back({"sn_mcf",ScalarInfo});
2295        ret->push_back({"sn_sigma",ScalarInfo});
2296        ret->push_back({"sn_tau",ScalarInfo});
2297        ret->push_back({"sn_area",ScalarInfo});
2298        ret->push_back({"user_area",ScalarInfo});
2299        ret->push_back({"rgap",ScalarInfo});
2300        ret->push_back({"sn_pois_force",VectorInfo});
2301        ret->push_back({"sn_pois",ScalarInfo});
2302        ret->push_back({"sn_non_diag",ScalarInfo});
2303        ret->push_back({"sn_cohres",ScalarInfo});
2304        ret->push_back({"sn_dil",ScalarInfo});
2305        ret->push_back({"sn_dilzero",ScalarInfo});
2306        ret->push_back({"sn_ndisp",ScalarInfo});
2307        ret->push_back({"sn_sdisp",VectorInfo});
2308        ret->push_back({"sn_cohdc",ScalarInfo});
2309        ret->push_back({"sn_heal",ScalarInfo});
2310        ret->push_back({"sn_tendc",ScalarInfo});
2311        ret->push_back({"sn_tabpos",ScalarInfo});
2312        ret->push_back({"sn_porp",ScalarInfo});
2313        ret->push_back({"sn_esigma",ScalarInfo});
2314        
2315    }
2316    
2317    void ContactModelRBSN::objectPropValues(std::vector<double> *ret,const IContact *con) const {
2318        FP_S;
2319        const IContactMechanical *c(convert_getcast<IContactMechanical>(con));
2320        ret->clear();
2321        ret->push_back(kTran_);
2322        ret->push_back(kTran_/kRatio_);
2323        ret->push_back(kRot_.mag());
2324        ret->push_back(fric_);
2325        ret->push_back(sn_bmul_);
2326        ret->push_back(sn_tmul_);
2327        ret->push_back(sn_mode_);
2328        ret->push_back(sn_F_.mag());
2329        ret->push_back(sn_M_.mag());
2330        ret->push_back(sn_S_);
2331        ret->push_back(sn_BS_);
2332        ret->push_back(sn_TS_);
2333        ret->push_back(sn_rmul_);
2334        double Cmax1 = c->getEnd1Curvature().y();
2335        double Cmax2 = c->getEnd2Curvature().y();
2336        double d;
2337        if (!userArea_)
2338            d= sn_rmul_ * 1.0 / std::max(Cmax1, Cmax2);
2339        else {
2340#ifdef THREED
2341            d= std::sqrt(userArea_ / dPi);
2342#else
2343            d= userArea_ / 2.0;
2344#endif
2345        }
2346        ret->push_back(d);
2347        ret->push_back(E_);
2348        ret->push_back(kRatio_);
2349        ret->push_back(dpProps_ ? dpProps_->dp_nratio_ : 0);
2350        ret->push_back(dpProps_ ? dpProps_->dp_sratio_ : 0);
2351        ret->push_back(dpProps_ ? dpProps_->dp_mode_ : 0);
2352        ret->push_back(dpProps_ ? dpProps_->dp_F_.mag() : 0);
2353        ret->push_back(sn_state_);
2354        ret->push_back(sn_Ten());
2355        ret->push_back(shearStrength(computeGeomData(c->getEnd1Curvature(),c->getEnd2Curvature()).x()));
2356        ret->push_back(cohTable_[0].x());
2357        ret->push_back(std::atan(sn_fa_)/dDegrad);
2358        ret->push_back(sn_mcf_);
2359        ret->push_back(SMax(c->getEnd1Curvature(),c->getEnd2Curvature()).x());
2360        ret->push_back(SMax(c->getEnd1Curvature(),c->getEnd2Curvature()).y());
2361        ret->push_back(userArea_ ? userArea_ : computeGeomData(c->getEnd1Curvature(),c->getEnd2Curvature()).x());
2362        ret->push_back(userArea_);
2363        ret->push_back(rgap_);
2364        ret->push_back(fictForce_.mag());
2365        ret->push_back(poisson_);
2366        ret->push_back(poisOffDiag_ == false ? 0 : 1);
2367        ret->push_back(sn_cohres_);
2368        ret->push_back(std::atan(sn_dil_)/dDegrad);
2369        ret->push_back(sn_dilzero_);
2370        ret->push_back(sn_ndisp_);
2371        ret->push_back(sn_sdisp_.mag());
2372        ret->push_back(sn_cohdc());
2373        ret->push_back(sn_heal_);
2374        ret->push_back(sn_tendc());
2375        ret->push_back(sn_tabPos_+1);
2376        ret->push_back(sn_por_);
2377        ret->push_back(SMax(c->getEnd1Curvature(),c->getEnd2Curvature()).x() + sn_por_);
2378        FP_S;
2379    }
2380
2381    DVect3 ContactModelRBSN::computeGeomData(const DVect2 &end1Curvature,const DVect2 &end2Curvature) const {
2382        double Cmax1 = end1Curvature.y();
2383        double Cmax2 = end2Curvature.y();
2384        double br = sn_rmul_ * 1.0 / std::max(Cmax1, Cmax2);
2385        if (userArea_)
2386#ifdef THREED
2387            br = std::sqrt(userArea_ / dPi);
2388#else
2389            br = userArea_ / 2.0;
2390#endif
2391        double br2 = br * br;
2392#ifdef THREED
2393        double area = dPi * br2;
2394        double bi = 0.25*area*br2;
2395#else
2396        double area = 2.0*br;
2397        double bi = 2.0*br*br2 / 3.0;
2398#endif
2399        return DVect3(area, bi, br);
2400    }
2401
2402    DVect2 ContactModelRBSN::SMax(const DVect2 &e1c,const DVect2 &e2c) const {
2403        DVect3 data = computeGeomData(e1c,e2c);
2404        double area = data.x();
2405        double bi = data.y();
2406        double br = data.z();
2407        /* maximum stresses */
2408        //double nsmax0 = -(totalForce.x() / area) + sn_mcf_* sqrt(sn_M_.y()*sn_M_.y() + sn_M_.z()*sn_M_.z()) * br / bi;
2409        double dbend = sqrt(sn_M_.y()*sn_M_.y() + sn_M_.z()*sn_M_.z());
2410        dbend *= sn_mcf_;
2411        double dtwist = sn_M_.x();
2412        DVect bfs(sn_F_);
2413        bfs.rx() = 0.0;
2414        double dbfs = bfs.mag();
2415        double nsmax = -((sn_F_.x()+fictForce_.x()) / area) + dbend * br / bi;
2416        double ssmax = dbfs / area + std::abs(dtwist) * 0.5* br / bi;
2417        return DVect2(nsmax, ssmax);
2418    }
2419
2420    double ContactModelRBSN::shearStrength(const double &area) const {
2421        double sig = -1.0*(sn_F_.x() + fictForce_.x()) / area;
2422        double nstr = (sn_state_ > 2 && sn_state_ != 6) ? tenTable_[0].x() : 0.0;
2423        return sig <= nstr ? cohTable_[0].x() - sn_fa_*sig
2424            : cohTable_[0].x() - sn_fa_*nstr;
2425    }
2426
2427
2428    double ContactModelRBSN::strainEnergy(double kn,double ks,double kb,double kt) const {
2429        double ret(0.0);
2430        if (kn)
2431            ret = 0.5 * (sn_F_.x()+fictForce_.x()) * (sn_F_.x()+fictForce_.x()) / kn;
2432        if (ks) {
2433            DVect tmp = sn_F_ + fictForce_;
2434            tmp.rx() = 0.0;
2435            double smag2 = tmp.mag2();
2436            ret += 0.5 * smag2 / ks;
2437        }
2438
2439        if (kt)
2440            ret += 0.5 * sn_M_.x() * sn_M_.x() / kt;
2441        if (kb) {
2442            DAVect tmp = sn_M_;
2443#ifdef THREED
2444            tmp.rx() = 0.0;
2445            double smag2 = tmp.mag2();
2446#else
2447            double smag2 = tmp.z() * tmp.z();
2448#endif
2449            ret += 0.5 * smag2 / kb;
2450        }
2451        return ret;
2452    }
2453
2454} // namespace cmodelsxd
2455// EoF

Top