← Back to team overview

yade-users team mailing list archive

[Question #706821]: Particle breakage of clump using JCFpm

 

New question #706821 on Yade:
https://answers.launchpad.net/yade/+question/706821

Hi! I want to model the particle breakage of clumps during the one-dimensional compression in a cylindrical boundary. My steps are as followed:
Step 1:  I  import 800 GTS files into Yade and generated one-on-one 800 clumps. That is to say that each GTS corresponds to a unique clump. 
Step 2:  I create a cylindrical boundary to cover those clumps and provide two plates (bottom one fixed, top one applied with force). Note that the force applied on the top plate is a multi-load, which varies with the strain of the sample.
Step 3:  I assign JCFpm material for each clump and material of steel for the boundary and plates.
Step 4:  record what we want to know and implement simulation.

Here, my questions are as follows:
1) Is my whole procedure appropriate to simulate the one-dimensional compression while considering particle breakage?
2) For Step 2, I would like to know how to assign multi-loads is correct. It seems currently the top plate is not quasi-static during loading. Do I need to clear the speed for every number of calculation steps?
3) For the whole simulation, how do we know how many clumps are broken and can we record the isolated clumps at the final? 

The MWM is presented below (I don't know how to provide the gts files without an external link, so maybe randomly generating some clumps can replace them.):

from __future__ import print_function
from yade import pack, ymport
import gts, os.path, locale, plot, random
import numpy as np

locale.setlocale(
        locale.LC_ALL, 'en_US.UTF-8'
)  #gts is locale-dependend.  If, for example, german locale is used, gts.read()-function does not import floats normally

############################################
###   DEFINING VARIABLES AND MATERIALS   ###
############################################

# The following lines are used parameter definitions
readParamsFromTable(
	# directory	
	dir_gts = '/home/kyle/Yade/install/bin/gts', # directory of storing gts files
	dir_vtk = '/home/kyle/Yade/install/bin/vtk', # directory of storing vtk files
	dir_txt = '/home/kyle/Yade/install/bin/txt', # directory of storing txt files
	fileType = '.gts', # type of file 

        # type of generating spheres ['hexa','dense']
        genType = 'hexa',
        
        # material parameters
        young_w = 210e9, # Young's modulus of plates and walls
        young_g = 180e6, # Young's modulus of grains [1]
        den_w = 7874, # density of plates and walls
        den_g = 1162, # density of grains
        poi_w = 0.3, # possion's ratio of plates and walls
        poi_g = 0.25, # possion's ratio of grains
        friAngle_w = 15, # friction angle of plates and walls 
        friAngle_g = 19, # friction angle of grains

        # Parameters of cylindrical walls
        x_cyl = 0.0106, # x-coordinate of center of cylindrical walls
        y_cyl = 0.01005, # y-coordinate of center of cylindrical walls
        z_cyl = 0, # z-coordinate of center of cylindrical walls
        r_cyl = 0.0097, # radius of the cylinder
        h_cyl = 0.04151, # height of the cylinder
        n_cyl = 100, # number of boxes forming cylinder
        t_cyl = 0.0001, # thickness of the boxes
        
        #Parameters of lower and upper plates (square box)
        w_plate = 0.015, # width of plates
        t_plate = 0.0001, # thickness of plates

        # applied stress
        stress = [12500, 25000, 50000, 100000, 200000, 300000, 400000, 800000, 1600000, 3200000], # target applied stress
        
        # target displacement 
        displacement = [3.6e-5, 6.8e-5, 0.00014, 0.00028, 0.00047, 0.000642, 0.000796, 0.001342, 0.002441, 0.003989] # target displacement
        
)
from yade.params.table import *

# create materials for spheres and walls
wallMat = O.materials.append(FrictMat(young = young_w, poisson = poi_w, frictionAngle = friAngle_w, density = den_w, label = 'walls'))
#sphereMat = O.materials.append(FrictMat(young = young_g, poisson = poi_g, frictionAngle = friAngle_g, density = den_g, label = 'spheres'))

def sphereMat():
	return JCFpmMat(
	        type=1,
	        young = young_g,
	        frictionAngle = radians(friAngle_g),
	        density = den_g,
	        poisson = poi_g,
	        tensileStrength = 1e6,
	        cohesion=1e6,
	        jointNormalStiffness=1e7,
	        jointShearStiffness=1e7,
	        jointCohesion=1e6,
	        jointFrictionAngle=radians(20),
	        jointDilationAngle=radians(0)
	)  ## Rq: density needs to be adapted as porosity of real rock is different to granular assembly due to difference in porosity (utils.sumForces(baseBodies,(0,1,0))/(Z*X) should be equal to Gamma*g*h with h=Y, g=9.82 and Gamma=2700 kg/m3

###############################################
###   IMPORTING GRAINS AND CREATING CLUMPS  ###
###############################################

##### a function that searching all .gts files
def SearchFiles(directory, ftype):
	fileList = []
	for root, subDirs, files in os.walk(directory):
		for fileName in files:
			if fileName.endswith(ftype):
				fileList.append(os.path.join(root, fileName))
	return fileList
##### 

# import gts and obatin file list
fileList = SearchFiles(dir_gts,fileType)

##### a function that determining the radius of spheres
def createSphere(pred, ndivmin, spacing, genType):
	#dim = pred.dim()
	#minDim = min(dim[0], dim[1], dim[2])
	aabb = pred.aabb()
	dim0 = aabb[1][0] - aabb[0][0]
	bodyList = []
	ndiv = ndivmin
	if genType == 'hexa':
		while len(bodyList)==0:
			radius = dim0 / ndiv # get some characteristic dimension, use it for radius
			bodyList = O.bodies.append(pack.regularHexa(pred, radius = radius, gap = -radius/4, color = [random.random(),random.random(),random.random()], material = sphereMat))
			ndiv = ndiv + spacing
	else:
		while len(bodyList)==0:
			radius = dim0 / ndiv # get some characteristic dimension, use it for radius
			sp = pack.randomDensePack(pred,radius=radius,returnSpherePack=True, color = [random.random(),random.random(),random.random()], material = sphereMat)
			bodyList = sp.toSimulation()
			ndiv = ndiv + spacing
	return bodyList
#####

# import gts and create clump for each .gts file
sphereList = []
for gtsName in fileList:
	surf = gts.read(open(gtsName))
	if surf.is_closed():
		print(gtsName)
		pred = pack.inGtsSurface(surf)
		bodyList = createSphere(pred, 20, 2, genType)
		idClump = O.bodies.clump(bodyList)
	del surf
	del pred
	del bodyList
O.bodies.updateClumpProperties() 
print('Number of bodies:', len(O.bodies))

###################################################################
###   CREATING CYLINDRICAL WALLS, BOTTOM PLATE, AND TOP PLATE   ###
###################################################################

##### a function that creating cylindrical walls
def cylSurf(center,radius,height,nSegments=12,thick=0,**kw):
   """creates cylinder made of boxes. Axis is parallel with z+
   center ... center of bottom disc
   radius ... radius of cylinder
   height ... height of cylinder
   nSegments ... number of boxes forming cylinder
   thick ... thickness of the boxes
   **kw ... keywords passed to box function (e.g. material, color ...)
   """
   # just converts (a,b,c) to Vector3(a,b,c) to enable "center + ..."
   center = Vector3(center)
   # nSegments angles to define individual boxes, from 0 to 2*pi
   angles = [i*2*pi/nSegments for i in range(nSegments)]
   # centers of boxes. Vector 3(radius,0,0) rotated by angle 'a'
   # z coordinate is .5*height
   # center + shift = center of corresponding box
   pts = [center + Vector3(radius*cos(a),radius*sin(a),.5*height) for a in angles] # centers of plates
   ret = []
   for i,pt in enumerate(pts): # for each box center create corresponding box
      # "length" of the box along circumference, cylinder circumference / nSegments
      l = pi*radius/nSegments
      # extents, half dimensions along x,y,z axis
      es = (.5*thick,l,.5*height)
      # by default box is axis-aligned, we need to rotate it along z axis (0,0,1) by angle i*2*pi/nSegments
      ori = Quaternion((0,0,1),i*2*pi/nSegments)
      # have a look at utils.box documentation. pt=center of box, es=extents (half dimension), ori=orientation
      ret.append(box(pt,es,ori,**kw))
   return ret
#####

# create cylindrical walls and fix them
surf = cylSurf([x_cyl, y_cyl, z_cyl], r_cyl, h_cyl, nSegments = n_cyl, thick = t_cyl, material = wallMat, wire=True)
O.bodies.append(surf)
for b in surf: b.state.blockedDOFs = 'xyzXYZ'
for b in surf: b.state.vel = (0, 0, 0)

# create the bottom plate and fix it
bottom_plate = O.bodies.append(box([x_cyl, y_cyl, z_cyl], (w_plate, w_plate, t_plate), material = wallMat))
O.bodies[bottom_plate].state.vel = (0, 0, 0)
O.bodies[bottom_plate].state.blockedDOFs = 'xyzXYZ'

# create the top plate 
top_plate=O.bodies.append(box([x_cyl, y_cyl, h_cyl], (w_plate, w_plate, t_plate), material = wallMat))

# apply initial force
force = np.dot(stress, w_plate * w_plate) # corresponding applied force
global deltaF
deltaF = force[0]
O.forces.setPermF(top_plate,(0, 0, -deltaF))

# target displacement
global numIter
numIter = 0
global maxIter
maxIter = 9
global dispT
dispT = displacement[numIter]
global disp
disp = 0
global dispE
dispE = displacement[-1]

# calculate initial void ratio
vol0 = h_cyl * pi * r_cyl * r_cyl
volG0 = sum([b.state.mass for b in O.bodies if b.isClump])/den_g
volV0 = vol0 - volG0
e0 = volV0/volG0
print(volG0, e0)
print('numIter', 'dispT', 'disp', 'disp/dispT', 'velTopPlate')

############################
###   DEFINING ENGINES   ###
############################

O.engines = [
	ForceResetter(),
	InsertionSortCollider([Bo1_Sphere_Aabb(aabbEnlargeFactor=1), 
				Bo1_Box_Aabb()]),
	InteractionLoop(
                [Ig2_Sphere_Sphere_ScGeom(interactionDetectionFactor=1), 
                 Ig2_Box_Sphere_ScGeom()],
                [Ip2_FrictMat_FrictMat_FrictPhys(), 
                 Ip2_JCFpmMat_JCFpmMat_JCFpmPhys(cohesiveTresholdIteration=1)],
                [Law2_ScGeom_FrictPhys_CundallStrack(), 
                 Law2_ScGeom_JCFpmPhys_JointedCohesiveFrictionalPM(smoothJoint=True)]),
        	GlobalStiffnessTimeStepper(timestepSafetyCoefficient=0.8),
	PyRunner(iterPeriod = 10,command = "checkDisplacement(displacement, force, h_cyl)"),
	VTKRecorder(iterPeriod = 500, recorders = ['jcfpm', 'cracks', 'spheres', 'boxes', 'stress', 'id', 'clumpId', 'coordNumber', 'colors'], fileName = dir_vtk + '/'),
	NewtonIntegrator(gravity = [0, 0, -9.81], damping = 0.4)
]
O.dt= 0.1 * PWaveTimeStep()
		
# check whether the target displacement has already met. if so, update applied stress
def checkDisplacement(displacement, force, h_cyl):
	global numIter
	global maxIter
	global disp
	global dispT
	global dispE
	global deltaF
	# at the very start, applied stress is the first of value of array 'stress'
	t = O.time
	H = O.bodies[top_plate].state.pos[2]
	disp = h_cyl - H
	velTopPlate = O.bodies[top_plate].state.vel[2]
	if disp > dispT:
		if numIter < maxIter:
			numIter = numIter + 1
			dispT = displacement[numIter]
			#deltaF = force[numIter] - force[numIter - 1]
			O.forces.setPermF(top_plate,(0, 0, -force[numIter]))	
		else:
			print(numIter, dispT, disp, disp/dispT, velTopPlate, O.forces.permF(top_plate)[2], O.forces.f(top_plate)[2])
			f = open(dir_txt + '/data.txt', 'a')
			f.write('{} {} {} {} {} {} {}\n'.format(O.iter, numIter, dispT, disp, velTopPlate, O.forces.permF(top_plate)[2], H))
			f.close()
			plot.saveDataTxt(O.tags['d.id'] + '.txt')
			O.pause()
	print(numIter, dispT, disp, disp/dispT, velTopPlate, O.forces.permF(top_plate)[2], O.forces.f(top_plate)[2])
	plot.addData(t=t,disp=disp)
	f = open(dir_txt + '/data.txt', 'a')
	f.write('{} {} {} {} {} {} {}\n'.format(O.iter, numIter, dispT, disp, velTopPlate, O.forces.permF(top_plate)[2], H))
	f.close()

##########################
###   RUN SIMULATION   ###
##########################
plot.plots = {'t':('disp')}
plot.plot()	
O.run()


-- 
You received this question notification because your team yade-users is
an answer contact for Yade.