Module DEMIC

@author: Alexandre Sac–Morane alexandre.sac-morane@enpc.fr

This is the Discrete Element Modelization employed during the generation of the initial configuration.

Expand source code

#-------------------------------------------------------------------------------
#Librairies
#-------------------------------------------------------------------------------

from yade import pack, plot, export
import numpy as np
import matplotlib.pyplot as plt
import math, random, pickle

#-------------------------------------------------------------------------------
#User
#-------------------------------------------------------------------------------

# load data 
with open('data/dict_ic', 'rb') as handle:
    dict_ic = pickle.load(handle)

# PSD
n_grains = dict_ic['n_grain']*4
L_dr = []

# Box
Dx = 1
Dy = 1
Dz = 1

# time step
factor_dt_crit = 0.6

# steady-state detection
unbalancedForce_criteria = 0.1

#-------------------------------------------------------------------------------
#Initialisation
#-------------------------------------------------------------------------------

# define wall material (no friction)
O.materials.append(CohFrictMat(young=1, poisson=0.25, frictionAngle=0, density=2650, isCohesive=False, momentRotationLaw=False))
# create box and grains
O.bodies.append(aabbWalls([Vector3(0,0,0),Vector3(Dx,Dy,Dz)], thickness=0., oversizeFactor=1))
# a list of 6 boxes Bodies enclosing the packing, in the order minX, maxX, minY, maxY, minZ, maxZ

# define grain material
O.materials.append(CohFrictMat(young=1, poisson=0.25, frictionAngle=atan(0.05), density=2650,\
                               isCohesive=False, momentRotationLaw=False))
# generate grain
for i in range(n_grains):  
    # define kinetics
    dradius = random.uniform(math.sqrt((0.5*Dx*Dy)/(n_grains*math.pi))/400, math.sqrt((0.5*Dx*Dy)/(n_grains*math.pi))/100)
    center_x = random.uniform(0+dradius*30, Dx-dradius*30)
    center_y = random.uniform(0+dradius*30, Dy-dradius*30)
    center_z = 0
    O.bodies.append(sphere(center=[center_x, center_y, center_z], radius=dradius*30))
    O.bodies[-1].state.blockedDOFs = 'zXY'
    L_dr.append(dradius)

# yade algorithm
O.engines = [
        ForceResetter(),
        # sphere, wall
        InsertionSortCollider([Bo1_Sphere_Aabb(), Bo1_Box_Aabb()]),
        InteractionLoop(
                # need to handle sphere+sphere and sphere+wall
                # Ig : compute contact point. Ig2_Sphere (3DOF) or Ig2_Sphere6D (6DOF)
                # Ip : compute parameters needed
                # Law : compute contact law with parameters from Ip
                [Ig2_Sphere_Sphere_ScGeom6D(), Ig2_Box_Sphere_ScGeom6D()],
                [Ip2_CohFrictMat_CohFrictMat_CohFrictPhys()],
                [Law2_ScGeom6D_CohFrictPhys_CohesionMoment(always_use_moment_law=True)]
        ),
        NewtonIntegrator(gravity=(0, 0, 0), damping=0.1, label = 'Newton'),
        PyRunner(command='checkUnbalanced_ir_ic()', iterPeriod = 200, label='checker')
]
# time step
O.dt = factor_dt_crit * PWaveTimeStep()

#-------------------------------------------------------------------------------

def compute_ratio_overlap_radius():
    '''
    Compute the mean ratio of the overlap and radius.
    '''
    overlap_average = 0
    radius_average = 0
    n_average = 0
    # iterate on interactions
    for i in O.interactions:
        # only grain-grain contact can be cemented
        if isinstance(O.bodies[i.id1].shape, Sphere) and isinstance(O.bodies[i.id2].shape, Sphere) :
            # compute overlap of this interaction
            b1_x = O.bodies[i.id1].state.pos[0]
            b1_y = O.bodies[i.id1].state.pos[1]
            b2_x = O.bodies[i.id2].state.pos[0]
            b2_y = O.bodies[i.id2].state.pos[1]
            dist = math.sqrt((b1_x-b2_x)**2+(b1_y-b2_y)**2)
            overlap = O.bodies[i.id1].shape.radius + O.bodies[i.id2].shape.radius - dist
            # compute mean value
            overlap_average = overlap_average + overlap
            radius_average = radius_average + (O.bodies[i.id1].shape.radius+O.bodies[i.id2].shape.radius)/2
            n_average = n_average + 1
    if n_average > 0:
        # plot and return the average
        overlap_average = overlap_average/n_average
        radius_average = radius_average/n_average
        return overlap_average/radius_average
    else :
        return 1
    
#-------------------------------------------------------------------------------

def checkUnbalanced_ir_ic():
    '''
    Increase particle radius until a steady-state is found.
    '''
    # the rest will be run only if unbalanced is < .1 (stabilized packing)
    # Compute the ratio of mean summary force on bodies and mean force magnitude on interactions.
    if (2<=avgNumInteractions()) and (avgNumInteractions()<=4) and (unbalancedForce() > unbalancedForce_criteria) :
        return

    if (avgNumInteractions()<4):   
        # increase the radius of particles
        i_L_r = 0
        for b in O.bodies :
            if isinstance(b.shape, Sphere):
                growParticle(b.id, (b.shape.radius+L_dr[i_L_r])/b.shape.radius)
                i_L_r = i_L_r + 1
        # update the dt as the radii change
        O.dt = factor_dt_crit * PWaveTimeStep()
        return
        
    # plot the psd
    binsSizes, binsProc, binsSumCum = psd(bins=10)
    plotPSD(binsSizes, binsProc)
    
    # close the simulation
    stopLoad()
    
#-------------------------------------------------------------------------------

def saveData():
    """
    Save data in .txt file during the ic.
    """
    # pp data
    L_pos = []
    L_radius = []
    for b in O.bodies :
        if isinstance(b.shape, Sphere):
            L_pos.append([b.state.pos[0], b.state.pos[1]])
            L_radius.append(b.shape.radius)

    # create dict
    dict_ic = {'L_pos': L_pos, 'L_radius': L_radius}

    # save
    with open('data/dict_ic', 'wb') as handle:
        pickle.dump(dict_ic, handle, protocol=pickle.HIGHEST_PROTOCOL)

#-------------------------------------------------------------------------------

def stopLoad():
    """
    Close simulation.
    """
    # save at the converged iteration
    saveData()
    # close yade
    O.pause()

    # give order to the user
    print("\nyou can type 'quit()' in the terminal")

#-------------------------------------------------------------------------------

def plotPSD(binsSizes, binsProc):
    """
    This function can be called to plot the evolution of the psd.
    """
    plt.figure(1, figsize=(16,9))
    plt.plot(binsSizes, binsProc)
    plt.title('Particle Size Distribution')
    plt.savefig('output/PSD.png')
    plt.close()

#-------------------------------------------------------------------------------
# start simulation
#-------------------------------------------------------------------------------

O.run()

Functions

def compute_ratio_overlap_radius()

Compute the mean ratio of the overlap and radius.

Expand source code

def compute_ratio_overlap_radius():
    '''
    Compute the mean ratio of the overlap and radius.
    '''
    overlap_average = 0
    radius_average = 0
    n_average = 0
    # iterate on interactions
    for i in O.interactions:
        # only grain-grain contact can be cemented
        if isinstance(O.bodies[i.id1].shape, Sphere) and isinstance(O.bodies[i.id2].shape, Sphere) :
            # compute overlap of this interaction
            b1_x = O.bodies[i.id1].state.pos[0]
            b1_y = O.bodies[i.id1].state.pos[1]
            b2_x = O.bodies[i.id2].state.pos[0]
            b2_y = O.bodies[i.id2].state.pos[1]
            dist = math.sqrt((b1_x-b2_x)**2+(b1_y-b2_y)**2)
            overlap = O.bodies[i.id1].shape.radius + O.bodies[i.id2].shape.radius - dist
            # compute mean value
            overlap_average = overlap_average + overlap
            radius_average = radius_average + (O.bodies[i.id1].shape.radius+O.bodies[i.id2].shape.radius)/2
            n_average = n_average + 1
    if n_average > 0:
        # plot and return the average
        overlap_average = overlap_average/n_average
        radius_average = radius_average/n_average
        return overlap_average/radius_average
    else :
        return 1
def checkUnbalanced_ir_ic()

Increase particle radius until a steady-state is found.

Expand source code

def checkUnbalanced_ir_ic():
    '''
    Increase particle radius until a steady-state is found.
    '''
    # the rest will be run only if unbalanced is < .1 (stabilized packing)
    # Compute the ratio of mean summary force on bodies and mean force magnitude on interactions.
    if (2<=avgNumInteractions()) and (avgNumInteractions()<=4) and (unbalancedForce() > unbalancedForce_criteria) :
        return

    if (avgNumInteractions()<4):   
        # increase the radius of particles
        i_L_r = 0
        for b in O.bodies :
            if isinstance(b.shape, Sphere):
                growParticle(b.id, (b.shape.radius+L_dr[i_L_r])/b.shape.radius)
                i_L_r = i_L_r + 1
        # update the dt as the radii change
        O.dt = factor_dt_crit * PWaveTimeStep()
        return
        
    # plot the psd
    binsSizes, binsProc, binsSumCum = psd(bins=10)
    plotPSD(binsSizes, binsProc)
    
    # close the simulation
    stopLoad()
def saveData()

Save data in .txt file during the ic.

Expand source code

def saveData():
    """
    Save data in .txt file during the ic.
    """
    # pp data
    L_pos = []
    L_radius = []
    for b in O.bodies :
        if isinstance(b.shape, Sphere):
            L_pos.append([b.state.pos[0], b.state.pos[1]])
            L_radius.append(b.shape.radius)

    # create dict
    dict_ic = {'L_pos': L_pos, 'L_radius': L_radius}

    # save
    with open('data/dict_ic', 'wb') as handle:
        pickle.dump(dict_ic, handle, protocol=pickle.HIGHEST_PROTOCOL)
def stopLoad()

Close simulation.

Expand source code

def stopLoad():
    """
    Close simulation.
    """
    # save at the converged iteration
    saveData()
    # close yade
    O.pause()

    # give order to the user
    print("\nyou can type 'quit()' in the terminal")
def plotPSD()

This function can be called to plot the evolution of the psd.

Expand source code

def plotPSD(binsSizes, binsProc):
    """
    This function can be called to plot the evolution of the psd.
    """
    plt.figure(1, figsize=(16,9))
    plt.plot(binsSizes, binsProc)
    plt.title('Particle Size Distribution')
    plt.savefig('output/PSD.png')
    plt.close()