Flat-Joint Model Implementation

See this page for the documentation of this contact model.

contactmodelflatjoint.h

  1#pragma once
  2// contactmodelflatjoint.h
  3
  4#include "contactmodel/src/contactmodelmechanical.h"
  5
  6#ifdef FLATJOINT_LIB
  7#  define FLATJOINT_EXPORT EXPORT_TAG
  8#elif defined(NO_MODEL_IMPORT)
  9#  define FLATJOINT_EXPORT
 10#else
 11#  define FLATJOINT_EXPORT IMPORT_TAG
 12#endif
 13
 14namespace cmodelsxd {
 15    using namespace itasca;
 16
 17    class ContactModelFlatJoint : public ContactModelMechanical {
 18    public:
 19        enum PropertyKeys { 
 20              kwFjNr=1
 21            , kwFjElem
 22            , kwFjKn
 23            , kwFjKs                            
 24            , kwFjFric   
 25            , kwFjEmod
 26            , kwFjKRatio                            
 27            , kwFjRmul
 28            , kwFjRadius
 29            , kwFjGap0
 30            , kwFjTen 
 31            , kwFjCoh
 32            , kwFjFa 
 33            , kwFjF
 34            , kwFjM
 35            , kwFjState
 36            , kwFjSlip
 37            , kwFjMType
 38            , kwFjA
 39            , kwFjEgap
 40            , kwFjGap
 41            , kwFjNstr
 42            , kwFjSstr
 43            , kwFjSs
 44#ifdef THREED
 45            , kwFjNa
 46#endif
 47            , kwFjRelBr
 48            , kwFjCen
 49            , kwFjTrack
 50            , kwUserArea
 51            , kwFjCohRes
 52            , kwFjResMode
 53        };
 54         
 55        FLATJOINT_EXPORT ContactModelFlatJoint();
 56        FLATJOINT_EXPORT ContactModelFlatJoint(const ContactModelFlatJoint &) noexcept;
 57        FLATJOINT_EXPORT const ContactModelFlatJoint & operator=(const ContactModelFlatJoint &);
 58        FLATJOINT_EXPORT void   addToStorage(poly::vector<ContactModelMechanical> *s,int ww = -1) override;
 59        FLATJOINT_EXPORT virtual ~ContactModelFlatJoint();
 60        void     copy(const ContactModel *c) override;
 61        void     archive(ArchiveStream &) override;
 62        QString  getName() const override { return "flatjoint"; }
 63        void     setIndex(int i) override { index_=i;}
 64        int      getIndex() const override {return index_;}
 65        QString  getProperties() const override { return "fj_nr"
 66                                                        ",fj_elem"
 67                                                        ",fj_kn"
 68                                                        ",fj_ks"
 69                                                        ",fj_fric"
 70                                                        ",fj_emod"
 71                                                        ",fj_kratio"
 72                                                        ",fj_rmul"
 73                                                        ",fj_radius"
 74                                                        ",fj_gap0"
 75                                                        ",fj_ten"
 76                                                        ",fj_coh"
 77                                                        ",fj_fa"
 78                                                        ",fj_force"
 79                                                        ",fj_moment"
 80                                                        ",fj_state"
 81                                                        ",fj_slip"
 82                                                        ",fj_mtype"
 83                                                        ",fj_area"
 84                                                        ",fj_egap"
 85                                                        ",fj_gap"
 86                                                        ",fj_sigma"
 87                                                        ",fj_tau"
 88                                                        ",fj_shear"
 89#ifdef THREED
 90                                                        ",fj_nal"
 91#endif
 92                                                        ",fj_relbr"
 93                                                        ",fj_cen"
 94                                                        ",fj_track"
 95                                                        ",user_area"
 96                                                        ",fj_cohres"
 97                                                        ",fj_resmode"
 98                                                        ;}
 99
100        enum EnergyKeys { kwEStrain=1,kwESlip};
101        QString  getEnergies() const { return "energy-strain,energy-slip";}
102        double   getEnergy(uint32 i) const override;  // Base 1
103        bool     getEnergyAccumulate(uint32 i) const override; // Base 1
104        void     setEnergy(uint32 i,const double &d) override; // Base 1
105        void     activateEnergy() override { if (energies_) return; energies_ = NEW Energies();}
106        bool     getEnergyActivated() const override {return (energies_ !=0);}
107
108        enum FishCallEvents {fActivated=0,fBondBreak,fBroken,fSlipChange};
109        QString  getFishCallEvents() const override { return "contact_activated,bond_break,broken,all_slip_change"; }
110        QVariant getProperty(uint32 i,const IContact *) const override;
111        bool     getPropertyGlobal(uint32 i) const override;
112        bool     setProperty(uint32 i,const QVariant &v,IContact *) override;
113        bool     getPropertyReadOnly(uint32 i) const override;
114
115        bool     supportsInheritance(uint32 ) const override { return false; }
116
117        enum MethodKeys { kwBond=1, kwUnbond, KwDeformability, KwUpdateGeom, kwArea, kwInitialize};
118
119        QString  getMethods() const override { return "bond"
120                                                     ",unbond"
121                                                     ",deformability"
122                                                     ",update_geometry"
123                                                     ",area"
124                                                     ",initialize"
125                                            ;}
126        
127        QString  getMethodArguments(uint32 i) const override;
128        
129        bool     setMethod(uint32 i,const QVector<QVariant> &vl,IContact *con=0) override; // Base 1 - returns true if timestep contributions need to be updated
130
131        uint32   getMinorVersion() const override;
132
133        bool    validate(ContactModelMechanicalState *state,const double &timestep) override;
134        bool    endPropertyUpdated(const QString &,const IContactMechanical *) override { return false; }
135        bool    forceDisplacementLaw(ContactModelMechanicalState *state,const double &timestep) override;
136        bool    thermalCoupling(ContactModelMechanicalState*, ContactModelThermalState*, IContactThermal*, const double&) override;
137        DVect2  getEffectiveTranslationalStiffness() const override { return effectiveTranslationalStiffness(); }
138        DAVect  getEffectiveRotationalStiffness() const override { return effectiveRotationalStiffness(); }
139
140        ContactModelFlatJoint *clone() const override { return NEW ContactModelFlatJoint(); }
141        double  getActivityDistance() const override {return 0.0;}
142        bool    isOKToDelete() const override { return !isBonded(); }
143        void    resetForcesAndMoments() override { fj_f(DVect(0.0)); fj_m(DAVect(0.0)); for (int i=0; i<f_.size(); ++i) f_[i] = DVect(0.0); }
144        void    setForce(const DVect &v,IContact *) override;
145        void    setArea(const double &d) override { userArea_ = d; }
146        double  getArea() const override { return userArea_; }
147        bool    checkActivity(const double &inGap) override;
148
149        /// Return the total force that the contact model holds.
150        DVect   getForce() const override;
151        /// Return the total moment on 1 that the contact model holds
152        DAVect  getMomentOn1(const IContactMechanical*) const override;
153        /// Return the total moment on 1 that the contact model holds
154        DAVect  getMomentOn2(const IContactMechanical*) const override;
155        
156        DAVect  getModelMomentOn1() const override;
157        DAVect  getModelMomentOn2() const override;
158        
159        // Used to efficiently get properties from the contact model for the object pane.
160        // List of properties for the object pane, comma separated.
161        // All properties will be cast to doubles for comparison. No other comparisons
162        // are supported. This may not be the same as the entire property list.
163        // Return property name and type for plotting.
164        void objectPropsTypes(std::vector<std::pair<QString,InfoTypes>> *) const override;
165        // All properties cast to doubles - this is what can be compared. 
166        void objectPropValues(std::vector<double> *,const IContact *) const override;
167
168        //virtual bool     isSliding() const { return fj_s_; }
169        bool    isBonded() const override { FOR(it,bmode_) if ((*it) == 3) return true; return false; }
170        void    unbond() override { FOR(it,bmode_) *it = 0; }
171
172        // For contact specific plotting
173        void getSphereList(const IContact* con, std::vector<DVect>* pos, std::vector<double>* rad, std::vector<double>* val) override;
174#ifdef THREED
175        void getDiskList(const IContact* con, std::vector<DVect>* pos, std::vector<DVect>* normal, std::vector<double>* radius, std::vector<double>* val) override;
176#endif
177        void getCylinderList(const IContact* con, std::vector<DVect>* bot, std::vector<DVect>* top, std::vector<double>* radlow, std::vector<double>* radhi, std::vector<double>* val) override;
178
179        int             fj_nr() const               {return fj_nr_;}
180        void            fj_nr(int d)                {       fj_nr_= d;}
181#ifdef THREED
182        int             fj_n() const                { return fj_na_ * fj_nr_; }
183        int             fj_na() const               {return fj_na_;}
184        void            fj_na(int d)                {       fj_na_= d;}
185#else
186        int             fj_n() const                { return fj_nr_; }
187#endif
188        int             fj_elem() const             {return fj_elem_;}
189        void            fj_elem(int d)              {       fj_elem_= d;}
190        const double &  fj_kn() const               {return fj_kn_;}
191        void            fj_kn(const double &d)      {       fj_kn_ = d;}
192        const double &  fj_ks() const               {return fj_ks_;}
193        void            fj_ks(const double &d)      {       fj_ks_ = d;}
194        const double &  fj_fric() const             {return fj_fric_;}
195        void            fj_fric(const double &d)    {       fj_fric_ = d;}
196        const double &  fj_rmul() const             {return fj_rmul_;}
197        void            fj_rmul(const double &d)    {       fj_rmul_ = d;}
198        const double &  fj_gap0() const             {return fj_gap0_;}
199        void            fj_gap0(const double &d)    {       fj_gap0_ = d;}
200        const double &  fj_ten() const              {return fj_ten_;}
201        void            fj_ten(const double &d)     {       fj_ten_ = d;}
202        const double &  fj_coh() const              {return fj_coh_;}
203        void            fj_coh(const double &d)     {       fj_coh_ = d;}
204        const double &  fj_cohres() const           {return fj_cohres_;}
205        void            fj_cohres(const double &d)  {       fj_cohres_ = d;}
206        const double &  fj_fa() const               {return fj_fa_;}
207        void            fj_fa(const double &d)      {       fj_fa_ = d;}
208        const DVect &   fj_f() const                {return fj_f_;}
209        void            fj_f(const DVect &f)        {       fj_f_=f;}
210        const DAVect &  fj_m() const                {return fj_m_;}
211        void            fj_m(const DAVect &f)       {       fj_m_=f;}
212        const DAVect &  fj_m_set() const            {return fj_m_set_;}
213        void            fj_m_set(const DAVect &f)   {       fj_m_set_=f;}
214        const double &  rmin() const                {return rmin_;}
215        void            rmin(const double &d)       {       rmin_ = d;}
216        const double &  rbar() const                {return rbar_;}
217        void            rbar(const double &d)       {       rbar_ = d;}
218        const int &     fj_resmode() const          {return fj_resmode_;}
219        void            fj_resmode(const int &i)    {       fj_resmode_ = i;}
220        const double &  atot() const                {return atot_;}
221        void            atot(const double &d)       {       atot_ = d;}
222        bool            propsFixed() const          {return propsFixed_; }
223        void            propsFixed(bool d)          {       propsFixed_ = d;}
224        int             mType() const               {return mType_; }
225        void            mType(int d)                {       mType_ = d;}
226        const DVect &   gap() const                 {return gap_; }
227        void            gap(const DVect &d)         {       gap_ = d;}
228        const double &  theta() const               {return theta_; }
229        void            theta(const double & d)     {       theta_ = d;}
230#ifdef THREED
231        const double &  thetaM() const              {return thetaM_; }
232        void            thetaM(const double & d)    {       thetaM_ = d;}
233#else
234        double thetaM() const                       {return 0.0;}
235#endif
236
237        bool    hasEnergies() const {return energies_ ? true:false;}
238        double  estrain() const {return hasEnergies() ? energies_->estrain_: 0.0;}
239        void    estrain(const double &d) { if(!hasEnergies()) return; energies_->estrain_=d;}
240        double  eslip() const {return hasEnergies() ? energies_->eslip_: 0.0;}
241        void    eslip(const double &d) { if(!hasEnergies()) return; energies_->eslip_=d;}
242
243        uint32 inheritanceField() const {return inheritanceField_;}
244        void inheritanceField(uint32 i) {inheritanceField_ = i;}
245
246        const DVect2 & effectiveTranslationalStiffness()  const             {return effectiveTranslationalStiffness_;}
247        void           effectiveTranslationalStiffness(const DVect2 &v )    {effectiveTranslationalStiffness_=v;}
248        const DAVect & effectiveRotationalStiffness()  const                {return effectiveRotationalStiffness_;}
249        void           effectiveRotationalStiffness(const DAVect &v )       {effectiveRotationalStiffness_=v;}
250
251    private:
252        static int index_;
253
254        struct Energies {
255            Energies() : estrain_(0.0), eslip_(0.0) {}
256            double estrain_;  // elastic energy stored in contact 
257            double eslip_;    // work dissipated by friction 
258        };
259
260        void   updateEffectiveStiffness(ContactModelMechanicalState *state);
261
262        // inheritance fields
263        uint32 inheritanceField_;
264
265        int                     fj_nr_ = 2;             // radial number of elements >= 1 (total in 2D)
266#ifdef THREED
267        int                     fj_na_ = 4;             // circumferential number of elements >= 3
268#endif
269        int                     fj_elem_ = 1;           // Element to be queried
270        double                  fj_kn_ = 0.0;             // normal stiffness
271        double                  fj_ks_ = 0.0;             // shear stiffness
272        double                  fj_fric_ = 0.0;           // Coulomb friction coefficient
273        double                  fj_rmul_ = 1.0;           // Radius multiplier
274        double                  fj_gap0_ = 0.0;           // Initial gap
275        double                  fj_ten_ = 0.0;            // Tensile strength 
276        double                  fj_coh_ = 0.0;            // Cohesive strength
277        double                  fj_cohres_ = 0.0;         // Residual cohesive strength
278        double                  fj_fa_ = 0.0;             // Friction angle
279        DVect                   fj_f_ = DVect(0.0);              // Force carried in the model
280        DAVect                  fj_m_ = DAVect(0.0);              // Moment carried in the model
281        DAVect                  fj_m_set_ = DAVect(0.0);          // When initializing forces then need an extra moment term
282        // Area related quantities
283        double                  rmin_ = 1.0;              // min(Ra,Rb) where Ra & Rb are particle radii
284        double                  rbar_ = 0.0;              // flat-joint radius [m]
285        double                  atot_ = 0.0;              // flat-joint area [m^2]
286        std::vector<double>     a_ = std::vector<double>(2);                 // cross-sectional area of elem[fj_elem-1] [m^2]
287#ifdef THREED
288        std::vector<DVect2>     rBarl_= std::vector<DVect2>(2);             // centroid relative position of elem[fj_elem-1] [m] (3D)
289#else
290        std::vector<double>     rBarl_ = std::vector<double>(2);             // centroid relative position of elem[fj_elem-1] [m] (2D)
291#endif
292        int                     fj_resmode_ = 0;         // Residual mode
293        void setAreaQuantities();                   // Set Rbar, Atot and A[]
294        DVect getRelElemPos(const IContact*,int ) const;   // Return the relative location of element i
295        void setRelElemPos(const IContact*,int ,const DVect &);   // Set the relative location of element i
296
297        bool                    propsFixed_ = false;        // {Rmul, N, G, bstate, mType} fixed, cannot reset
298        int                     mType_ = 3;             // initial microstructural type
299        int getmType() const;                       // {1,2,3,4}={bonded, gapped, slit, other}
300        
301        std::vector<int>        bmode_ = std::vector<int>(2);             // bond mode - 0 unbonded, 1 failed in tension, 2 failed in shear, 3 bonded
302        std::vector<bool>       smode_ = std::vector<bool>(2);             // slip mode
303        bool Bonded(int e) const { return bmode_[e-1] == 3 ? true : false; }
304
305        // Set bstate and bmode (can only bond if fj_gap0_==0.0)
306        void bondElem(int iSeg,bool bBond);
307        // Set bstate & bmode 
308        void breakBond(int iSeg,int fmode,ContactModelMechanicalState *state);
309        void slipChange(int iSeg,bool smode,ContactModelMechanicalState *state);
310
311        // For use in 2D only!
312        double tauC(const double &dSig,bool bBonded) const; // shear strength (positive) [N/m^2]
313
314        // INTERFACE RESPONSE QUANTITIES:
315        DVect                   gap_ = DVect(0.0);               // total relative displacement [m]
316        double                  theta_ = 0.0;             // total relative rotation [rad]
317#ifdef THREED
318        double                  thetaM_ = 0.0;            // total relative rotation [rad]
319        double thbMag() const   { return sqrt(theta_*theta_ + thetaM_*thetaM_); }
320        // unit-vector xi of middle surface system xi-eta
321        // (If both thb_l and thb_m are zero, then xi is undefined
322        // and returns zero for both components.)
323        double xi(int comp /* component (l,m) = (1,2) */) const;
324#endif
325        std::vector<double>     egap_ = std::vector<double>(2);          // gap at centroid of elem[fj_elem-1] [N]
326        std::vector<DVect>      f_ = std::vector<DVect>(2);             // force on elem[fj_elem-1] [N]
327
328        void   initVectors();                   // Resize and zero all vector types based on current value of N
329#ifdef TWOD
330        double gap(const double &x) const;      // Gap (g>0 is open) along the interface, x in [0, 2*Rbar]
331#else
332        double gap(const double &rl,const double &rm) const; // Gap (g>0 is open) gap at relative position (l,m) [m]
333        double sigBar( int e /* element, e = 1,2,...,Nel */ ) const; // normal stress at centroid of elem[eN-1] [N/m^2]
334        double tauBar( int e /* element, e = 1,2,...,Nel */ ) const; // shear  stress at centroid of elem[eN-1] [N/m^2]
335#endif
336        double computeStrainEnergy(int e /* element, e = 1,2,...,Nel */) const; // strain energy in elem[eN-1]
337        // For use in 2D only! Segment normal stress
338        double computeSig(const double &g0,   /* gap at left end  */
339                          const double &g1,   /* gap at right end */
340                          const double &rbar, /* length is 2*rbar */
341                          const double &dA,   /* area             */
342                          bool bBonded        /* bond state       */
343                          ) const;
344        // For use in 2D only! Segment moment
345        double computeM(const double &g0,   /* gap at left end  */ 
346                        const double &g1,   /* gap at right end */ 
347                        const double &rbar, /* length is 2*rbar */
348                        bool bBonded        /* bond state       */
349                        ) const;
350        // For use in 2D only! getCase used by ComputeSig and ComputeM
351        int getCase(const double &g0, /* gap at left end  */ 
352                 const double &g1  /* gap at right end */ 
353                 ) const;
354        // Segment elastic shear-displacement increment, which is portion of
355        // increment that occurs while gap is negative.
356        double delUse(const double &gapStart, /* gap at start of FDlaw  */
357                      const double &gapEnd,   /* gap at end of FDlaw    */
358                      bool bBonded,           /* bond state             */
359                      const double &delUs     /* shear displ. increment */
360                     ) const;
361        double      userArea_ = 0.0;   // Area as specified by the user 
362        Energies *   energies_ = nullptr;    // energies
363
364        DVect2  effectiveTranslationalStiffness_ = DVect2(0.0);
365        DAVect  effectiveRotationalStiffness_ = DAVect(0.0);
366
367        struct orientProps {
368            orientProps() : origNormal_(DVect(0.0)) {}
369            Quat    orient1_;
370            Quat    orient2_;
371            DVect   origNormal_;
372        };
373
374        orientProps *orientProps_ = nullptr;
375    };
376} // namespace itascaxd
377
378
379// EoF

Top

contactmodelflatjoint.cpp

   1// contactmodelflatjoint.cpp
   2#include "contactmodelflatjoint.h"
   3
   4#include "../version.txt"
   5#include "fish/src/parameter.h"
   6#include "utility/src/tptr.h"
   7#include "shared/src/mathutil.h"
   8#include "baseqt/src/basetoqt.h"
   9#include "contactmodel/src/contactmodelthermal.h"
  10#include "contactmodel/src/contactmodelfluid.h"
  11
  12#include "kernel/interface/iprogram.h"
  13#include "module/interface/icontact.h"
  14#include "module/interface/icontactmechanical.h"
  15#include "module/interface/icontactthermal.h"
  16#include "module/interface/icontactfluid.h"
  17#include "module/interface/ifishcalllist.h"
  18#include "module/interface/ipiece.h"
  19#include "module/interface/ipiecemechanical.h"
  20
  21#ifdef FLATJOINT_LIB
  22#ifdef _WIN32
  23  int __stdcall DllMain(void *,unsigned, void *)
  24  {
  25    return 1;
  26  }
  27#endif
  28
  29  extern "C" EXPORT_TAG const char *getName()
  30  {
  31#if DIM==3
  32    return "contactmodelmechanical3dflatjoint";
  33#else
  34    return "contactmodelmechanical2dflatjoint";
  35#endif
  36  }
  37
  38  extern "C" EXPORT_TAG unsigned getMajorVersion()
  39  {
  40    return MAJOR_VERSION;
  41  }
  42
  43  extern "C" EXPORT_TAG unsigned getMinorVersion()
  44  {
  45    return MINOR_VERSION;
  46  }
  47
  48  extern "C" EXPORT_TAG void *createInstance()
  49  {
  50    cmodelsxd::ContactModelFlatJoint *m = NEW cmodelsxd::ContactModelFlatJoint();
  51    return (void *)m;
  52  }
  53#endif // FLATJOINT_LIB
  54
  55namespace cmodelsxd {
  56    static const uint32 fjKnMask      = 0x00002; // Base 1!
  57    static const uint32 fjKsMask      = 0x00004;
  58    static const uint32 fjFricMask    = 0x00008;
  59
  60    using namespace itasca;
  61
  62    int ContactModelFlatJoint::index_ = -1;
  63    uint32 ContactModelFlatJoint::getMinorVersion() const { return MINOR_VERSION; ;}
  64
  65    ContactModelFlatJoint::ContactModelFlatJoint() : inheritanceField_(fjKnMask|fjKsMask|fjFricMask) {
  66        initVectors();
  67        setAreaQuantities();
  68        //setFromParent(ContactModelMechanicalList::instance()->find(getName()));
  69    }
  70    
  71    ContactModelFlatJoint::ContactModelFlatJoint(const ContactModelFlatJoint &m) noexcept {
  72        inheritanceField(fjKnMask|fjKsMask|fjFricMask);
  73        initVectors();
  74        setAreaQuantities();
  75        this->copy(&m);
  76    }
  77    
  78    const ContactModelFlatJoint & ContactModelFlatJoint::operator=(const ContactModelFlatJoint &m) {
  79        inheritanceField(fjKnMask|fjKsMask|fjFricMask);
  80        initVectors();
  81        setAreaQuantities();
  82        this->copy(&m);
  83        return *this;
  84    }
  85    
  86    void ContactModelFlatJoint::addToStorage(poly::vector<ContactModelMechanical> *s,int ww) { 
  87        s->addToStorage<ContactModelFlatJoint>(*this,ww);
  88    }
  89
  90    ContactModelFlatJoint::~ContactModelFlatJoint() {
  91        if (orientProps_)
  92            delete orientProps_;
  93        if (energies_)
  94            delete energies_;
  95    }
  96
  97    void ContactModelFlatJoint::archive(ArchiveStream &stream) {
  98        stream & fj_nr_;
  99#ifdef THREED
 100        stream & fj_na_;
 101#endif
 102        stream & fj_elem_;
 103        stream & fj_kn_;
 104        stream & fj_ks_;
 105        stream & fj_fric_;
 106        stream & fj_rmul_;
 107        stream & fj_gap0_;
 108        stream & fj_ten_;
 109        stream & fj_coh_;
 110        stream & fj_fa_;
 111        stream & fj_f_;
 112        stream & fj_m_;
 113        stream & rmin_;
 114        stream & rbar_;
 115        stream & atot_;
 116        stream & a_;
 117        stream & rBarl_;
 118        stream & propsFixed_;
 119        stream & mType_;
 120        stream & bmode_;
 121        stream & smode_;
 122        stream & gap_;
 123        stream & theta_;
 124#ifdef THREED
 125        stream & thetaM_;
 126#endif
 127        stream & egap_;
 128        stream & f_;
 129
 130        if (stream.getArchiveState()==ArchiveStream::Save) {
 131            bool b = false;
 132            if (orientProps_) {
 133                b = true;
 134                stream & b;
 135                stream & orientProps_->orient1_;
 136                stream & orientProps_->orient2_;
 137                stream & orientProps_->origNormal_;
 138            } else
 139                stream & b;
 140            b = false;
 141            if (energies_) {
 142                b = true;
 143                stream & b;
 144                stream & energies_->estrain_;
 145                stream & energies_->eslip_;
 146            } else
 147                stream & b;
 148        } else {
 149            bool b(false);
 150            stream & b;
 151            if (b) {
 152                if (!orientProps_)
 153                    orientProps_ = NEW orientProps();
 154                stream & orientProps_->orient1_;
 155                stream & orientProps_->orient2_;
 156                stream & orientProps_->origNormal_;
 157            }
 158            stream & b;
 159            if (b) {
 160                if (!energies_)
 161                    energies_ = NEW Energies();
 162                stream & energies_->estrain_;
 163                stream & energies_->eslip_;
 164            }
 165        }
 166
 167        stream & inheritanceField_;
 168        stream & effectiveTranslationalStiffness_;
 169        stream & effectiveRotationalStiffness_;
 170
 171        if (stream.getArchiveState()==ArchiveStream::Save || stream.getRestoreVersion() > 1)
 172            stream & userArea_;
 173
 174        if (stream.getArchiveState()==ArchiveStream::Save || stream.getRestoreVersion() > 2)
 175            stream & fj_m_set_;
 176
 177        if (stream.getArchiveState()==ArchiveStream::Save || stream.getRestoreVersion() > 3) {
 178            stream & fj_cohres_;
 179            stream & fj_resmode_;
 180        }
 181    }
 182
 183    void ContactModelFlatJoint::copy(const ContactModel *cm) {
 184        ContactModelMechanical::copy(cm);
 185        const ContactModelFlatJoint *in = dynamic_cast<const ContactModelFlatJoint*>(cm);
 186        if (!in) throw std::runtime_error("Internal error: contact model dynamic cast failed.");
 187        fj_nr(in->fj_nr());
 188#ifdef THREED
 189        fj_na(in->fj_na());
 190#endif
 191        fj_elem(in->fj_elem());
 192        fj_kn(in->fj_kn());
 193        fj_ks(in->fj_ks());
 194        fj_fric(in->fj_fric());
 195        fj_rmul(in->fj_rmul());
 196        fj_gap0(in->fj_gap0());
 197        fj_ten(in->fj_ten());
 198        fj_coh(in->fj_coh());
 199        fj_cohres(in->fj_cohres());
 200        fj_fa(in->fj_fa());
 201        fj_f(in->fj_f());
 202        fj_m(in->fj_m());
 203        fj_m_set(in->fj_m_set());
 204        rmin(in->rmin());
 205        rbar(in->rbar());
 206        fj_resmode(in->fj_resmode());
 207        atot(in->atot());
 208        a_ = in->a_;
 209        rBarl_ = in->rBarl_;
 210        propsFixed(in->propsFixed());
 211        mType(in->mType());
 212        bmode_ = in->bmode_;
 213        smode_ = in->smode_;
 214        gap(in->gap());
 215        theta(in->theta());
 216#ifdef THREED
 217        thetaM(in->thetaM());
 218#endif
 219        egap_ = in->egap_;
 220        f_ = in->f_;
 221        if (in->orientProps_) {
 222            if (!orientProps_)
 223                orientProps_ = NEW orientProps();
 224            orientProps_->orient1_ = in->orientProps_->orient1_;
 225            orientProps_->orient2_ = in->orientProps_->orient2_;
 226            orientProps_->origNormal_ =  in->orientProps_->origNormal_;
 227        }
 228        if (in->hasEnergies()) {
 229            if (!energies_)
 230                energies_ = NEW Energies();
 231            estrain(in->estrain());
 232            eslip(in->eslip());
 233        }
 234        userArea_ = in->userArea_;
 235        inheritanceField(in->inheritanceField());
 236        effectiveTranslationalStiffness(in->effectiveTranslationalStiffness());
 237        effectiveRotationalStiffness(in->effectiveRotationalStiffness());
 238    }
 239
 240
 241    QVariant ContactModelFlatJoint::getProperty(uint32 i,const IContact *con) const {
 242        // The IContact pointer may be a nullptr!
 243        const IContactMechanical *c(convert_getcast<IContactMechanical>(con));        
 244        QVariant var;
 245        switch (i) {
 246        case kwFjNr     :   return fj_nr();
 247        case kwFjElem   :   return fj_elem();
 248        case kwFjKn     :   return fj_kn();
 249        case kwFjKs     :   return fj_ks();
 250        case kwFjFric   :   return fj_fric();
 251        case kwFjEmod   :  {
 252                                if (c ==nullptr) return 0.0;
 253                                double rsum(0.0);
 254                                if (c->getEnd1Curvature().y())
 255                                    rsum += 1.0/c->getEnd1Curvature().y();
 256                                if (c->getEnd2Curvature().y())
 257                                    rsum += 1.0/c->getEnd2Curvature().y();
 258                                if (userArea_) {
 259#ifdef THREED
 260                                    rsum = std::sqrt(userArea_ / dPi);
 261#else
 262                                    rsum = userArea_ / 2.0;
 263#endif
 264                                    rsum += rsum;
 265                                }
 266                                return (fj_kn_ * rsum);
 267                           }
 268        case kwFjKRatio :   return (fj_ks_ == 0.0 ) ? 0.0 : (fj_kn_/fj_ks_);
 269        case kwFjRmul   :   return fj_rmul();
 270        case kwFjRadius :   return rbar();
 271        case kwFjGap0   :   return fj_gap0();
 272        case kwFjTen    :   return fj_ten();
 273        case kwFjCoh    :   return fj_coh();
 274        case kwFjFa     :   return fj_fa();
 275        case kwFjF      :   var.setValue(fj_f()); return var;
 276        case kwFjM      :   var.setValue(fj_m()); return var;
 277        case kwFjState  :   return bmode_[fj_elem()-1];
 278        case kwFjSlip   :   return smode_[fj_elem()-1];
 279        case kwFjMType  :   return getmType();
 280        case kwFjA      :   return a_[fj_elem()-1];
 281        case kwFjEgap   :   return egap_[fj_elem()-1];
 282        case kwFjGap    :   return gap().x();
 283        case kwFjNstr   :   return -f_[fj_elem()-1].x() / a_[fj_elem()-1];
 284        case kwFjSstr   :   return f_[fj_elem()-1].y() / a_[fj_elem()-1];
 285        case kwFjSs     :   return tauC((-f_[fj_elem()-1].x() / a_[fj_elem()-1]),(bmode_[fj_elem()-1]==3));
 286        case kwFjRelBr  :   var.setValue(DVect2(theta(),thetaM())); return var;
 287        case kwFjCen    :   var.setValue(getRelElemPos(con,fj_elem()-1)); return var;
 288#ifdef THREED
 289        case kwFjNa     :   return fj_na();
 290#endif
 291        case kwFjTrack  :   var.setValue(orientProps_ ? true : false); return var;
 292        case kwUserArea :   return userArea_;
 293        case kwFjCohRes :   return fj_cohres();
 294        case kwFjResMode:   return fj_resmode();
 295        }
 296        assert(0);
 297        return QVariant();
 298    }
 299
 300    bool ContactModelFlatJoint::getPropertyGlobal(uint32 i) const {
 301        switch (i) {
 302        case kwFjF:
 303            return false;
 304        }
 305        return true;
 306    }
 307
 308    bool ContactModelFlatJoint::setProperty(uint32 i,const QVariant &v,IContact *c) {
 309        bool ok(true);
 310        switch (i) {
 311        case kwFjNr: {
 312                if (!propsFixed()) {
 313                    int val(v.toInt(&ok));
 314                    if (!ok || val < 1)
 315                        throw Exception("fj_nr must be an integer greater than 0.");
 316                    fj_nr(val);
 317                    if (fj_elem() > fj_n())
 318                        fj_elem(fj_n());
 319                    initVectors();
 320                    setAreaQuantities();
 321                } else
 322                    throw Exception("fj_nr cannot be modified.");
 323                return true;
 324            }
 325
 326        case kwFjElem: {
 327               int val(v.toInt(&ok));
 328               if (!ok || val < 1 || val > fj_n())
 329                   throw Exception("fj_elem must be an integer between 1 and {0}.",fj_n());
 330               fj_elem(val);
 331               return false;
 332           }
 333        case kwFjKn: {
 334                double val(v.toDouble(&ok));
 335                if (!ok || val<0.0)
 336                    throw Exception("fj_kn must be a positive double.");
 337                fj_kn(val);
 338                return true;
 339            }
 340        case kwFjKs: {
 341                double val(v.toDouble(&ok));
 342                if (!ok || val<0.0)
 343                    throw Exception("fj_ks must be a positive double.");
 344                fj_ks(val);
 345                return true;
 346            }
 347        case kwFjFric: {
 348                double val(v.toDouble(&ok));
 349                if (!ok || val<0.0)
 350                    throw Exception("fj_fric must be a positive double.");
 351                fj_fric(val);
 352                return false;
 353            }
 354        case kwFjRmul: {
 355                if (!propsFixed()) {
 356                    double val(v.toDouble(&ok));
 357                    if (!ok || val<0.01)
 358                        throw Exception("fj_rmul must be a double greater than or equal to 0.01.");
 359                    fj_rmul(val);
 360                    setAreaQuantities();
 361                    return true;
 362                } else
 363                    throw Exception("fj_rmul cannot be modified.");
 364
 365                return false;
 366            }
 367        case kwFjGap0: {
 368                if (!propsFixed()) {
 369                    double val(v.toDouble(&ok));
 370                    if (!ok || val<0.0)
 371                        throw Exception("fj_gap0 must be a positive double.");
 372                    fj_gap0(val);
 373                    if (fj_gap0() > 0.0) {
 374                        for(int i=1; i<=fj_n(); ++i)
 375                            bondElem(i,false);
 376                        // surfaces are parallel w/ gap G
 377                        DVect temp(0.0);
 378                        temp.rx() = fj_gap0();
 379                        gap(temp);
 380                        theta(0.0);
 381                    }
 382                } else
 383                    throw Exception("fj_gap0 cannot be modified.");
 384                return true;
 385            }
 386        case kwFjTen: {
 387                double val(v.toDouble(&ok));
 388                if (!ok || val<0.0)
 389                    throw Exception("fj_ten must be a positive double.");
 390                fj_ten(val);
 391                return false;
 392            }
 393        case kwFjFa: {
 394                double val(v.toDouble(&ok));
 395                if (!ok || val<0.0)
 396                    throw Exception("fj_fa must be a positive double.");
 397                fj_fa(val);
 398                return false;
 399            }
 400        case kwFjCoh: {
 401                double val(v.toDouble(&ok));
 402                if (!ok || val<0.0)
 403                    throw Exception("fj_coh must be a positive double.");
 404                fj_coh(val);
 405                return false;
 406            }
 407        case kwFjA: {
 408                double val(v.toDouble(&ok));
 409                if (!ok || val<0.0)
 410                    throw Exception("fj_area must be a positive double.");
 411                a_[fj_elem()-1] = val;
 412                return false;
 413            }
 414        case kwFjNstr: {
 415                double val(v.toDouble(&ok));
 416                if (!ok || val<0.0)
 417                    throw Exception("fj_sigma must be a positive double.");
 418                f_[fj_elem()-1].rx() = -val * a_[fj_elem()-1];
 419                return false;
 420            }
 421        case kwFjSstr: {
 422                double val(v.toDouble(&ok));
 423                if (!ok || val<0.0)
 424                    throw Exception("fj_tau must be a positive double.");
 425                f_[fj_elem()-1].ry() = val * a_[fj_elem()-1];
 426                return false;
 427            }
 428#ifdef THREED
 429        case kwFjNa: {
 430                if (!propsFixed()) {
 431                    int val(v.toInt(&ok));
 432                    if (!ok || val < 1)
 433                        throw Exception("fj_na must be an integer greater than 0.");
 434                    fj_na(val);
 435                    if (fj_elem() > fj_n())
 436                        fj_elem(fj_n());
 437                    initVectors();
 438                    setAreaQuantities();
 439                } else
 440                    throw Exception("fj_na cannot be modified.");
 441                return true;
 442            }
 443#endif
 444        case kwFjCen: {
 445                if (!v.canConvert<DVect>())
 446                    throw Exception("fj_cen cannot be modified.");
 447                DVect val(v.value<DVect>());
 448                int el = fj_elem()-1;
 449                setRelElemPos(c,el,val);
 450                return false;
 451            }
 452        case kwFjTrack: {
 453                if (!v.canConvert<bool>())
 454                    throw Exception("fj_track must be a boolean.");
 455                bool b = v.toBool();
 456                if (b) {
 457                    if (!orientProps_)
 458                        orientProps_ = NEW orientProps();
 459                } else {
 460                    if (orientProps_) {
 461                        delete orientProps_;
 462                        orientProps_ = 0;
 463                    }
 464                }
 465                return true;
 466            }
 467        case kwUserArea: {
 468                if (!v.canConvert<double>())
 469                    throw Exception("user_area must be a double.");
 470                double val(v.toDouble());
 471                if (val < 0.0)
 472                    throw Exception("Negative user_area not allowed.");
 473                userArea_ = val;
 474                propsFixed_ = false;
 475                return true;
 476            }
 477        case kwFjCohRes: {
 478                double val(v.toDouble(&ok));
 479                if (!ok || val<0.0)
 480                    throw Exception("fj_cohres must be a positive double.");
 481                fj_cohres(val);
 482                return false;
 483            }
 484        case kwFjResMode: {
 485                int val(v.toInt(&ok));
 486                if (!ok || (val != 0 && val != 1))
 487                    throw Exception("fj_resmode must be 0 or 1.");
 488                fj_resmode(val);
 489                return false;
 490            }
 491        }
 492        return false;
 493    }
 494
 495    bool ContactModelFlatJoint::getPropertyReadOnly(uint32 i) const {
 496        switch (i) {
 497        case kwFjF:
 498        case kwFjM:
 499        case kwFjGap:
 500        case kwFjRelBr:
 501        case kwFjState:
 502        case kwFjSlip:
 503        case kwFjEgap:
 504        case kwFjNstr:
 505        case kwFjSstr:
 506        case kwFjSs:
 507        case kwFjRadius:
 508            return true;
 509        default:
 510            break;
 511        }
 512        return false;
 513    }
 514
 515    QString  ContactModelFlatJoint::getMethodArguments(uint32 i) const {
 516        switch (i) {
 517        case kwBond:
 518        case kwUnbond:
 519            return "gap,element";
 520        case KwDeformability:
 521            return "emod,kratio";
 522        case kwInitialize:
 523            return "force,moment";
 524        }
 525        return QString();
 526    }
 527
 528    bool ContactModelFlatJoint::setMethod(uint32 i,const QVector<QVariant> &vl,IContact *con) {
 529        IContactMechanical *c(convert_getcast<IContactMechanical>(con));
 530        bool bond(false);
 531        switch (i) {
 532        case kwBond:
 533            bond = true;
 534            [[fallthrough]];
 535        case kwUnbond: {
 536                int seg(0);
 537                double mingap = -1.0 * limits<double>::max();
 538                double maxgap = 0;
 539                if (vl.size()==2) {
 540                    // The first is the gap
 541                    QVariant arg = vl.at(0);
 542                    if (!arg.isNull()) {
 543                        if (arg.canConvert<double>())
 544                            maxgap = vl.at(0).toDouble();
 545                        else if (arg.canConvert<DVect2>()) {
 546                            DVect2 value = vl.at(0).value<DVect2>();
 547                            mingap = value.minComp();
 548                            maxgap = value.maxComp();
 549                        } else
 550                            throw Exception("Argument {0} not recognized in method {1} in contact model {2}.",vl.at(0),bond ? "bond":"unbond",getName());
 551                    }
 552                    arg = vl.at(1);
 553                    if (!arg.isNull()) {
 554                        seg = vl.at(1).toUInt();
 555                        if (seg < 1)
 556                            throw Exception("Element indices start at 1 in method {0} in contact model {1}.",bond ? "bond":"unbond",getName());
 557                        if (seg > fj_n())
 558                            throw Exception("Element index {0} exceeds segments number ({1}) in method {2} in contact model {3}.",seg,fj_n(),bond ? "bond":"unbond",getName());
 559                    }
 560                }
 561                double gap = c->getGap();
 562                if (gap >= mingap && gap <= maxgap) {
 563                    if (!seg) {
 564                        for(int iSeg=1; iSeg<=fj_n(); ++iSeg)
 565                            bondElem(iSeg,bond);
 566                    } else {
 567                        bondElem(seg,bond);
 568                    }
 569                    // If have installed bonds and tracking is enabled then set the contact normal appropriately
 570                    if (orientProps_) {
 571                        orientProps_->orient1_ = Quat::identity();
 572                        orientProps_->orient2_ = Quat::identity();
 573                        orientProps_->origNormal_ = toVect(con->getNormal());
 574                    }
 575                }
 576                return true;
 577             }
 578        case KwDeformability:
 579            {
 580                double emod;
 581                double krat;
 582                if (vl.at(0).isNull())
 583                    throw Exception("Argument emod must be specified with method deformability in contact model {0}.",getName());
 584                emod = vl.at(0).toDouble();
 585                if (emod<0.0)
 586                    throw Exception("Negative emod not allowed in contact model {0}.",getName());
 587                if (vl.at(1).isNull())
 588                    throw Exception("Argument kratio must be specified with method deformability in contact model {0}.",getName());
 589                krat = vl.at(1).toDouble();
 590                if (krat<0.0)
 591                    throw Exception("Negative stiffness ratio not allowed in contact model {0}.",getName());
 592                double rsum(0.0);
 593                if (c->getEnd1Curvature().y())
 594                    rsum += 1.0/c->getEnd1Curvature().y();
 595                if (c->getEnd2Curvature().y())
 596                    rsum += 1.0/c->getEnd2Curvature().y();
 597                if (userArea_) {
 598#ifdef THREED
 599                    rsum = std::sqrt(userArea_ / dPi);
 600#else
 601                    rsum = userArea_ / 2.0;
 602#endif
 603                    rsum += rsum;
 604                }
 605                fj_kn_ = emod / rsum;
 606                fj_ks_ = (krat == 0.0) ? 0.0 : fj_kn_ / krat;
 607                return true;
 608            }
 609        case KwUpdateGeom: {
 610                // go through and update the total area (atot) and the
 611                // radius rbar
 612                double at = 0.0;
 613                for (int i=1; i<=fj_n(); ++i)
 614                    at += a_[i-1];
 615                atot(at);
 616                //get the equivalent radius
 617#ifdef THREED
 618                rbar(sqrt(at/dPi));
 619#else
 620                rbar(at/2.0);
 621#endif
 622                return true;
 623            }
 624        case kwArea: {
 625                if (!userArea_) {
 626                    double rsq(1./std::max(c->getEnd1Curvature().y(),c->getEnd2Curvature().y()));
 627#ifdef THREED
 628                    userArea_ = rsq * rsq * dPi;
 629#else
 630                    userArea_ = rsq * 2.0;
 631#endif
 632                }
 633                return true;
 634            }
 635        case kwInitialize: {
 636                DVect force;
 637                DAVect moment;
 638                if (vl.at(0).isNull())
 639                    throw Exception("Argument force must be specified with method initialize in contact model {0}.",getName());
 640                force = vl.at(0).value<DVect>();
 641                if (vl.at(1).isNull())
 642                    throw Exception("Argument moment must be specified with method initialize in contact model {0}.",getName());
 643#ifdef THREED
 644                moment = vl.at(1).value<DVect>();
 645#else
 646                moment.rz() = vl.at(1).toDouble();
 647#endif
 648                // Set the gap accordingly to get the correct force
 649                setForce(force,con);
 650                fj_m_set(moment);
 651                return true;
 652            }
 653        }
 654        return false;
 655    }
 656
 657    double ContactModelFlatJoint::getEnergy(uint32 i) const {
 658        double ret(0.0);
 659        if (!energies_)
 660            return ret;
 661        switch (i) {
 662        case kwEStrain:  return energies_->estrain_;
 663        case kwESlip:    return energies_->eslip_;
 664        }
 665        assert(0);
 666        return ret;
 667    }
 668
 669    bool ContactModelFlatJoint::getEnergyAccumulate(uint32 i) const {
 670        switch (i) {
 671        case kwEStrain:  return false;
 672        case kwESlip:    return true;
 673        }
 674        assert(0);
 675        return false;
 676    }
 677
 678    void ContactModelFlatJoint::setEnergy(uint32 i,const double &d) {
 679        if (!energies_) return;
 680        switch (i) {
 681        case kwEStrain:  energies_->estrain_ = d; return;
 682        case kwESlip:    energies_->eslip_   = d; return;
 683        }
 684        assert(0);
 685        return;
 686    }
 687
 688    bool ContactModelFlatJoint::validate(ContactModelMechanicalState *state,const double &) {
 689        assert(state);
 690        const IContactMechanical *c = state->getMechanicalContact();
 691        assert(c);
 692        // This presumes that one of the ends has a non-zero curvature
 693        rmin(1.0/std::max(c->getEnd1Curvature().y(),c->getEnd2Curvature().y()));
 694        if (userArea_) {
 695#ifdef THREED
 696            rmin(std::sqrt(userArea_ / dPi));
 697#else
 698            rmin(userArea_ / 2.0);
 699#endif
 700        }
 701        if (!propsFixed()) {
 702            setAreaQuantities();
 703            mType(getmType());
 704        }
 705
 706        // Initialize the tracking if not initialized
 707        if (orientProps_ && orientProps_->origNormal_ == DVect(0.0)) {
 708            orientProps_->origNormal_ = toVect(c->getContact()->getNormal());
 709            orientProps_->orient1_ = Quat::identity();
 710            orientProps_->orient2_ = Quat::identity();
 711        }
 712
 713        if (state->trackEnergy_)
 714            activateEnergy();
 715
 716        updateEffectiveStiffness(state);
 717        return checkActivity(state->gap_);
 718    }
 719
 720    void ContactModelFlatJoint::updateEffectiveStiffness(ContactModelMechanicalState *) {
 721        DVect2 ret(fj_kn_,fj_ks_);
 722        ret *= atot();
 723        effectiveTranslationalStiffness(ret);
 724#ifdef TWOD
 725        effectiveRotationalStiffness(DAVect(fj_kn() * (2.0/3.0)*rbar()*rbar()*rbar()));
 726#else
 727        double piR4 = dPi * rbar() * rbar() * rbar() * rbar();
 728        double t = fj_kn() * 0.25 * piR4;
 729        effectiveRotationalStiffness(DAVect(fj_ks() * 0.50 * piR4,t,t));
 730#endif
 731    }
 732
 733    bool ContactModelFlatJoint::forceDisplacementLaw(ContactModelMechanicalState *state,const double &timestep) {
 734        FP_S;
 735        if (!propsFixed())
 736            propsFixed(true);
 737        FP_S;
 738        timestep;
 739        assert(state);
 740
 741        FP_S;
 742        if (state->activated()) {
 743            if (cmEvents_[fActivated] >= 0) {
 744                auto c = state->getContact();
 745                std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()) };
 746                IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
 747                fi->setCMFishCallArguments(c,arg,cmEvents_[fActivated]);
 748            }
 749        }
 750        FP_S;
 751
 752        // Update the orientations
 753        if (orientProps_) {
 754            orientProps_->orient1_.increment(state->getMechanicalContact()->getEnd1Mechanical()->getAngVelocity()*timestep);
 755            orientProps_->orient2_.increment(state->getMechanicalContact()->getEnd2Mechanical()->getAngVelocity()*timestep);
 756        }
 757        FP_S;
 758
 759#ifdef TWOD
 760        // Translational increment in local coordinates
 761        DVect del_U = state->relativeTranslationalIncrement_;
 762        double del_theta  = state->relativeAngularIncrement_.z();
 763        gap(gap() + del_U); // in normal and shear direction in 2D
 764        theta(theta() + del_theta);
 765        double dSig=0.0, dTau=0.0;
 766        double delX = 2*rbar() / fj_n();
 767        double rbar2 = rbar() / fj_n();
 768        DVect dFSum(0.0);
 769        double dMSum = 0.0;
 770        if (state->trackEnergy_) {
 771            assert(energies_);
 772            energies_->estrain_ =  0.0;
 773        }
 774        bool oneBonded = false;
 775        for(int i=0; i<fj_n(); ++i) {
 776            double g0 = gap((i  )*delX);
 777            double g1 = gap((i+1)*delX);
 778            double gMid = 0.5*(g0 + g1);
 779            if (bmode_[i] != 3 && gMid > 0) {
 780                egap_[i] = gMid;
 781                f_[i] = DVect(0.0);
 782                continue;
 783            }
 784            dSig = computeSig(g0,g1,rbar2,a_[i],(bmode_[i]==3));
 785            bool tensileBreak = false;
 786            if (bmode_[i]==3) {
 787                if (state->canFail_ && dSig >= fj_ten()) {
 788                    breakBond(i+1,1,state);
 789                    dSig = dTau = 0.0;
 790                    tensileBreak = true;
 791                }
 792            }
 793            if (!tensileBreak) {
 794                dTau = f_[i].y() / a_[i];
 795                double dUse = delUse(egap_[i],gMid,(bmode_[i]==3),del_U.y());
 796                double dtauP = dTau - fj_ks()*dUse;
 797                double dtauPabs = abs(dtauP);
 798                if (bmode_[i]==3) { // bonded
 799                    if (dtauPabs < tauC(dSig,true))
 800                        dTau = dtauP;
 801                    else {
 802                        if (state->canFail_) {
 803                            breakBond(i+1,2,state);
 804                            if (fj_resmode() == 0)
 805                                dSig = dTau = 0.0;
 806                            else
 807                                dTau = fj_cohres() - dSig * fj_fric();
 808                        }
 809                    }
 810                } else {             // unbonded
 811                    double dtauC = tauC(dSig,false);
 812                    if (dtauPabs <= dtauC) {
 813                        dTau = dtauP;
 814                        slipChange(i+1,false,state);
 815                    } else {
 816                        dTau = dtauP * ( dtauC / dtauPabs );
 817                        slipChange(i+1,true,state);
 818                        if (state->trackEnergy_) { energies_->eslip_ += dtauC*a_[i]*abs(dUse);}
 819                    }
 820                }
 821            }
 822            oneBonded = true;
 823            egap_[i] = gMid;
 824            f_[i] = DVect(-dSig*a_[i],dTau*a_[i]);
 825            dFSum += f_[i];
 826            double m = computeM(g0,g1,rbar2,(bmode_[i]==3)) + fj_m_set().z()/fj_n();
 827            dMSum  += m - rBarl_[i]*f_[i].x();
 828            if (state->trackEnergy_) {
 829                if (fj_kn_) {
 830                    double ie = 2.0*rBarl_[i]*rBarl_[i]*rBarl_[i] / 3.0;
 831                    energies_->estrain_ += 0.5*(dSig*dSig*a_[i] + m*m/ie) / fj_kn_;
 832                }
 833                if (fj_ks_) {
 834                    energies_->estrain_ += 0.5 * dTau*dTau*a_[i] / fj_ks_;
 835                }
 836            }
 837        }
 838        //
 839        fj_f(dFSum);
 840        fj_m(DAVect(dMSum));
 841        if (!oneBonded)
 842            fj_m_set(DAVect(0.0));
 843#else
 844        FP_S;
 845        CAxes localSys = state->axes_;
 846        DVect trans = state->relativeTranslationalIncrement_; // translation increment in local coordinates
 847        DAVect ang = state->relativeAngularIncrement_; // rotational increment in local coordinates
 848        DVect shear(0.0,trans.y(),trans.z());
 849        DVect del_Us = localSys.toGlobal(shear); // In global coordinates
 850        // What is the twist in global coordinates?
 851        DVect del_Theta_t = localSys.e1()*ang.x();
 852        theta_ += ang.y();
 853        thetaM_ += ang.z();
 854
 855        FP_S;
 856        gap(gap() + trans);
 857        if (state->trackEnergy_) {
 858            assert(energies_);
 859            energies_->estrain_ =  0.0;
 860        }
 861        FP_S;
 862        DVect force(0.0);
 863        DAVect mom(0.0);
 864        bool oneBonded = false;
 865        FP_S;
 866        for (int e=1,i=0; e<=fj_n(); ++e, ++i) {
 867            FP_S;
 868            double gBar1 = gap( rBarl_[i].x(),rBarl_[i].y());
 869            FP_S;
 870            if (!Bonded(e) && gBar1 > 0) {
 871                FP_S;
 872                egap_[i] = gBar1;
 873                f_[i] = DVect(0.0);
 874                FP_S;
 875                continue;
 876            }
 877            FP_S;
 878            DVect r = localSys.e2()*rBarl_[i].x() + localSys.e3()*rBarl_[i].y(); // location of element point
 879            FP_S;
 880            double sigBar_e = sigBar(e);
 881            f_[i].rx() = -sigBar_e * a_[i]; // Step 1...
 882            FP_S;
 883            if (Bonded(e) && (sigBar_e >= fj_ten())) { // break bond in tension
 884                FP_S;
 885                if (state->canFail_) {
 886                    FP_S;
 887                    breakBond(e,1,state);
 888                    FP_S;
 889                    f_[i] = DVect(0.0);
 890                }
 891                FP_S;
 892            } else {
 893                FP_S;
 894                DVect del_us  = del_Us + (del_Theta_t & r); // In global - has the twist in there
 895                double  del_usl = delUse(egap_[i],gBar1,Bonded(e),(del_us | localSys.e2()));
 896                double  del_usm = delUse(egap_[i],gBar1,Bonded(e),(del_us | localSys.e3()));
 897                double Fs_lP = f_[i].y() - fj_ks() * a_[i] * del_usl;
 898                double Fs_mP = f_[i].z() - fj_ks() * a_[i] * del_usm;
 899                FP_S;
 900                double FsPMag = sqrt( Fs_lP*Fs_lP + Fs_mP*Fs_mP );
 901                FP_S;
 902                double tauBarP = FsPMag / a_[i];
 903                FP_S;
 904                if ( !Bonded(e) ) {
 905                    FP_S;
 906                    double tau_c = sigBar_e < 0.0 ? fj_cohres()-fj_fric()*sigBar_e : 0.0;
 907                    if ( tauBarP <= tau_c ) {
 908                        f_[i].ry() = Fs_lP;
 909                        f_[i].rz() = Fs_mP;
 910                        slipChange(e,false,state);
 911                    } else { // enforce sliding
 912                        FP_S;
 913                        double sFac = tau_c * a_[i] / FsPMag;
 914                        FP_S;
 915                        f_[i].ry() = Fs_lP * sFac;
 916                        f_[i].rz() = Fs_mP * sFac;
 917                        slipChange(e,true,state);
 918                        if (state->trackEnergy_) { energies_->eslip_ += tau_c*a_[i]*sqrt(del_usl*del_usl+del_usm*del_usm);}
 919                    }
 920                } else { // Bonded(e)
 921                    FP_S;
 922                    double tau_c = fj_coh() - sigBar_e * tan(dDegrad*fj_fa());
 923                    if ( tauBarP <= tau_c ) {
 924                        f_[i].ry() = Fs_lP;
 925                        f_[i].rz() = Fs_mP;
 926                    } else { // break bond in shear
 927                        if (state->canFail_) {
 928                            breakBond(e,2,state);
 929                            if (fj_resmode() == 0)
 930                                f_[i] = DVect(0.0);
 931                            else {
 932                                double newForce = fj_cohres() - sigBar_e * fj_fric();
 933                                if (!userArea_)
 934                                    newForce *= a_[i];
 935                                else
 936                                    newForce *= userArea_ / fj_n();
 937                                FP_S;
 938                                newForce /= std::sqrt(f_[i].y()*f_[i].y() + f_[i].z()*f_[i].z());
 939                                FP_S;
 940                                f_[i].ry() *= newForce;
 941                                f_[i].rz() *= newForce;
 942                            }
 943                        }
 944                    }
 945                    FP_S;
 946                }
 947            }
 948            oneBonded = true;
 949            force += f_[i];
 950            FP_S;
 951            mom += (localSys.toLocal(r) & f_[i]) + fj_m_set()/fj_n();
 952            FP_S;
 953            egap_[i] = gBar1;
 954            if (state->trackEnergy_) {
 955                energies_->estrain_ += computeStrainEnergy(e);
 956                FP_S;
 957            }
 958            FP_S;
 959        }
 960        FP_S;
 961        fj_f(force);
 962        fj_m(mom);
 963        if (!oneBonded)
 964            fj_m_set(DAVect(0.0));
 965        FP_S;
 966#endif
 967        assert(fj_f_ == fj_f_);
 968        FP_S;
 969        return checkActivity(0.0);
 970    }
 971
 972    bool ContactModelFlatJoint::thermalCoupling(ContactModelMechanicalState*, ContactModelThermalState* ts, IContactThermal*, const double&) {
 973        // Account for thermal expansion in incremental mode
 974        if (ts->gapInc_ == 0.0) return false;
 975        DVect dg(0.0);
 976        dg.rx() = ts->gapInc_;
 977        gap(gap() + dg);
 978        return true;
 979    }
 980
 981    void ContactModelFlatJoint::setAreaQuantities() {
 982        rbar(fj_rmul() * rmin());
 983#ifdef TWOD
 984        atot(2.0 * rbar());
 985        double v = atot()/fj_n();
 986        for (int i=1; i<=fj_n(); ++i) {
 987            a_[i-1] = v;
 988            rBarl_[i-1] = rbar() * (double(-2*i + 1 + fj_n()) / fj_n());
 989        }
 990#else
 991        atot(dPi * rbar() * rbar());
 992        double del_r  = rbar() / fj_nr();
 993        double del_al = 2.0*dPi / fj_na();
 994        double fac = 2.0/3.0;
 995        for (int i=0; i < fj_n(); ++i) {
 996            double dVal = i / fj_na();
 997            int I = (int)floor( dVal );
 998            int J = i - I*fj_na();
 999            double r1  =  I      * del_r;
1000            double r2  = (I + 1) * del_r;
1001            double al1 =  J      * del_al;
1002            double al2 = (J + 1) * del_al;
1003            a_[i] = 0.5 * (al2 - al1) * (r2*r2 - r1*r1);
1004            rBarl_[i] = DVect2(((sin(al2) - sin(al1)) / (al2 - al1))*((r2*r2*r2 - r1*r1*r1)/(r2*r2 - r1*r1)),
1005                               ((cos(al1) - cos(al2)) / (al2 - al1))*((r2*r2*r2 - r1*r1*r1)/(r2*r2 - r1*r1)))*fac;
1006        }
1007#endif
1008        updateEffectiveStiffness(0);
1009    }
1010
1011    DVect ContactModelFlatJoint::getRelElemPos(const IContact* c,int i) const {
1012        DVect ret(0.0);
1013        if (c) {
1014            ret = c->getPosition();
1015            CAxes localSys = c->getLocalSystem();
1016#ifdef TWOD
1017            ret += localSys.e2()*rBarl_[i];
1018#else
1019            ret += localSys.e2()*rBarl_[i].x() + localSys.e3()*rBarl_[i].y();
1020#endif
1021        }
1022        return ret;
1023    }
1024
1025    void ContactModelFlatJoint::setRelElemPos(const IContact* c,int i,const DVect &pos) {
1026        // pos is a position in space in global coordinates
1027        propsFixed(true);
1028        if (c) {
1029            // project onto the plane
1030            DVect cp = c->getPosition();
1031            DVect norm = toVect(c->getNormal());
1032            double sd = norm|(cp - pos);
1033            // np is the point on the plane
1034            DVect np = pos+norm*sd;
1035            np = np-cp;
1036            CAxes localSys = c->getLocalSystem();
1037            np = localSys.toLocal(np);
1038#ifdef TWOD
1039            rBarl_[i] = np.y();
1040#else
1041            rBarl_[i] = DVect2(np.y(),np.z());
1042#endif
1043        }
1044    }
1045
1046    int ContactModelFlatJoint::getmType() const {
1047        if (propsFixed()) return mType();
1048        //
1049        if (fj_gap0() > 0.0)   return 2;
1050        //
1051        // If we get to here, then G == 0.0.
1052        bool AllBonded = true;
1053        bool AllSlit = true;
1054        for(int i=0; i<fj_n(); ++i) {
1055            if (bmode_[i] != 3) AllBonded = false;
1056            else AllSlit = false;
1057        }
1058        if (AllBonded) return 1;
1059        if (AllSlit)   return 3;
1060        //
1061        return 4;
1062    }
1063
1064    void ContactModelFlatJoint::bondElem(int iSeg,bool bBond ) {
1065        if (bBond) {
1066            if (fj_gap0() == 0.0) {
1067                bmode_[iSeg-1]  = 3;
1068            } else
1069                bmode_[iSeg-1] = 0;
1070        } else
1071            bmode_[iSeg-1] = 0;
1072    }
1073
1074    void ContactModelFlatJoint::breakBond(int iSeg,int fmode,ContactModelMechanicalState *state) {
1075        bmode_[iSeg-1]  = fmode;
1076        if (cmEvents_[fBondBreak] >= 0) {
1077            auto c = state->getContact();
1078            std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
1079                                                 fish::Parameter((qint64)iSeg),
1080                                                 fish::Parameter((qint64)fmode),
1081                                                 fish::Parameter(computeStrainEnergy(iSeg))
1082                                               };
1083            IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1084            fi->setCMFishCallArguments(c,arg,cmEvents_[fBondBreak]);
1085        }
1086        if (!isBonded() && cmEvents_[fBroken] >= 0) {
1087            auto c = state->getContact();
1088            std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()) };
1089            IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1090            fi->setCMFishCallArguments(c,arg,cmEvents_[fBroken]);
1091        }
1092    }
1093
1094    void ContactModelFlatJoint::slipChange(int iSeg,bool smode,ContactModelMechanicalState *state) {
1095        bool emitEvent = false;
1096        if (smode) {
1097            if (!smode_[iSeg-1]) {
1098                emitEvent = true;
1099                smode_[iSeg-1] = smode;
1100            }
1101        } else {
1102            if (smode_[iSeg-1]) {
1103                emitEvent = true;
1104                smode_[iSeg-1] = smode;
1105            }
1106        }
1107        if (emitEvent && cmEvents_[fSlipChange] >= 0) {
1108            auto c = state->getContact();
1109            std::vector<fish::Parameter> arg = { fish::Parameter(c->getIThing()),
1110                                                 fish::Parameter((qint64)iSeg),
1111                                                 fish::Parameter(smode) };
1112            IFishCallList *fi = const_cast<IFishCallList*>(state->getProgram()->findInterface<IFishCallList>());
1113            fi->setCMFishCallArguments(c,arg,cmEvents_[fSlipChange]);
1114        }
1115    }
1116
1117    double ContactModelFlatJoint::tauC(const double &dSig,bool bBonded) const {
1118        if (bBonded) return (fj_coh() + (tan(dDegrad*fj_fa()) * (-dSig)) );
1119        else
1120            return (dSig < 0.0 ? fj_cohres() - fj_fric() * dSig : 0.0 );
1121    }
1122
1123#ifdef THREED
1124    double ContactModelFlatJoint::xi(int comp) const {
1125        if (comp == 1) return abs(theta_) <= 1e-12 ? 0.0 : theta_/thbMag();
1126        else           return abs(thetaM_) <= 1e-12 ? 0.0 : thetaM_/thbMag();
1127    }
1128#endif
1129
1130    void ContactModelFlatJoint::initVectors() {
1131        a_.resize(fj_n());
1132        rBarl_.resize(fj_n());
1133        bmode_.resize(fj_n());
1134        smode_.resize(fj_n());
1135        egap_.resize(fj_n());
1136        f_.resize(fj_n());
1137        for (int i=0; i<fj_n(); ++i) {
1138            a_[i] = egap_[i] = 0.0;
1139#ifdef THREED
1140            rBarl_[i] = DVect2(0.0);
1141#else
1142            rBarl_[i] = 0.0;
1143#endif
1144            f_[i] = DVect(0.0);
1145            bmode_[i] = 0;
1146            smode_[i] = false;
1147        }
1148    }
1149
1150#ifdef TWOD
1151    double ContactModelFlatJoint::gap(const double &x) const {
1152        return gap().x() + theta()*(x - rbar());
1153    }
1154#else
1155    double ContactModelFlatJoint::gap(const double &r_l,const double &r_m ) const {
1156        FP_S;
1157        auto ret = gap().x() + ( r_m*xi(1) - r_l*xi(2) ) * thbMag();
1158        FP_S;
1159        return ret;
1160    }
1161
1162    double ContactModelFlatJoint::sigBar(int e) const {
1163        FP_S;
1164        if (!Bonded(e) && gap(rBarl_[e-1].x(),rBarl_[e-1].y()) >= 0.0) {
1165            FP_S;
1166            return 0.0;
1167        } else {
1168            auto ret = fj_kn() * gap(rBarl_[e-1].x(),rBarl_[e-1].y());
1169            FP_S;
1170            return ret;
1171        }
1172    }
1173
1174    double ContactModelFlatJoint::tauBar(int e) const {
1175        return a_[e-1] <= 1e-12 ?
1176        0.0 : sqrt(f_[e-1].y()*f_[e-1].y() + f_[e-1].z()*f_[e-1].z())/a_[e-1] ;
1177    }
1178
1179#endif
1180
1181    double ContactModelFlatJoint::computeStrainEnergy(int e) const {
1182        double ret(0.0);
1183        int i = e - 1;
1184#ifdef TWOD
1185        double delX = 2 * rbar() / fj_n();
1186        double g0 = gap((i)*delX);
1187        double g1 = gap((i + 1)*delX);
1188        double rbar2 = rbar() / fj_n();
1189        double dSig = computeSig(g0, g1, rbar2, a_[i], (bmode_[i] == 3));
1190        double m = computeM(g0, g1, rbar2, (bmode_[i] == 3));
1191        double dTau = f_[i].y() / a_[i]; // only valid before failure
1192        if (fj_kn_) {
1193            double ie = 2.0*rBarl_[i] * rBarl_[i] * rBarl_[i] / 3.0;
1194            ret += 0.5*(dSig*dSig*a_[i] + m * m / ie) / fj_kn_;
1195        }
1196        if (fj_ks_) {
1197            ret += 0.5 * dTau*dTau*a_[i] / fj_ks_;
1198        }
1199#else
1200        if (fj_kn_) {
1201            ret += 0.5*(sigBar(e)*sigBar(e)*a_[i]) / fj_kn_;
1202        }
1203        if (fj_ks_) {
1204            ret += 0.5 * (f_[i].y()*f_[i].y() + f_[i].z()*f_[i].z()) / (fj_ks_*a_[i]);
1205        }
1206#endif
1207        return ret;
1208    }
1209
1210    double ContactModelFlatJoint::computeSig(const double &g0,const double &g1,const double &rbar,
1211                                             const double &dA,bool bBonded ) const {
1212        double gTerm;
1213        switch (getCase(g0, g1)) {
1214        case 1:
1215            if (bBonded)       gTerm =  (g0 + g1);
1216            else if (g0 < 0.0) gTerm = -( g0*g0 / (g1 - g0) );
1217            else               gTerm =  ( g1*g1 / (g1 - g0) );
1218            break;
1219        case 2:
1220            if (bBonded) gTerm = (g0 + g1);
1221            else         gTerm = 0.0;
1222            break;
1223        case 3:
1224            gTerm = (g0 + g1);
1225            break;
1226        default:  gTerm = 0.0;  break;
1227        }
1228        return (fj_kn() * gTerm * rbar) / dA;
1229    }
1230
1231    double ContactModelFlatJoint::computeM(const double &g0,const double &g1,const double &rbar,
1232                                           bool bBonded) const {
1233        double gTerm;
1234        switch (getCase(g0,g1)) {
1235            case 1:
1236                if (bBonded)       gTerm = -((g1 - g0) / 3.0);
1237                else if (g0 < 0.0) gTerm = g0*g0*(g0 - 3.0*g1) / (3.0*(g1-g0)*(g1-g0));
1238                else               gTerm = -(((g1-g0)*(g1-g0)*(g1-g0) + g0*g0*(g0 - 3.0*g1))
1239                                            / (3.0*(g1-g0)*(g1-g0)));
1240            break;
1241          case 2:
1242                if (bBonded) gTerm = -((g1 - g0) / 3.0);
1243                else         gTerm = 0.0;
1244                break;
1245          case 3:
1246                gTerm = -((g1 - g0) / 3.0);
1247                break;
1248          default:  gTerm = 0.0;  break;
1249        }
1250        return fj_kn() * gTerm * rbar*rbar;
1251    }
1252
1253    int ContactModelFlatJoint::getCase(const double &g0,const double &g1) const {
1254        if (g0 * g1 < 0.0) // Case 1: gap changes sign
1255            return 1;
1256        else if (g0 >= 0.0 && g1 >= 0.0) // Case 2: gap remains positive or zero
1257            return 2;
1258        else // Case 3: gap remains negative
1259            return 3;
1260    }
1261
1262    double ContactModelFlatJoint::delUse(const double &gapStart,const double &gapEnd,bool bBonded,
1263                                         const double &delUs) const {
1264        if ( bBonded ) return delUs;
1265        if ( gapStart <= 0.0 ) {
1266            if ( gapEnd <= 0.0 )
1267                return delUs;
1268            else { // gapEnd > 0.0
1269                double xi = -gapStart / (gapEnd - gapStart);
1270                return delUs * xi;
1271            }
1272        } else { // gapStart > 0.0
1273            if ( gapEnd >= 0.0 )
1274                return 0.0;
1275            else { // gapEnd < 0.0
1276                double xi = -gapStart / (gapEnd - gapStart);
1277                return delUs * (1.0 - xi);
1278            }
1279        }
1280    }
1281
1282    bool ContactModelFlatJoint::checkActivity(const double &inGap) {
1283        FP_S;
1284        // If any subcontact is bonded return true
1285        FOR(it,bmode_) if ((*it) == 3)
1286            return true;
1287        FP_S;
1288        // If the normal gap is less than 2*rbar return true
1289        if (gap().x() < 2.0*rbar())
1290            return true;
1291        FP_S;
1292        // check to see if there is overlap (based on the initial gap) to reset activity if the contact has been previously deactivated
1293        if (inGap < 0) {
1294            // reset the relative rotation
1295            theta(0.0);
1296#ifdef THREED
1297            thetaM(0.0);
1298#endif
1299            // set the gap to be the current gap, removing the shear displacement
1300            DVect inp(inGap,0.0);
1301            gap(inp);
1302            FP_S;
1303            return true;
1304        }
1305        FP_S;
1306        return false;
1307    }
1308
1309    void ContactModelFlatJoint::setForce(const DVect &v,IContact *) {
1310        fj_f_ = v;
1311        DVect df = v / f_.size();
1312        for (int i=0; i<f_.size(); ++i)
1313            f_[i] = df;
1314        // Set gap consistent with normal force
1315        double at = userArea_;
1316        if (!userArea_) {
1317            for (int i = 1; i <= fj_n(); ++i)
1318                at += a_[i - 1];
1319        }
1320        gap_.rx() = -1.0 * v.x() / (fj_kn_ * at);
1321    }
1322
1323    void ContactModelFlatJoint::getSphereList(const IContact *con,std::vector<DVect> *pos,std::vector<double> *rad,std::vector<double> *val) {
1324        assert(pos);
1325        assert(rad);
1326        assert(val);
1327        if (!orientProps_)
1328            return;
1329        // find minimal radii for end1
1330        double br = convert_getcast<IContactMechanical>(con)->getEnd1Curvature().y();
1331        if (br) {
1332            const IPiece *p = con->getEnd1();
1333            FArray<const IContact*> arr;
1334            p->getContactList(&arr);
1335            double maxgap = 0.0;
1336            FOR(ic,arr) {
1337                const IContactMechanical *mc = convert_getcast<IContactMechanical>(*ic);
1338                const IContactModelMechanical *mcm = mc->getModelMechanical();
1339                if (mcm->getContactModel()->getIndex() == ContactModelFlatJoint::getIndex()) {
1340                    const ContactModelFlatJoint *in = dynamic_cast<const ContactModelFlatJoint*>(mcm);
1341                    maxgap = std::max<double>(maxgap,in->gap().x()- mc->getGap());
1342                }
1343            }
1344            br = 1.0 / br - 0.5*maxgap;
1345            const IContactMechanical *mc = convert_getcast<IContactMechanical>(con);
1346            pos->push_back(convert_getcast<IPieceMechanical>(mc->getEnd1())->getPosition());
1347            rad->push_back(br);
1348            val->push_back(mc->getEnd1()->getIThing()->getID());
1349        }
1350
1351        // Give the end2 sphere - bummer
1352        br = convert_getcast<IContactMechanical>(con)->getEnd2Curvature().y();
1353        if (br) {
1354            const IPiece *p = con->getEnd2();
1355            FArray<const IContact*> arr;
1356            p->getContactList(&arr);
1357            double maxgap = 0.0;
1358            FOR(ic,arr) {
1359                const IContactMechanical *mc = convert_getcast<IContactMechanical>(*ic);
1360                const IContactModelMechanical *mcm = mc->getModelMechanical();
1361                if (mcm->getContactModel()->getIndex() == ContactModelFlatJoint::getIndex()) {
1362                    const ContactModelFlatJoint *in = dynamic_cast<const ContactModelFlatJoint*>(mcm);
1363                    maxgap = std::max<double>(maxgap,in->gap().x()- mc->getGap());
1364                }
1365            }
1366            br = 1.0 / br - 0.5*maxgap;
1367            const IContactMechanical *mc = convert_getcast<IContactMechanical>(con);
1368            pos->push_back(convert_getcast<IPieceMechanical>(mc->getEnd2())->getPosition());
1369            rad->push_back(br);
1370            val->push_back(mc->getEnd2()->getIThing()->getID());
1371        }
1372    }
1373
1374#ifdef THREED
1375
1376    void ContactModelFlatJoint::getDiskList(const IContact *con,std::vector<DVect> *pos,std::vector<DVect> *normal,std::vector<double> *radius,std::vector<double> *val) {
1377        assert(pos);
1378        assert(normal);
1379        assert(radius);
1380        assert(val);
1381        if (!orientProps_)
1382            return;
1383        // plot the contact plane right in the middle of the 2 normals
1384        double rad = fj_rmul()*rmin();
1385        DVect axis1 = orientProps_->orient1_.rotate(orientProps_->origNormal_);
1386        DVect axis2 = orientProps_->orient2_.rotate(orientProps_->origNormal_);
1387        DVect norm = ((axis1.unit()+axis2.unit())*0.5).unit();
1388        pos->push_back(con->getPosition());
1389        normal->push_back(norm);
1390        radius->push_back(rad);
1391        const IContactMechanical *mc = convert_getcast<IContactMechanical>(con);
1392        val->push_back(mc->getLocalForce().mag());
1393    }
1394
1395#endif
1396
1397    void ContactModelFlatJoint::getCylinderList(const IContact *con,std::vector<DVect> *bot,std::vector<DVect> *top,std::vector<double> *radlow,std::vector<double> *radhi,std::vector<double> *val) {
1398        assert(bot);
1399        assert(top);
1400        assert(radlow);
1401        assert(radhi);
1402        assert(val);
1403        if (!orientProps_)
1404            return;
1405        const IContactMechanical *mc = convert_getcast<IContactMechanical>(con);
1406        double br = mc->getEnd1Curvature().y(), br2 = mc->getEnd2Curvature().y();
1407        if (userArea_) {
1408#ifdef THREED
1409            br = std::sqrt(userArea_ / dPi);
1410#else
1411            br = userArea_ / 2.0;
1412#endif
1413            br = 1. / br;
1414            br2 = br;
1415        }
1416
1417        double cgap = mc->getGap();
1418        if (br > 0 && br2 > 0) {
1419            br = 1.0 / br;
1420            br2 = 1.0 / br2;
1421            double rad = fj_rmul()*rmin();
1422            DVect bp = convert_getcast<IPieceMechanical>(mc->getEnd1())->getPosition();
1423            DVect axis = orientProps_->orient1_.rotate(orientProps_->origNormal_);
1424            bot->push_back(axis.unit()*(br-0.5*(gap().x()- cgap))+bp);
1425            top->push_back(bp);
1426            radlow->push_back(rad);
1427            radhi->push_back(0.0);
1428            val->push_back(mc->getEnd1()->getIThing()->getID());
1429            bp = convert_getcast<IPieceMechanical>(mc->getEnd2())->getPosition();
1430            axis = orientProps_->orient2_.rotate(orientProps_->origNormal_);
1431            bot->push_back(axis.unit()*(br2-0.5*(gap().x()-cgap))*(-1.0)+bp);
1432            top->push_back(bp);
1433            radlow->push_back(rad);
1434            radhi->push_back(0.0);
1435            val->push_back(mc->getEnd2()->getIThing()->getID());
1436        }
1437    }
1438
1439    DVect ContactModelFlatJoint::getForce() const {
1440        DVect ret(fj_f_);
1441        return ret;
1442    }
1443
1444    DAVect ContactModelFlatJoint::getMomentOn1(const IContactMechanical *c) const {
1445        DAVect ret(fj_m_);
1446        if (c) {
1447            DVect force = getForce();
1448            c->updateResultingTorqueOn1Local(force,&ret);
1449        }
1450        return ret;
1451    }
1452
1453    DAVect ContactModelFlatJoint::getMomentOn2(const IContactMechanical *c) const {
1454        DAVect ret(fj_m_);
1455        if (c) {
1456            DVect force = getForce();
1457            c->updateResultingTorqueOn2Local(force,&ret);
1458        }
1459        return ret;
1460    }
1461    
1462    DAVect ContactModelFlatJoint::getModelMomentOn1() const {
1463        DAVect ret(fj_m_);
1464        return ret;
1465    }
1466    
1467    DAVect ContactModelFlatJoint::getModelMomentOn2() const {
1468        DAVect ret(fj_m_);
1469        return ret;
1470    }
1471    
1472    void ContactModelFlatJoint::objectPropsTypes(std::vector<std::pair<QString,InfoTypes>> *ret) const {
1473        ret->clear();
1474        ret->push_back({"fj_nr",ScalarInfo});
1475        ret->push_back({"fj_elem",ScalarInfo});
1476        ret->push_back({"fj_kn",ScalarInfo});
1477        ret->push_back({"fj_ks",ScalarInfo});
1478        ret->push_back({"fj_fric",ScalarInfo});
1479        ret->push_back({"fj_emod",ScalarInfo});
1480        ret->push_back({"fj_kratio",ScalarInfo});
1481        ret->push_back({"fj_rmul",ScalarInfo});
1482        ret->push_back({"fj_radius",ScalarInfo});
1483        ret->push_back({"fj_gap0",ScalarInfo});
1484        ret->push_back({"fj_ten",ScalarInfo});
1485        ret->push_back({"fj_coh",ScalarInfo});
1486        ret->push_back({"fj_fa",ScalarInfo});
1487        ret->push_back({"fj_force",VectorInfo});
1488        ret->push_back({"fj_moment",VectorInfo});
1489        ret->push_back({"fj_state",ScalarInfo});
1490        ret->push_back({"fj_slip",ScalarInfo});
1491        ret->push_back({"fj_mtype",ScalarInfo});
1492        ret->push_back({"fj_area",ScalarInfo});
1493        ret->push_back({"fj_egap",ScalarInfo});
1494        ret->push_back({"fj_gap",ScalarInfo});
1495        ret->push_back({"fj_sigma",ScalarInfo});
1496        ret->push_back({"fj_tau",ScalarInfo});
1497        ret->push_back({"fj_shear",ScalarInfo});
1498#ifdef THREED
1499        ret->push_back({"fj_nal",ScalarInfo});
1500#endif
1501        ret->push_back({"fj_relbr",VectorInfo});
1502        ret->push_back({"fj_cen",VectorInfo});
1503        ret->push_back({"fj_track",ScalarInfo});
1504        ret->push_back({"user_area",ScalarInfo});
1505        ret->push_back({"fj_cohres",ScalarInfo});
1506        ret->push_back({"fj_resmode",ScalarInfo});
1507        
1508    }
1509    
1510    void ContactModelFlatJoint::objectPropValues(std::vector<double> *ret,const IContact *con) const {
1511        ret->clear();
1512        ret->push_back(fj_nr());
1513        ret->push_back(fj_elem());
1514        ret->push_back(fj_kn());
1515        ret->push_back(fj_ks());
1516        ret->push_back(fj_fric());
1517        const IContactMechanical *c(convert_getcast<IContactMechanical>(con));
1518        double rsum(0.0);
1519        if (c->getEnd1Curvature().y())
1520            rsum += 1.0/c->getEnd1Curvature().y();
1521        if (c->getEnd2Curvature().y())
1522            rsum += 1.0/c->getEnd2Curvature().y();
1523        if (userArea_) {
1524#ifdef THREED
1525            rsum = std::sqrt(userArea_ / dPi);
1526#else
1527            rsum = userArea_ / 2.0;
1528#endif
1529            rsum += rsum;
1530        }
1531        ret->push_back(fj_kn_ * rsum);
1532        ret->push_back((fj_ks_ == 0.0 ) ? 0.0 : (fj_kn_/fj_ks_));
1533        ret->push_back(fj_rmul());
1534        ret->push_back(rbar());
1535        ret->push_back(fj_gap0());
1536        ret->push_back(fj_ten());
1537        ret->push_back(fj_coh());
1538        ret->push_back(fj_fa());
1539        ret->push_back(fj_f().mag());
1540        ret->push_back(fj_m().mag());
1541        ret->push_back(bmode_[fj_elem()-1]);
1542        ret->push_back(smode_[fj_elem()-1]);
1543        ret->push_back(getmType());
1544        ret->push_back(a_[fj_elem()-1]);
1545        ret->push_back(egap_[fj_elem()-1]);
1546        ret->push_back(gap().x());
1547        ret->push_back(-f_[fj_elem()-1].x() / a_[fj_elem()-1]);
1548        ret->push_back(f_[fj_elem()-1].y() / a_[fj_elem()-1]);
1549        ret->push_back(tauC((-f_[fj_elem()-1].x() / a_[fj_elem()-1]),(bmode_[fj_elem()-1]==3)));
1550#ifdef THREED
1551        ret->push_back(fj_na());
1552#endif
1553        ret->push_back(DVect2(theta(),thetaM()).mag());
1554        ret->push_back(getRelElemPos(con,fj_elem()-1).mag());
1555        ret->push_back(orientProps_ ? true : false);
1556        ret->push_back(userArea_);
1557        ret->push_back(fj_cohres());
1558        ret->push_back(fj_resmode());
1559    }
1560
1561} // namespace itascaxd
1562
1563// EoF

Top