How to simulate small-strain triaxial compression test

Asked by Leonard

Hi,

I'd like to ask that how to simulate a drained triaxial compression test within a small-strain level.

The goal of this simulation is to get the small strain stiffness G0.

A typical way is (1) generating a sand packing and isotropically load it to target confining pressure, e.g., 100 kPa. (2) a small axial strain increment deps1 is applied on the top wall, while the lateral stresses sigma2 and sigma3 keep constant. (3) the simulation finishes when the shear strain, gamma = deps1-deps2, reaches 10e-6. (4) then G0 can be calculated by G0 = dsigma1/(2*gamma)

What I have done is using the example code provided by Bruno[1]. I set the loading rate to a very small value (1e-20), and the simulations finished in one second. I thought this process is too quick to generate accurate results, actually the lateral confining pressure changes (i.e., not constant). I know this script works well for a classic triaxial compression test because usually we look at a large strain level (like 20% of axial strain), thereby a small amout of fluctuation on lateral stress is acceptable. But when we look at such a small range and in such a short time, this will lead to inaccurate results. So my question is how can we make the simulation of triaxial compression test within a small-strain level.

Thanks,
Leonard

[1]https://gitlab.com/yade-dev/trunk/blob/master/examples/triax-tutorial/script-session1.py

The MWE is as follow if the question is not clearly described.

from yade import pack

nRead=readParamsFromTable(
 num_spheres=1000,# number of spheres
 compFricDegree = 30, # contact friction during the confining phase
 key='_triax_base_', # put you simulation's name here
 unknownOk=True
)
from yade.params import table

num_spheres=table.num_spheres# number of spheres
key=table.key
targetPorosity = 0.43 #the porosity we want for the packing
compFricDegree = table.compFricDegree # initial contact friction during the confining phase (will be decreased during the REFD compaction process)
finalFricDegree = 30 # contact friction during the deviatoric loading
rate=-1e-20 # loading rate (strain rate)
damp=0.2 # damping coefficient
stabilityThreshold=0.01 # we test unbalancedForce against this value in different loops (see below)
young=5e6 # contact stiffness
mn,mx=Vector3(0,0,0),Vector3(1,1,1) # corners of the initial packing

O.materials.append(FrictMat(young=young,poisson=0.5,frictionAngle=radians(compFricDegree),density=2600,label='spheres'))
O.materials.append(FrictMat(young=young,poisson=0.5,frictionAngle=0,density=0,label='walls'))

walls=aabbWalls([mn,mx],thickness=0,material='walls')
wallIds=O.bodies.append(walls)

sp=pack.SpherePack()

sp.makeCloud(mn,mx,-1,0.3333,num_spheres,False, 0.95,seed=1) #"seed" make the "random" generation always the same
O.bodies.append([sphere(center,rad,material='spheres') for center,rad in sp])

triax=TriaxialStressController(
 maxMultiplier=1.+2e4/young, # spheres growing factor (fast growth)
 finalMaxMultiplier=1.+2e3/young, # spheres growing factor (slow growth)
 thickness = 0,

 stressMask = 7,
 internalCompaction=True, # If true the confining pressure is generated by growing particles
)

newton=NewtonIntegrator(damping=damp)

O.engines=[
 ForceResetter(),
 InsertionSortCollider([Bo1_Sphere_Aabb(),Bo1_Box_Aabb()]),
 InteractionLoop(
  [Ig2_Sphere_Sphere_ScGeom(),Ig2_Box_Sphere_ScGeom()],
  [Ip2_FrictMat_FrictMat_FrictPhys()],
  [Law2_ScGeom_FrictPhys_CundallStrack()]
 ),
 GlobalStiffnessTimeStepper(active=1,timeStepUpdateInterval=100,timestepSafetyCoefficient=0.8),
 triax,
 TriaxialStateRecorder(iterPeriod=100,file='WallStresses'+table.key),
 newton,
]

Gl1_Sphere.stripes=0
if nRead==0: yade.qt.Controller(), yade.qt.View()

triax.goal1=triax.goal2=triax.goal3=-100000

while 1:
  O.run(1000, True)
  unb=unbalancedForce()
  print 'unbalanced force:',unb,' mean stress: ',triax.meanStress
  if unb<stabilityThreshold and abs(-100000-triax.meanStress)/100000<0.001:
    break

print "### Isotropic state saved ###"

import sys #this is only for the flush() below
while triax.porosity>targetPorosity:
 # we decrease friction value and apply it to all the bodies and contacts
 compFricDegree = 0.95*compFricDegree
 setContactFriction(radians(compFricDegree))
 print "\r Friction: ",compFricDegree," porosity:",triax.porosity,
 sys.stdout.flush()
 O.run(500,1)

O.save('compactedState'+key+'.yade.gz')
print "### Compacted state saved ###"

triax.internalCompaction=False

setContactFriction(radians(finalFricDegree))

triax.stressMask = 5
triax.goal2=rate
triax.goal1=-100000
triax.goal3=-100000
newton.damping=0.1

print "gamma before deviatoric loading is", abs(triax.strain[1]-triax.strain[0])
print "click run to start small-strain deviatoric loading"
from yade import plot

def history():
 plot.addData(e11=-triax.strain[0], e22=-triax.strain[1], e33=-triax.strain[2],
   ev=-triax.strain[0]-triax.strain[1]-triax.strain[2],
   s11=-triax.stress(triax.wall_right_id)[0],
   s22=-triax.stress(triax.wall_top_id)[1],
   s33=-triax.stress(triax.wall_front_id)[2],
   i=O.iter)

def stop():
 if abs(triax.strain[1]-triax.strain[0])>1e-6:
  O.pause()
  print "gamma after deviatoric loading is", abs(triax.strain[1]-triax.strain[0])

O.engines=O.engines+[PyRunner(iterPeriod=20,command='history()',label='recorder')]
O.engines=O.engines+[PyRunner(iterPeriod=1,command='stop()',label='stop')]

### declare what is to plot. "None" is for separating y and y2 axis
# plot.plots={'i':('e11','e22','e33',None,'s11','s22','s33')}
## the traditional triaxial curves would be more like this:
#plot.plots={'e22':('s11','s22','s33',None,'ev')}

# display on the screen (doesn't work on VMware image it seems)
# plot.plot()

Question information

Language:
English Edit question
Status:
Solved
For:
Yade Edit question
Assignee:
No assignee Edit question
Solved by:
Jérôme Duriez
Solved:
Last query:
Last reply:
Revision history for this message
Leonard (z2521899293) said :
#1

Hi,

I came up with a way: using a small enough dt for the simulation.

Do you have any other ideas?

Cheers
Leonard

Revision history for this message
Best Jérôme Duriez (jduriez) said :
#2

Hi,

Decrease TriaxialStressController.max_vel ?

Which is the first responsible for lateral stress fluctuations (or absence of)

Revision history for this message
Leonard (z2521899293) said :
#3

Thanks Jérôme Duriez, that solved my question.

Revision history for this message
Bruno Chareyre (bruno-chareyre) said :
#4

Transitioning between phases is often based on the ubalanced force (see "stabilityThreshold=0.01").
That's basically a residual amount of dynamic noise we accept.
If you want a very clean equilibrium state before imposing a strain increment you can aim at an even smaller value.

I'm surprised decreasing max_vel alone is a sufficient answer.

Bruno

Revision history for this message
William (qfxx-123) said :
#5

Hi, Leonard
I have read your code and the conversation. There are some questions still trouble me. I hope you or some other friends could help me.

> young=5e6 # contact stiffness
Many researchers adopt young=29e9 in their papers. However, the unbalanced force is nan and the mean stress is 0 all the time whlie I modfy this using your code.

> Decrease TriaxialStressController.max_vel
How much should TriaxialStressController.max_vel go down to?

Thanks,
William

Revision history for this message
Jan Stránský (honzik) said :
#6

Hello,

please open a new question for a new problem [2], try not to continue in long-closed questions.
You can reference this conversation in the new question.

Quick response (to be continued in the new question :-)

>> young=5e6 # contact stiffness
> Many researchers adopt young=29e9 in their papers.

"Many researchers" is vague, please reference specific paper(s).
The stiffness values depends on actual material.
Also, Yade just counts numbers, without units, so 5e6 in one script and 29e9 in another can theoretically be the same physical property.

> However, the unbalanced force is nan and the mean stress is 0 all the time whlie I modfy this using your code.

both symptoms suggest zero number of interactions.
Please check it.

>> Decrease TriaxialStressController.max_vel
> How much should TriaxialStressController.max_vel go down to?

It would pretty much depend on your needs and requirements, I doubt there is one universal value..

Cheers
Jan

[2] https://www.yade-dem.org/wiki/Howtoask

Revision history for this message
Leonard (z2521899293) said :
#7

Hi William,

>However, the unbalanced force is nan and the mean stress is 0 all the time whlie I modfy this using your code.

The reason I think is that: you modify only the young from 5e6 to 29e9, while in

triax=TriaxialStressController(
 maxMultiplier=1.+2e4/young, # spheres growing factor (fast growth)
 finalMaxMultiplier=1.+2e3/young, # spheres growing factor (slow growth)
 thickness = 0,

 stressMask = 7,
 internalCompaction=True, # If true the confining pressure is generated by growing particles
)

The radii expansion multiplier is associated with the young value in my MWE, which means you set it very small when you change young very high. Actually, the particles are growing but very slowly, so that you didn't observe any contacts between particles and hence there is no unb and mean stress. If you wait enough time (could be very long), you may observe the unb and mean stress increase.

You may play with the parameters in the TriaxialStressController, for example, using a fixed value of maxMultiplier.

Cheers
Leonard