File: HertzMindlinExtended.cpp

package info (click to toggle)
yade 2026.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 34,448 kB
  • sloc: cpp: 97,645; python: 52,173; sh: 677; makefile: 162
file content (341 lines) | stat: -rw-r--r-- 15,915 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/*
 2016 - Bettina Suhr 
 extension of Hertz-Mindlin model (see HertzMindlin.cpp): 

 normal direction: conical damage model
 tangential direction: stress dependent interparticle friction coefficient
 both models can be switched on/off separately

 references:
 Harkness, Zervos, Le Pen, Aingaran, Powrie: Discrete element simulation of railway ballast: modelling cell pressure effects in triaxial tests, Granular Matter, (2016) 18:65
 Suhr & Six 2017: Parametrisation of a DEM model for railway ballast under different load cases, Granular Matter, (2017) 19:64
 Suhr & Six 2016: On the effect of stress dependent interparticle friction in direct shear tests Powder Technology , (2016) 294:211 - 220
*/

#include "HertzMindlinExtended.hpp"
#include <lib/high-precision/Constants.hpp>
#include <core/Omega.hpp>
#include <core/Scene.hpp>
#include <pkg/dem/ScGeom.hpp>


namespace yade { // Cannot have #include directive inside.

YADE_PLUGIN((FrictMatCDM)(MindlinPhysCDM)(Ip2_FrictMatCDM_FrictMatCDM_MindlinPhysCDM)(Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM)(
        Law2_ScGeom_MindlinPhysCDM_HertzMindlinCDM));

CREATE_LOGGER(Ip2_FrictMatCDM_FrictMatCDM_MindlinPhysCDM);


void Ip2_FrictMatCDM_FrictMatCDM_MindlinPhysCDM::go(const shared_ptr<Material>& b1, const shared_ptr<Material>& b2, const shared_ptr<Interaction>& interaction)
{
	if (interaction->phys) return; // no updates of an already existing contact necessary

	interaction->phys                                = shared_ptr<MindlinPhysCDM>(new MindlinPhysCDM());
	const shared_ptr<MindlinPhysCDM>& contactPhysics = YADE_PTR_CAST<MindlinPhysCDM>(interaction->phys);

	const FrictMatCDM* mat1 = YADE_CAST<FrictMatCDM*>(b1.get());
	const FrictMatCDM* mat2 = YADE_CAST<FrictMatCDM*>(b2.get());

	/* from interaction physics */
	const Real Ea = mat1->young;
	const Real Eb = mat2->young;
	const Real Va = mat1->poisson;
	const Real Vb = mat2->poisson;
	const Real fa = mat1->frictionAngle;
	const Real fb = mat2->frictionAngle;


	/* from interaction geometry */
	const GenericSpheresContact* scg = YADE_CAST<GenericSpheresContact*>(interaction->geom.get());

	/* calculate stiffness coefficients */
	const Real Ga = Ea / (2 * (1 + Va));
	const Real Gb = Eb / (2 * (1 + Vb));
	//Real V = (Va+Vb)/2; // average of poisson's ratio
	const Real E = Ea * Eb / ((1. - math::pow(Va, 2)) * Eb + (1. - math::pow(Vb, 2)) * Ea); // equivalent Young's modulus

	//CHANGED use equivalent radius from geometry!!
	const Real Da = scg->refR1 > 0 ? scg->refR1 : scg->refR2;
	const Real Db = scg->refR2;
	const Real R  = Da * Db / (Da + Db); // equivalent radius

	const Real Kno = 4. / 3. * E * sqrt(R); // coefficient for normal stiffness
	//Real Kso = 2*sqrt(4*R)*G/(2-V); // coefficient for shear stiffness
	//CHANGED
	const Real Kso           = 8 * sqrt(R) / ((2 - Va) / Ga + (2 - Vb) / Gb); // coefficient for shear stiffness
	const Real frictionAngle = (!frictAngle) ? math::min(fa, fb) : (*frictAngle)(mat1->id, mat2->id, mat1->frictionAngle, mat2->frictionAngle);


	/* pass values calculated from above to MindlinPhys */
	//contactPhysics->prevNormal = scg->normal; // used to compute relative rotation
	contactPhysics->E   = E;                                     // equiv young modulus
	contactPhysics->G   = 1.0 / ((2 - Va) / Ga + (2 - Vb) / Gb); //equiv shear modulus
	contactPhysics->kno = Kno;                                   // this is just a coeff, will be changed
	contactPhysics->kso = Kso;                                   // this is just a coeff, will be changed
	//parameters for conical damage model
	contactPhysics->R        = R; //save current contact radius, will increase during yielding
	contactPhysics->radius   = R; //HERE Rmin is stored!! will not be changed during contact lifetime
	contactPhysics->sigmaMax = math::min(mat1->sigmaMax, mat2->sigmaMax);
	contactPhysics->alphaFac = (1.0 - math::sin(math::min(mat1->alpha, mat2->alpha))) / math::sin(math::min(mat1->alpha, mat2->alpha));
	//parameter for stress dependent interparticle friction coefficient
	contactPhysics->tangensOfFrictionAngle = math::tan(frictionAngle); //current value of friction coefficient is stored here, will change
	contactPhysics->mu0
	        = math::tan(frictionAngle); // parameter for stress dependent interparticle friction coefficient, will not change during contact lifetime
	contactPhysics->c1 = math::min(mat1->c1, mat2->c1);
	contactPhysics->c2 = math::min(mat1->c2, mat2->c2);


	if (math::min(mat1->alpha, mat2->alpha) <= 0 or math::min(mat1->alpha, mat2->alpha) >= Mathr::PI / 2.0)
		throw std::invalid_argument("Ip2_FrictMatCDM_FrictMatCDM_MindlinPhysCDM: alpha must in (0,pi/2) radians ,NOT equal to 0 or pi/2");
	if (contactPhysics->mu0 <= 0) throw std::invalid_argument("Ip2_FrictMatCDM_FrictMatCDM_MindlinPhysCDM: mu0/friction angle must be > 0");
	if (contactPhysics->sigmaMax <= 0) throw std::invalid_argument("Ip2_FrictMatCDM_FrictMatCDM_MindlinPhysCDM: sigmaMax must be > 0");
	if (contactPhysics->sigmaMax >= E) throw std::invalid_argument("Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM: sigmaMax must be < Young's modulus!");
	if (contactPhysics->c1 < 0) throw std::invalid_argument("Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM: c1 must be >=0!");
	if (contactPhysics->c2 < 0) throw std::invalid_argument("Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM: c2 must be >=0!");

	//damping currently cot used-----------------------------------------------------
	contactPhysics->betan = 0.0;
	contactPhysics->betas = 0.0;
	//adhesion, bending currently cot used-----------------------------------------------------
	contactPhysics->adhesionForce = 0; //Skipped at the moment! Adhesion;
	contactPhysics->kr            = 0; //Skipped at the moment! krot;
	contactPhysics->ktw           = 0; //Skipped at the moment! ktwist;
	contactPhysics->maxBendPl     = 0; //Skipped at the moment!
};

CREATE_LOGGER(Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM);

void Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM::go(const shared_ptr<Material>& b1, const shared_ptr<Material>& b2, const shared_ptr<Interaction>& interaction)
{
	if (interaction->phys) return; // no updates of an already existing contact necessary
	shared_ptr<MindlinPhysCDM> contactPhysics(new MindlinPhysCDM());
	interaction->phys = contactPhysics;
	const FrictMat*    matFrictMat;
	const FrictMatCDM* matFrictMatCDM;
	// check which interaction partner is of which material class
	// try to cast b1 to FrictMatCDM, returns null if b1 is FrictMat
	const FrictMatCDM* matFrictMatCDM1 = dynamic_cast<FrictMatCDM*>(b1.get());
	if (matFrictMatCDM1) {
		// b1 is FrictMatCDM, b2 is FrictMat
		matFrictMatCDM = YADE_CAST<FrictMatCDM*>(b1.get());
		matFrictMat    = YADE_CAST<FrictMat*>(b2.get());
	} else {
		// b1 is FrictMat, b2 is FrictMatCDM
		matFrictMatCDM = YADE_CAST<FrictMatCDM*>(b2.get());
		matFrictMat    = YADE_CAST<FrictMat*>(b1.get());
	}

	/* from interaction physics */
	const Real Ea = matFrictMat->young;
	const Real Eb = matFrictMatCDM->young;
	const Real Va = matFrictMat->poisson;
	const Real Vb = matFrictMatCDM->poisson;
	const Real fa = matFrictMat->frictionAngle;
	const Real fb = matFrictMatCDM->frictionAngle;


	/* from interaction geometry */
	const GenericSpheresContact* scg = YADE_CAST<GenericSpheresContact*>(interaction->geom.get());
	if (Va <= 0 or Vb <= 0) throw std::invalid_argument("Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM: Poisson's ratio must be > 0");

	/* calculate stiffness coefficients */
	const Real Ga = Ea / (2 * (1 + Va));
	const Real Gb = Eb / (2 * (1 + Vb));
	//Real V = (Va+Vb)/2; // average of poisson's ratio
	const Real E = Ea * Eb / ((1. - math::pow(Va, 2)) * Eb + (1. - math::pow(Vb, 2)) * Ea); //equivalent Young's modulus

	const Real Da = scg->refR1 > 0 ? scg->refR1 : scg->refR2;
	const Real Db = scg->refR2;
	const Real R  = Da * Db / (Da + Db); // equivalent radius

	const Real Kno           = 4. / 3. * E * sqrt(R);                         // coefficient for normal stiffness
	const Real Kso           = 8 * sqrt(R) / ((2 - Va) / Ga + (2 - Vb) / Gb); // coefficient for shear stiffness
	const Real frictionAngle = (!frictAngle)
	        ? math::min(fa, fb)
	        : (*frictAngle)(matFrictMat->id, matFrictMatCDM->id, matFrictMat->frictionAngle, matFrictMatCDM->frictionAngle);


	/* pass values calculated from above to MindlinPhys */
	contactPhysics->E   = E;                                     // equiv Young's modulus
	contactPhysics->G   = 1.0 / ((2 - Va) / Ga + (2 - Vb) / Gb); //equiv shear modulus
	contactPhysics->kno = Kno;                                   // this is just a coeff, will be changed
	contactPhysics->kso = Kso;                                   // this is just a coeff, will be changed
	//parameters for conical damage model
	contactPhysics->R        = R; //save current contact radius, will increase during yielding
	contactPhysics->radius   = R; //HERE Rmin is stored!! will not be changed during contact lifetime
	contactPhysics->sigmaMax = matFrictMatCDM->sigmaMax;
	contactPhysics->alphaFac = (1.0 - math::sin(matFrictMatCDM->alpha)) / math::sin(matFrictMatCDM->alpha);
	//parameter for stress dependent interparticle friction coefficient
	//friction coeff is ALWAYS constant!!
	contactPhysics->tangensOfFrictionAngle = math::tan(frictionAngle);
	contactPhysics->mu0                    = math::tan(frictionAngle);
	contactPhysics->c1                     = 0.0;
	contactPhysics->c2                     = 0.0;

	if (matFrictMatCDM->alpha <= 0 or matFrictMatCDM->alpha >= Mathr::PI / 2.0)
		throw std::invalid_argument("Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM: alpha must in (0,pi/2) radians ,NOT equal to 0 or pi/2");
	if (contactPhysics->mu0 <= 0) throw std::invalid_argument("Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM: mu0/frictionAngle must be > 0");
	if (contactPhysics->sigmaMax <= 0) throw std::invalid_argument("Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM: sigmaMax must be > 0");
	if (contactPhysics->sigmaMax >= E) throw std::invalid_argument("Ip2_FrictMat_FrictMatCDM_MindlinPhysCDM: sigmaMax must be < Young's modulus!");
	//damping currently cot used-----------------------------------------------------
	contactPhysics->betan = 0.0;
	contactPhysics->betas = 0.0;
	//adhesion, bending currently cot used-----------------------------------------------------
	contactPhysics->adhesionForce = 0; //Skipped at the moment! Adhesion;
	contactPhysics->kr            = 0; //Skipped at the moment! krot;
	contactPhysics->ktw           = 0; //Skipped at the moment! ktwist;
	contactPhysics->maxBendPl     = 0; //Skipped at the moment!
};


/* Function which returns the ratio between the number of sliding contacts to the total number at a given time */
Real Law2_ScGeom_MindlinPhysCDM_HertzMindlinCDM::ratioSlidingContacts()
{
	Real ratio(0);
	int  count(0);
	FOREACH(const shared_ptr<Interaction>& I, *scene->interactions)
	{
		if (!I->isReal()) continue;
		const MindlinPhysCDM* phys = dynamic_cast<MindlinPhysCDM*>(I->phys.get());
		if (phys->isSliding) { ratio += 1; }
		count++;
	}
	ratio /= count;
	return ratio;
}

/* Function which returns the ratio between the number of yielding contacts to the total number at a given time */
Real Law2_ScGeom_MindlinPhysCDM_HertzMindlinCDM::ratioYieldingContacts()
{
	Real ratio(0);
	int  count(0);
	FOREACH(const shared_ptr<Interaction>& I, *scene->interactions)
	{
		if (!I->isReal()) continue;
		const MindlinPhysCDM* phys = dynamic_cast<MindlinPhysCDM*>(I->phys.get());
		if (phys->isYielding) { ratio += 1; }
		count++;
	}
	ratio /= count;
	return ratio;
}

/******************** Law2_ScGeom_MindlinPhysCDM_HertzMindlinCDM *********/
CREATE_LOGGER(Law2_ScGeom_MindlinPhysCDM_HertzMindlinCDM);

bool Law2_ScGeom_MindlinPhysCDM_HertzMindlinCDM::go(shared_ptr<IGeom>& ig, shared_ptr<IPhys>& ip, Interaction* contact)
{
	//const Body::id_t id1 = contact->getId1(); // get id body 1
	//const Body::id_t id2 = contact->getId2(); // get id body 2
	const auto id1 = contact->getId1();
	const auto id2 = contact->getId2();

	const State* de1 = Body::byId(id1, scene)->state.get();
	const State* de2 = Body::byId(id2, scene)->state.get();

	const ScGeom*   scg  = static_cast<ScGeom*>(ig.get());
	MindlinPhysCDM* phys = static_cast<MindlinPhysCDM*>(ip.get());

	/****************/
	/* NORMAL FORCE */
	/****************/
	const Real uDEM = scg->penetrationDepth; // get DEM overlap
	if (uDEM < 0) {
		if (neverErase) {
			phys->shearForce = phys->normalForce = Vector3r::Zero();
			phys->kn = phys->ks = 0;
			return true;
		} else
			return false;
	}


	// Hertz-Mindlin's formulation + conical damage model
	//conical damage model: Harkness et al. 2016 (see header for full reference)
	//modification in Suhr&Six 2017 (see header for full reference)
	//DIFFERENT INTERPRETATION OF OVERLAP DEFINITION
	//phys->radius: R_eq as in Hertz law
	//phys->R: current contact radius
	Real uN = uDEM + (phys->radius - phys->R) * phys->alphaFac; //elastic overlap
	if (uN < 0) {
		phys->shearForce = phys->normalForce = Vector3r::Zero();
		phys->kn = phys->ks = 0;
		return true;
	}
	//reformulated formula
	phys->isYielding = false;
	//check yield condition
	if (2.0 * phys->E / Mathr::PI * math::pow(uN / phys->R, 0.5) > phys->sigmaMax) {
		phys->isYielding = true;
		Real hfac        = math::pow(Mathr::PI * phys->sigmaMax / 2.0 / phys->E, 2.0);
		phys->R          = (uDEM + phys->radius * phys->alphaFac) / (hfac + phys->alphaFac);
		uN               = uDEM + (phys->radius - phys->R) * phys->alphaFac; //adapted elastic overlap
	}

	// here we store the value of kn to compute the time step
	phys->kn          = 4. / 3.0 * phys->E * math::pow(phys->R * uN, 0.5);
	Real Fn           = phys->kn * uN;    // normal Force (scalar)
	phys->normalForce = Fn * scg->normal; // normal Force (vector)

	/***************/
	/* SHEAR FORCE */
	/***************/

	phys->ks = 8.0 * phys->G * math::pow(phys->R * uN, 0.5); //adapted tangential stiffness
	// 1. Rotate shear force
	Vector3r& shearElastic = scg->rotate(phys->shearElastic);
	// 2. Get shear force (incrementally)
	const Vector3r& shearDisp = scg->shearIncrement();
	shearElastic              = shearElastic - phys->ks * (shearDisp);


	/********************/
	/* MOHR-COULOMB law */
	/********************/
	phys->isSliding    = false;
	phys->shearViscous = Vector3r::Zero(); // reset so that during sliding, the previous values is not there
	                                       // NO ADHESION
	//-------Suhr&Six16---------------------------------------------------------
	//change mu to be pressure dependent!
	Real pm;
	if (Fn > 0.0) {
		//divide force with contact area (circular), radius: sqrt(phys->R*uN)
		pm = Fn / (phys->R * uN * M_PI); //mean pressure of contact
	} else {
		pm = 0.0;
	}
	Real mu                      = phys->mu0 + phys->c1 / (1.0 + phys->c2 * pm);
	phys->tangensOfFrictionAngle = mu;

	Real maxFs = Fn * phys->tangensOfFrictionAngle;

	if (shearElastic.squaredNorm() > maxFs * maxFs) {
		phys->isSliding = true;
		Real ratio      = maxFs / shearElastic.norm();
		shearElastic *= ratio;
		phys->shearForce = shearElastic; /*store only elastic shear displacement*/
	} else {
		phys->shearForce = shearElastic;
	} // update the shear force at the elastic value if no damping is present and if we passed MC

	/****************/
	/* APPLY FORCES */
	/****************/

	if (!scene->isPeriodic)
		applyForceAtContactPoint(-phys->normalForce - phys->shearForce, scg->contactPoint, id1, de1->se3.position, id2, de2->se3.position);
	else { // in scg we do not wrap particles positions, hence "applyForceAtContactPoint" cannot be used
		Vector3r force = -phys->normalForce - phys->shearForce;
		scene->forces.addForce(id1, force);
		scene->forces.addForce(id2, -force);
		scene->forces.addTorque(id1, (scg->radius1 - 0.5 * scg->penetrationDepth) * scg->normal.cross(force));
		scene->forces.addTorque(id2, (scg->radius2 - 0.5 * scg->penetrationDepth) * scg->normal.cross(force));
	}


	return true;
}


} // namespace yade