Is it possible to transform sphere to polyhedron particles?

Asked by Huan

Hi,
I am try to modify my marshall mix design code that involves gravity deposition and oedometric test from using spheres to polyhedral shape materials. I couldn't find out anything about polyhedral, so I want to know if it is possible to change spheres for every sieve size to be polyhedron with different shape and size for each radius group. For example, I want to create polyhedron in radius1 as for x,y,z range from x(max, min)=(0.0347,0.0314), y(max, min)=(0.0202,0.0159), and z(max, min)=(0.01655,0.0139) and polyhedron faces range from 5-11. Is it possible?

##The following are my code that I'm currently working with##
import random
import math
from yade import geom, pack, utils, plot, ymport
import pandas as pd

# Define material properties
youngModulus = 1e7
poissonRatio = 0.25
density = 2000

# Create material
material = O.materials.append(FrictMat(young=youngModulus, poisson=poissonRatio, density=density))

# Define cylinder with funnel parameters
center = (0, 0, 0)
diameter = 0.102
height = 0.18

# create cylindrical body with radius 0.102 m and height 0.064 m
cylinder = geom.facetCylinder(center=center, radius=diameter/2, height=height, segmentsNumber=80, wallMask=6)

# assign material to each body in the cylinder
for body in cylinder:
    body.bodyMat = material

# add cylinder to simulation
O.bodies.append(cylinder)

# Define cylinder with funnel parameters
center1 = (0,0,height/2)
dBunker = 0.4
dOutput = 0.102
hBunker = 0
hOutput = 0.15
hPipe = 0

# create funnel as a bunker with diameter 0.102 m, height 0.064 m
funnel = geom.facetBunker(center=center1, dBunker=dBunker, dOutput=dOutput, hBunker=hBunker,hOutput=hOutput, hPipe=hPipe, segmentsNumber=80, wallMask=4)

# assign material to each body in the funnel
for body in funnel:
    body.bodyMat = material

# add funnel to simulation
O.bodies.append(funnel)

# define sphere parameters and number of spheres
rMean1 = (0.0125+0.019)/4
rRelFuzz1 = (0.019-0.0125)/4/rMean1
num1 = 17
rMean2 = (0.0095+0.0125)/4
rRelFuzz2 = (0.0125-0.0095)/4/rMean2
num2 = 45
rMean3 = (0.00475+0.0095)/4
rRelFuzz3 = (0.0095-0.00475)/4/rMean3
num3 = 1267
rMean4 = (0.00236+0.00475)/4
rRelFuzz4 = (0.00475-0.00236)/4/rMean4
num4 = 11624

#create empty sphere packing
sp = pack.SpherePack()

# generate randomly sphere
sp.makeCloud((-dBunker/4,-dBunker/4,1.3*height),(dBunker/4,dBunker/4,2*height), rMean = rMean1, rRelFuzz = rRelFuzz1, num = num1)
sp.makeCloud((-dBunker/4,-dBunker/4,1.3*height),(dBunker/4,dBunker/4,2*height), rMean = rMean2, rRelFuzz = rRelFuzz2, num = num2)
sp.makeCloud((-dBunker/4,-dBunker/4,1.3*height),(dBunker/4,dBunker/4,2*height), rMean = rMean3, rRelFuzz = rRelFuzz3, num = num3)
sp.makeCloud((-dBunker/4,-dBunker/4,1.3*height),(dBunker/4,dBunker/4,2*height), rMean = rMean4, rRelFuzz = rRelFuzz4, num = num4)

# add the sphere pack to the simulation
sp.toSimulation(material = material)

for body in O.bodies:
   if not isinstance(body.shape, Sphere):
       continue
   if body.shape.radius >= rMean1 :
       body.shape.color = (0,0,1) #blue
   if body.shape.radius <= rMean1 and body.shape.radius > rMean2:
       body.shape.color = (0,0,1) #blue
   if body.shape.radius <= rMean2 and body.shape.radius > rMean3:
       body.shape.color = (1,0,0) #red
   if body.shape.radius <= rMean3 and body.shape.radius > rMean4:
       body.shape.color = (0,1,0) #green
   if body.shape.radius <= rMean4 :
       body.shape.color = (1,1,0) #yellow

O.engines = [
        ForceResetter(),
        # sphere, facet, wall
        InsertionSortCollider([Bo1_Sphere_Aabb(), Bo1_Facet_Aabb(), Bo1_Wall_Aabb()]),
        InteractionLoop(
                # the loading plate is a wall, we need to handle sphere+sphere, sphere+facet, sphere+wall
                [Ig2_Sphere_Sphere_ScGeom(), Ig2_Facet_Sphere_ScGeom(), Ig2_Wall_Sphere_ScGeom()],
                [Ip2_FrictMat_FrictMat_FrictPhys()],
                [Law2_ScGeom_FrictPhys_CundallStrack()]
        ),
        NewtonIntegrator(gravity=(0, 0, -1000), damping=0.3),
        # the label creates an automatic variable referring to this engine
        # we use it below to change its attributes from the functions called
        PyRunner(command='checkUnbalanced()', realPeriod=2, label='checker'),
]
O.dt = PWaveTimeStep()

# enable energy tracking; any simulation parts supporting it
# can create and update arbitrary energy types, which can be
# accessed as O.energy['energyName'] subsequently
O.trackEnergy = True

# the following checkUnbalanced, unloadPlate and stopUnloading functions are all called by the 'checker'
# (the last engine) one after another; this sequence defines progression of different stages of the
# simulation, as each of the functions, when the condition is satisfied, updates 'checker' to call
# the next function when it is run from within the simulation next time
# check whether the gravity deposition has already finished
# if so, add wall on the top of the packing and start the oedometric test
def checkUnbalanced():
 # at the very start, unbalanced force can be low as there is only few contacts, but it does not mean the packing is stable
 if O.iter < 15000:
  return
 # add plate at the position on the top of the packing
 # the maximum finds the z-coordinate of the top of the topmost particle
 O.bodies.append(wall(max([b.state.pos[2] + b.shape.radius for b in O.bodies if isinstance(b.shape, Sphere)]), axis=2, sense=-1))
 global plate # without this line, the plate variable would only exist inside this function
 plate = O.bodies[-1] # the last particles is the plate
 # Wall objects are "fixed" by default, i.e. not subject to forces
 # prescribing a velocity will therefore make it move at constant velocity (downwards)
 plate.state.vel = (0, 0, -.8)
 # start plotting the data now, it was not interesting before
 O.engines = O.engines + [PyRunner(command='addPlotData()', iterPeriod=200)]
 # next time, do not call this function anymore, but the next one (unloadPlate) instead
 checker.command = 'unloadPlate()'

def unloadPlate():
 # if the force on plate exceeds maximum load, start unloading
 if abs(O.forces.f(plate.id)[2]) > 1e4:
  plate.state.vel *= -1
  # next time, do not call this function anymore, but the next one (stopUnloading) instead
  checker.command = 'stopUnloading()'

def stopUnloading():
    if abs(O.forces.f(plate.id)[2]) < 1e2:
        # calculate the volume of the packing
        volume_packing = 0
        num_spheres = 0
        for b in O.bodies:
            if isinstance(b.shape, yade.wrapper.Sphere):
                volume_packing += 4/3 * math.pi * b.shape.radius**3
                num_spheres += 1
        # print the number of spheres and volume of packking
        print("Number of spheres:", "{:d}".format(num_spheres))
        print("V Packing:", "{:e}".format(volume_packing))
        # print the height of the plate
        top = abs(plate.state.pos[2] + height/2)
        print("Plate height:", top)
        # calculate the volume of the cylinder
        new_volume_cylinder = math.pi * (diameter/2)**2 * top
        # calculate the porosity and porosity percentage
        new_porosity = (new_volume_cylinder - volume_packing) / new_volume_cylinder
        new_porosity_percent = new_porosity * 100
        print("V Cylinder:", "{:e}".format(new_volume_cylinder))
        print("Porosity:", "{:.2f}".format(new_porosity))
        print("Porosity:", "{:.2f}%".format(new_porosity_percent))
        # O.tags can be used to retrieve unique identifiers of the simulation
        # if running in batch, subsequent simulation would overwrite each other's output files otherwise
        # d (or description) is simulation description (composed of parameter values)
        # while the id is composed of time and process number
        plot.saveDataTxt(O.tags['d.id'] + '.txt')
        O.pause()

def addPlotData():
 if not isinstance(O.bodies[-1].shape, Wall):
  plot.addData()
  return
 Fz = O.forces.f(plate.id)[2]
 plot.addData(Fz=Fz, w=plate.state.pos[2] - plate.state.refPos[2], unbalanced=unbalancedForce(), i=O.iter)

# besides unbalanced force evolution, also plot the displacement-force diagram
plot.plots = {'i': ('unbalanced',), 'w': ('Fz',)}
plot.plot()

Question information

Language:
English Edit question
Status:
Expired
For:
Yade Edit question
Assignee:
No assignee Edit question
Last query:
Last reply:
Revision history for this message
Jan Stránský (honzik) said :
#1

Hello,

> I couldn't find out anything about polyhedral ...

[1,2,...]

> if it is possible to change spheres for every sieve size to be polyhedron with different shape and size for each radius group ... Is it possible?

Yes, it is possible
First you have to decide fundamental questions (like if you use Polyhedra or PotentialBlock or ... shape), then continue to "details" (specific shape of particles)

Cheers
Jan

[1] https://gitlab.com/yade-dev/trunk/-/tree/master/examples/polyhedra
[2] https://yade-dem.org/doc/potentialparticles.html

Revision history for this message
Huan (huan-liu) said :
#2

Hello,

I tried understand both method potentialparticles and polyhedra_utils.polyhedra, but it quite complicated for me. I meeting my project deadline soon, if it not too troublesome can you show me method to transform the sphere by using my code?

Thanks
Huan

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

> I tried understand both method potentialparticles and polyhedra_utils.polyhedra, but it quite complicated for me.

You have a few options:
- not use it (easy way)
- invest time to understand and use it with understanding (preferable way IMO)
- use it without understanding (not preferable IMO)

Note: potential blocks is different than potential particles and might suit better for polyhedron modeling.

> I meeting my project deadline soon, if it not too troublesome can you show me method to transform the sphere by using my code?

I think it is quite unlikely that anybody will be willing to do this relatively complex job. Myself I have enough my own deadlines...
Alternatively see Yade main page and section "Paid support and Consulting".

Cheers
Jan

Revision history for this message
Launchpad Janitor (janitor) said :
#4

This question was expired because it remained in the 'Open' state without activity for the last 15 days.