The code

@author: Alexandre Sac–Morane alexandre.sac-morane@uclouvain.be

This is the main file with the functions defined.

Expand source code
#-------------------------------------------------------------------------------
#Librairies
#-------------------------------------------------------------------------------

from yade import pack, plot, export
import numpy as np
import matplotlib.pyplot as plt
import os
import shutil
import time
import math
import random
import pickle
from pathlib import Path

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

# PSD
n_grains = 3000
L_r = []

# Particles
rMean = 0.000100  # m
rRelFuzz = .25

# Box
Dz_on_Dx = 1 # ratio Dz / Dxy
Dz = 0.0028 # m
Dx = Dz/Dz_on_Dx
Dy = Dx

# IC
n_steps_ic = 100

# Top wall
P_load = 1e7 # Pa
kp = 1e-9 # m.N-1

# Lateral wall
k0_target = 0.7
# same kp as top wall

# cementation
P_cementation = P_load*0.01 # Pa
# 2T   : E ( 300MPa), f_cemented (0.13), m_log (6.79), s_log (0.70)
# 2MB  : E ( 320MPa), f_cemented (0.88), m_log (7.69), s_log (0.60)
# 11BB : E ( 760MPa), f_cemented (0.98), m_log (8.01), s_log (0.88)
# 13BT : E ( 860MPa), f_cemented (1.00), m_log (8.44), s_log (0.92)
# 13MB : E (1000MPa), f_cemented (1.00), m_log (8.77), s_log (0.73)
type_cementation = '13MB' # only for the report
f_cemented = 1. # -
m_log = 8.77 # -
s_log = 0.73 # -
YoungModulus = 1000e6
considerYoungReduction = True
tensileCohesion = 2.75e6 # Pa
shearCohesion = 6.6e6 # Pa

# Dissolution
f_Sc_diss_1 = 2e-2
f_Sc_diss_2 = 1e-1
dSc_dissolved_1 = f_Sc_diss_1*np.exp(m_log)*1e-12
dSc_dissolved_2 = f_Sc_diss_2*np.exp(m_log)*1e-12
diss_level_1_2 = 0.9
f_n_bond_stop = 0
s_bond_diss = 0

# time step
factor_dt_crit = 0.6

# steady-state detection
unbalancedForce_criteria = 0.01

# Report
simulation_report_name = O.tags['d.id']+'_report.txt'
simulation_report = open(simulation_report_name, 'w')
simulation_report.write('Oedometer test under acid injection\n')
simulation_report.write('Type of sample: Rock\n')
simulation_report.write('Cementation at '+str(int(P_cementation))+' Pa\n')
simulation_report.write('Type of cementation: '+type_cementation+'\n')
simulation_report.write('Confinement at '+str(int(P_load))+' Pa\n')
simulation_report.write('Initial k0 targetted: '+str(round(k0_target,3))+'\n\n')
simulation_report.close()

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

# clock to show performances
tic = time.perf_counter()
tic_0 = tic
iter_0 = 0

# plan simulation
if Path('plot').exists():
    shutil.rmtree('plot')
os.mkdir('plot')
if Path('data').exists():
    shutil.rmtree('data')
os.mkdir('data')
if Path('vtk').exists():
    shutil.rmtree('vtk')
os.mkdir('vtk')
if Path('save').exists():
    shutil.rmtree('save')
os.mkdir('save')

# define wall material (no friction)
O.materials.append(CohFrictMat(young=YoungModulus, 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
# extent the plates
O.bodies[0].shape.extents = Vector3(0,1.5*Dy/2,1.5*Dz/2)
O.bodies[1].shape.extents = Vector3(0,1.5*Dy/2,1.5*Dz/2)
O.bodies[2].shape.extents = Vector3(1.5*Dx/2,0,1.5*Dz/2)
O.bodies[3].shape.extents = Vector3(1.5*Dx/2,0,1.5*Dz/2)
O.bodies[4].shape.extents = Vector3(1.5*Dx/2,1.5*Dy/2,0)
O.bodies[5].shape.extents = Vector3(1.5*Dx/2,1.5*Dy/2,0)
# global names
lateral_plate = O.bodies[1]
upper_plate = O.bodies[-1]

# define grain material
O.materials.append(CohFrictMat(young=YoungModulus, poisson=0.25, frictionAngle=atan(0.05), density=2650,\
                               isCohesive=True, normalCohesion=tensileCohesion, shearCohesion=shearCohesion,\
                               momentRotationLaw=True, alphaKr=0, alphaKtw=0))
# frictionAngle, alphaKr, alphaKtw are set to 0 during IC. The real value is set after IC.
frictionAngleReal = radians(20)
alphaKrReal = 0.5
alphaKtwReal = 0.5

# generate grain
for i in range(n_grains):
    radius = random.uniform(rMean*(1-rRelFuzz),rMean*(1+rRelFuzz))
    center_x = random.uniform(0+radius/n_steps_ic, Dx-radius/n_steps_ic)
    center_y = random.uniform(0+radius/n_steps_ic, Dy-radius/n_steps_ic)
    center_z = random.uniform(0+radius/n_steps_ic, Dz-radius/n_steps_ic)
    O.bodies.append(sphere(center=[center_x, center_y, center_z], radius=radius/n_steps_ic))
    # can use b.state.blockedDOFs = 'xyzXYZ' to block translation of rotation of a body
    L_r.append(radius)
O.tags['Step ic'] = '1'

# 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.001, label = 'Newton'),
        PyRunner(command='checkUnbalanced_ir_ic()', iterPeriod = 200, label='checker')
]
# time step
O.dt = factor_dt_crit * PWaveTimeStep()

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

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 unbalancedForce() > .1:
        return
    # increase the radius of particles
    if int(O.tags['Step ic']) < n_steps_ic :
        print('IC step '+O.tags['Step ic']+'/'+str(n_steps_ic)+' done')
        O.tags['Step ic'] = str(int(O.tags['Step ic'])+1)
        i_L_r = 0
        for b in O.bodies :
            if isinstance(b.shape, Sphere):
                growParticle(b.id, int(O.tags['Step ic'])/n_steps_ic*L_r[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
    global L_L_psd_binsSizes, L_L_psd_binsProc
    L_L_psd_binsSizes = []
    L_L_psd_binsProc = []
    binsSizes, binsProc, binsSumCum = psd(bins=10)
    L_L_psd_binsSizes.append(binsSizes)
    L_L_psd_binsProc.append(binsProc)
    plotPSD()
    # characterize the ic algorithm
    global tic
    global iter_0
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    tic = tac
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("IC Generated : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    simulation_report.write(str(O.iter-iter_0)+' Iterations\n')
    simulation_report.write(str(n_grains)+' grains\n\n')
    simulation_report.close()
    print("\nIC Generated : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    # save
    #O.save('save/simu_ic.yade.bz2')
    # next time, do not call this function anymore, but the next one instead
    iter_0 = O.iter
    checker.command = 'checkUnbalanced_load_cementation_ic()'
    checker.iterPeriod = 500
    # control top wall
    O.engines = O.engines + [PyRunner(command='controlTopWall_ic()', iterPeriod = 1)]
    # switch on the gravity
    Newton.gravity = [0, 0, -9.81]

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

def controlTopWall_ic():
    '''
    Control the upper wall to applied a defined confinement force.

    The displacement of the wall depends on the force difference. A maximum value is defined.
    '''
    Fz = O.forces.f(upper_plate.id)[2]
    if Fz == 0:
        upper_plate.state.pos =  (lateral_plate.state.pos[0]/2, Dy/2, max([b.state.pos[2]+0.99*b.shape.radius for b in O.bodies if isinstance(b.shape, Sphere)]))
    else :
        dF = Fz - P_cementation*lateral_plate.state.pos[0]*Dy
        v_plate_max = rMean*0.00005/O.dt
        v_try_abs = abs(kp*dF)/O.dt
        # maximal speed is applied to top wall
        if v_try_abs < v_plate_max :
            upper_plate.state.vel = (0, 0, np.sign(dF)*v_try_abs)
        else :
            upper_plate.state.vel = (0, 0, np.sign(dF)*v_plate_max)

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

def checkUnbalanced_load_cementation_ic():
    '''
    Wait to reach the vertical pressure targetted for cementation.
    '''
    addPlotData_cementation_ic()
    saveData_ic()
    # check the force applied
    if abs(O.forces.f(upper_plate.id)[2]-P_cementation*Dx*Dy)/(P_cementation*Dx*Dy) > 0.005:
        return
    if unbalancedForce() > unbalancedForce_criteria :
        return
    # characterize the ic algorithm
    global tic
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    tic = tac
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Pressure (Cementation) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    simulation_report.write(str(n_grains)+' grains\n\n')
    simulation_report.close()
    print("\nPressure (Cementation) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    # switch on friction, bending resistance and twisting resistance between particles
    O.materials[-1].frictionAngle = frictionAngleReal
    O.materials[-1].alphaKr = alphaKrReal
    O.materials[-1].alphaKtw = alphaKtwReal
    # for existing contacts, clear them
    O.interactions.clear()
    # calm down particles
    for b in O.bodies:
        if isinstance(b.shape,Sphere):
            b.state.angVel = Vector3(0,0,0)
            b.state.vel = Vector3(0,0,0)
    # switch off damping
    Newton.damping = 0
    # next time, do not call this function anymore, but the next one instead
    checker.command = 'checkUnbalanced_param_ic()'

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

def checkUnbalanced_param_ic():
    '''
    Wait to reach the equilibrium after switching on the friction and the rolling resistances.
    '''
    addPlotData_cementation_ic()
    saveData_ic()
    # check the force applied
    if abs(O.forces.f(upper_plate.id)[2]-P_cementation*Dx*Dy)/(P_cementation*Dx*Dy) > 0.005:
        return
    if unbalancedForce() > unbalancedForce_criteria :
        return
    # characterize the ic algorithm
    global tic
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    tic = tac
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Parameters applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n\n")
    simulation_report.close()
    print("\nParameters applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    # save
    #O.save('save/'+O.tags['d.id']+'_ic.yade.bz2')
    # next time, do not call this function anymore, but the next one instead
    checker.command = 'cementation()'
    checker.iterPeriod = 10

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

def pdf_lognormal(x,m_log,s_log):
    '''
    Return the probability of a value x with a log normal function defined by the mean m_log and the variance s_log.
    '''
    p = np.exp(-(np.log(x)-m_log)**2/(2*s_log**2))/(x*s_log*np.sqrt(2*np.pi))
    return p

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

def cementation():
    '''
    Generate cementation between grains.
    '''
    # generate the list of cohesive surface area and its list of weight
    x_min = 1e2 # µm2
    x_max = 4e4 # µm2
    n_x = 20000
    x_L = np.linspace(x_min, x_max, n_x)
    p_x_L = []
    for x in x_L :
        p_x_L.append(pdf_lognormal(x,m_log,s_log))
    # counter
    global counter_bond, counter_bond0, counter_bond_broken_diss, counter_bond_broken_load
    counter_bond = 0
    counter_bond_broken_diss = 0
    counter_bond_broken_load = 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) :
            # only a fraction of the contact is cemented
            if random.uniform(0,1) < f_cemented :
                counter_bond = counter_bond + 1
                # creation of cohesion
                i.phys.cohesionBroken = False
                # determine the cohesive surface
                cohesiveSurface = random.choices(x_L,p_x_L)[0]*1e-12 # µm2
                # set normal and shear adhesions
                i.phys.normalAdhesion = tensileCohesion*cohesiveSurface
                i.phys.shearAdhesion = shearCohesion*cohesiveSurface
    counter_bond0 = counter_bond
    # write in the report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write(str(counter_bond)+" contacts cemented\n\n")
    simulation_report.close()
    print('\n'+str(counter_bond)+" contacts cemented\n")

    # next time, do not call this function anymore, but the next one instead
    checker.command = 'checkUnbalanced_load_confinement_ic()'
    checker.iterPeriod = 200
    # activate Young reduction
    if considerYoungReduction :
        O.engines = O.engines[:-1] + [PyRunner(command='YoungReduction()', iterPeriod = 100)] + [O.engines[-1]]
    # change the vertical pressure applied
    O.engines = O.engines[:-1] + [PyRunner(command='controlTopWall()', iterPeriod = 1)]

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

def checkUnbalanced_load_confinement_ic():
    '''
    Wait to reach the vertical pressure targetted for confinement.
    '''
    addPlotData_confinement_ic()
    saveData_ic()
    # check the force applied
    if abs(O.forces.f(upper_plate.id)[2]-P_load*Dx*Dy)/(P_load*Dx*Dy) > 0.005:
        return
    if unbalancedForce() > unbalancedForce_criteria :
        return
    # characterize the ic algorithm
    global tic
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    tic = tac
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Pressure (Confinement) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    simulation_report.write(str(n_grains)+' grains\n\n')
    simulation_report.close()
    print("\nPressure (Confinement) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")

    # next time, do not call this function anymore, but the next one instead
    checker.command = 'checkUnbalanced_load_k0_ic()'
    # control lateral wall
    O.engines = O.engines + [PyRunner(command='controlLateralWall_ic()', iterPeriod = 1, label='k0_checker')]

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

def controlLateralWall_ic():
    '''
    Control the lateral wall to applied a defined confinement force.

    The displacement of the wall depends on the force difference. A maximum value is defined.
    '''
    Fx = O.forces.f(lateral_plate.id)[0]
    if Fx == 0:
        lateral_plate.state.pos =  (max([b.state.pos[0]+0.99*b.shape.radius for b in O.bodies if isinstance(b.shape, Sphere)]), Dy/2, upper_plate.state.pos[2]/2)
    else :
        dF = Fx - k0_target*P_load*upper_plate.state.pos[2]*Dy
        v_plate_max = rMean*0.00002/O.dt
        v_try_abs = abs(kp*dF)/O.dt
        # maximal speed is applied to top wall
        if v_try_abs < v_plate_max :
            lateral_plate.state.vel = (np.sign(dF)*v_try_abs, 0, 0)
        else :
            lateral_plate.state.vel = (np.sign(dF)*v_plate_max, 0, 0)

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

def DoNotControlLateralWall_ic():
    '''
    Switch off the control of the lateral wall.
    '''
    lateral_plate.state.vel = (0,0,0)
    O.engines = O.engines[:-1]

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

def checkUnbalanced_load_k0_ic():
    '''
    Wait to reach the earth pressure coefficient targetted.

    k0 = s_II/s_I, where s_I is the vertical pressure and s_II is the lateral pressure.
    '''
    addPlotData_confinement_ic()
    saveData_ic()
    # check the force applied
    if abs(O.forces.f(upper_plate.id)[2]  - P_load*lateral_plate.state.pos[0]*Dy)/(P_load*lateral_plate.state.pos[0]*Dy) > 0.005 or\
    abs(O.forces.f(lateral_plate.id)[0] - k0_target*P_load*upper_plate.state.pos[2]*Dy)/(k0_target*P_load*upper_plate.state.pos[2]*Dy) > 0.005:
        return
    if unbalancedForce() > unbalancedForce_criteria :
        return

    # characterize the ic algorithm
    global tic, iter_0
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    tic = tac
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Pressure (k0) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    simulation_report.write('IC generation ends\n')
    simulation_report.write(str(O.iter-iter_0)+' Iterations\n')
    simulation_report.write(str(n_grains)+' grains\n\n')
    simulation_report.close()
    print("\nPressure (k0) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")

    # reset plot (IC done, simulation starts)
    plot.reset()
    # switch off control lateral wall
    O.engines = O.engines[:-1]+[PyRunner(command='DoNotControlLateralWall_ic()', iterPeriod = 1)]
    # save new reference position for upper wall
    upper_plate.state.refPos = upper_plate.state.pos
    # next time, do not call this function anymore, but the next one instead
    iter_0 = O.iter
    checker.command = 'checkUnbalanced()'
    checker.iterPeriod = 500
    # label step
    O.tags['Current Step']='0'
    # trackers
    global L_unbalanced_ite, L_k0_ite, L_confinement_ite, L_count_bond, n_try
    n_try = 0
    L_unbalanced_ite = []
    L_k0_ite = []
    L_confinement_ite = []
    L_count_bond = []

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

def addPlotData_cementation_ic():
    """
    Save data in plot.
    """
    # add forces applied on wall x and z
    sz = O.forces.f(upper_plate.id)[2]/(lateral_plate.state.pos[0]*Dy)
    sx = O.forces.f(lateral_plate.id)[0]/(Dy*upper_plate.state.pos[2])
    # add data
    plot.addData(i=O.iter-iter_0, porosity=porosity(), coordination=avgNumInteractions(), unbalanced=unbalancedForce(),\
                 Sx=sx, Sz=sz, conf_verified=sz/P_cementation*100, n_bond=0,\
                 vert_strain=100*(upper_plate.state.pos[2]-upper_plate.state.refPos[2])/upper_plate.state.refPos[2], lat_strain=100*(lateral_plate.state.pos[0]-lateral_plate.state.refPos[0])/lateral_plate.state.refPos[0])

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

def addPlotData_confinement_ic():
    """
    Save data in plot.
    """
    # add forces applied on wall x and z
    sz = O.forces.f(upper_plate.id)[2]/(lateral_plate.state.pos[0]*Dy)
    sx = O.forces.f(lateral_plate.id)[0]/(Dy*upper_plate.state.pos[2])
    # count the number the bond
    n_bond = 0
    for i in O.interactions:
        if isinstance(O.bodies[i.id1].shape, Sphere) and isinstance(O.bodies[i.id2].shape, Sphere):
            if not i.phys.cohesionBroken :
                n_bond = n_bond + 1
    # add data
    plot.addData(i=O.iter-iter_0, porosity=porosity(), coordination=avgNumInteractions(), unbalanced=unbalancedForce(),\
                 Sx=sx, Sz=sz, conf_verified=sz/P_load*100, n_bond=n_bond,\
                 vert_strain=100*(upper_plate.state.pos[2]-upper_plate.state.refPos[2])/upper_plate.state.refPos[2], lat_strain=100*(lateral_plate.state.pos[0]-lateral_plate.state.refPos[0])/lateral_plate.state.refPos[0])

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

def saveData_ic():
    """
    Save data in .txt file during the ic.
    """
    plot.saveDataTxt('data/IC_'+O.tags['d.id']+'.txt')
    # post-proccess
    L_sigma_x = []
    L_sigma_z = []
    L_confinement = []
    L_coordination = []
    L_unbalanced = []
    L_ite  = []
    L_vert_strain = []
    L_lat_strain = []
    L_porosity = []
    L_n_bond = []
    file = 'data/IC_'+O.tags['d.id']+'.txt'
    data = np.genfromtxt(file, skip_header=1)
    file_read = open(file, 'r')
    lines = file_read.readlines()
    file_read.close()
    if len(lines) >= 3:
        for i in range(len(data)):
            L_sigma_x.append(abs(data[i][0]))
            L_sigma_z.append(abs(data[i][1]))
            L_confinement.append(data[i][2])
            L_coordination.append(data[i][3])
            L_ite.append(data[i][4])
            L_lat_strain.append(data[i][5])
            L_n_bond.append(data[i][6])
            L_porosity.append(data[i][7])
            L_unbalanced.append(data[i][8])
            L_vert_strain.append(data[i][9])

        # plot
        fig, ((ax1, ax2, ax3), (ax4, ax5, ax6)) = plt.subplots(2,3, figsize=(20,10),num=1)

        ax1.plot(L_ite, L_sigma_x, label = r'$\sigma_x$')
        ax1.plot(L_ite, L_sigma_z, label = r'$\sigma_z$')
        ax1.legend()
        ax1.set_title('Stresses (Pa)')

        ax2.plot(L_ite, L_unbalanced, 'b')
        ax2.set_ylabel('Unbalanced (-)', color='b')
        ax2.set_ylim(ymin=0, ymax=2*unbalancedForce_criteria)
        ax2b = ax2.twinx()
        ax2b.plot(L_ite, L_confinement, 'r')
        ax2b.set_ylabel('Confinement (%)', color='r')
        ax2b.set_title('Steady-state indices')

        ax3.plot(L_ite, L_n_bond)
        ax3.set_title('Number of bond (-)')

        ax4.plot(L_ite, L_lat_strain, label=r'$\epsilon_x$ (%)')
        ax4.plot(L_ite, L_vert_strain, label=r'$\epsilon_z$ (%)')
        ax4.legend()
        ax4.set_title('Strains (%)')

        ax5.plot(L_ite, L_porosity)
        ax5.set_title('Porosity (-)')

        ax6.plot(L_ite, L_coordination)
        ax6.set_title('Coordination number (-)')

        plt.savefig('plot/IC_'+O.tags['d.id']+'.png')

        plt.close()

#-------------------------------------------------------------------------------
#Load
#-------------------------------------------------------------------------------

def YoungReduction():
    '''
    Reduce the Young modulus with the number of bond dissolved.

    E = (E0-E1)*f_diss + E1
    '''
    # count the number of bond dissolved
    global counter_bond, counter_bond_broken_load
    counter_bond = 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) :
            if not i.phys.cohesionBroken :
                counter_bond = counter_bond + 1
    counter_bond_broken_load = (counter_bond0-counter_bond) - counter_bond_broken_diss
    # compute the new Young modulus
    f_bond_diss = (counter_bond_broken_diss+counter_bond_broken_load)/counter_bond0
    NewYoungModulus = (YoungModulus-80e6)*(1-f_bond_diss) + 80e6
    # update material
    for mat in O.materials :
        mat.young = NewYoungModulus
    # update the interactions
    for inter in O.interactions :
        if isinstance(O.bodies[inter.id1].shape, Sphere) and isinstance(O.bodies[inter.id2].shape, Sphere):
            inter.phys.kn = NewYoungModulus*(O.bodies[inter.id1].shape.radius*2*O.bodies[inter.id2].shape.radius*2)/(O.bodies[inter.id1].shape.radius*2+O.bodies[inter.id2].shape.radius*2)
            inter.phys.ks = 0.25*NewYoungModulus*(O.bodies[inter.id1].shape.radius*2*O.bodies[inter.id2].shape.radius*2)/(O.bodies[inter.id1].shape.radius*2+O.bodies[inter.id2].shape.radius*2) # 0.25 is the Poisson ratio
            inter.phys.kr = inter.phys.ks*alphaKrReal*O.bodies[inter.id1].shape.radius*O.bodies[inter.id2].shape.radius
            inter.phys.ktw = inter.phys.ks*alphaKtwReal*O.bodies[inter.id1].shape.radius*O.bodies[inter.id2].shape.radius
        else : # Sphere-Wall contact
            if isinstance(O.bodies[inter.id1].shape, Sphere):
                grain = O.bodies[inter.id1]
            else:
                grain = O.bodies[inter.id2]
            # diameter of the wall is equivalent of the diameter of the sphere
            inter.phys.kn = NewYoungModulus*(grain.shape.radius*2*grain.shape.radius*2)/(grain.shape.radius*2+grain.shape.radius*2)
            inter.phys.ks = 0.25*NewYoungModulus*(grain.shape.radius*2*grain.shape.radius*2)/(grain.shape.radius*2+grain.shape.radius*2) # 0.25 is the Poisson ratio
            # no moment/twist for sphere-wall
    # update time step because the Young modulus change
    O.dt = factor_dt_crit * PWaveTimeStep()

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

def controlTopWall():
    '''
    Control the upper wall to applied a defined confinement force.

    The displacement of the wall depends on the force difference. A maximum value is defined.
    '''
    Fz = O.forces.f(upper_plate.id)[2]
    if Fz == 0:
        upper_plate.state.pos =  (lateral_plate.state.pos[0]/2, Dy/2, max([b.state.pos[2]+0.99*b.shape.radius for b in O.bodies if isinstance(b.shape, Sphere)]))
    else :
        dF = Fz - P_load*lateral_plate.state.pos[0]*Dy
        v_plate_max = rMean*0.00005/O.dt
        v_try_abs = abs(kp*dF)/O.dt
        # maximal speed is applied to top wall
        if v_try_abs < v_plate_max :
            upper_plate.state.vel = (0, 0, np.sign(dF)*v_try_abs)
        else :
            upper_plate.state.vel = (0, 0, np.sign(dF)*v_plate_max)

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

def count_bond():
    '''
    Count the number of bond.
    '''
    counter_bond = 0
    for i in O.interactions:
        if isinstance(O.bodies[i.id1].shape, Sphere) and isinstance(O.bodies[i.id2].shape, Sphere):
            if not i.phys.cohesionBroken :
                counter_bond = counter_bond + 1
    return counter_bond

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

def checkUnbalanced():
    """
    Look for the equilibrium during the loading phase.
    """
    global L_unbalanced_ite, L_k0_ite, L_confinement_ite, L_count_bond, n_try
    # count the number of tries
    n_try = n_try + 1
    # track and plot unbalanced
    L_unbalanced_ite.append(unbalancedForce())
    if O.forces.f(upper_plate.id)[2] != 0:
        k0 = abs(O.forces.f(lateral_plate.id)[0]/(upper_plate.state.pos[2]*Dy)*(lateral_plate.state.pos[0]*Dy)/O.forces.f(upper_plate.id)[2])
    else :
        k0 = 0
    L_k0_ite.append(k0)
    L_confinement_ite.append(O.forces.f(upper_plate.id)[2]/(P_load*lateral_plate.state.pos[0]*Dy)*100)
    L_count_bond.append(count_bond())

    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2,2, figsize=(16,9),num=1)

    ax1.plot(L_unbalanced_ite)
    ax1.set_title('unbalanced force (-)')

    ax2.plot(L_k0_ite)
    ax2.set_title(r'$k_0$ (-)')

    ax3.plot(L_confinement_ite)
    ax3.set_title('confinement (%)')

    ax4.plot(L_count_bond)
    ax4.set_title('Number of bond (-)')

    fig.savefig('plot/tracking_ite.png')
    plt.close()

    # verify confinement pressure applied
    if not abs(O.forces.f(upper_plate.id)[2]-P_load*lateral_plate.state.pos[0]*Dy) < 0.005*P_load*lateral_plate.state.pos[0]*Dy:
        return
    # verify unbalanced force criteria
    # a limit is set for number of tries
    if unbalancedForce() < unbalancedForce_criteria or n_try > 30:

        # reset trackers
        n_try = 0
        L_unbalanced_ite = []
        L_k0_ite = []
        L_confinement_ite = []
        L_count_bond = []

        if counter_bond0*f_n_bond_stop < counter_bond:
            dissolve()
        else :
            stopLoad()

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

def dissolve():
    """
    Dissolve bond with a constant surface reduction.
    """
    O.tags['Current Step'] = str(int(O.tags['Current Step'])+1)
    # save at the end
    saveData()
    # count the number of bond
    global counter_bond, counter_bond_broken_diss, s_bond_diss
    counter_bond = 0
    counter_bond_broken = 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) :
            if not i.phys.cohesionBroken :
                counter_bond = counter_bond + 1
                # set normal and shear adhesions
                if (counter_bond_broken_diss+counter_bond_broken_load)/counter_bond0 < diss_level_1_2 :
                    i.phys.normalAdhesion = i.phys.normalAdhesion - tensileCohesion*dSc_dissolved_1
                    i.phys.shearAdhesion = i.phys.shearAdhesion - shearCohesion*dSc_dissolved_1
                else :
                    i.phys.normalAdhesion = i.phys.normalAdhesion - tensileCohesion*dSc_dissolved_2
                    i.phys.shearAdhesion = i.phys.shearAdhesion - shearCohesion*dSc_dissolved_2
                if i.phys.normalAdhesion <= 0 or i.phys.shearAdhesion <=0 :
                    # bond brokes
                    counter_bond = counter_bond - 1
                    counter_bond_broken = counter_bond_broken + 1
                    i.phys.cohesionBroken = True
                    i.phys.normalAdhesion = 0
                    i.phys.shearAdhesion = 0
    # update bond surface dissolved tracker
    if (counter_bond_broken_diss+counter_bond_broken_load)/counter_bond0 < diss_level_1_2 :
        s_bond_diss = s_bond_diss + dSc_dissolved_1
    else :
        s_bond_diss = s_bond_diss + dSc_dissolved_2
    # update the counter of bond dissolved during the dissolution step
    counter_bond_broken_diss = counter_bond_broken_diss + counter_bond_broken

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

def stopLoad():
    """
    Close simulation.
    """
    # save at the converged iteration
    saveData()
    # close yade
    O.pause()
    # characterize the dem step
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Oedometric test : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    simulation_report.close()
    print("Oedometric test : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds")
    # characterize the last DEM step and the simulation
    hours = (tac-tic_0)//(60*60)
    minutes = (tac-tic_0 -hours*60*60)//(60)
    seconds = int(tac-tic_0 -hours*60*60 -minutes*60)
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Simulation time : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n\n")
    simulation_report.close()
    print("\nSimulation time : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")

    # give final values
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("k0 initial: "+str(round(k0_target,3))+" / k0 final: "+str(round(abs(O.forces.f(lateral_plate.id)[0]/(upper_plate.state.pos[2]*Dy)*(lateral_plate.state.pos[0]*Dy)/O.forces.f(upper_plate.id)[2]),3))+"\n")
    simulation_report.write(str(int(counter_bond_broken_diss))+" bonds broken during dissolution steps ("+str(int(counter_bond_broken_diss/counter_bond0*100))+" % of initial number of bonds)\n")
    simulation_report.write(str(int(counter_bond_broken_load))+" bonds broken during loading steps ("+str(int(counter_bond_broken_load/counter_bond0*100))+" % of initial number of bonds)\n")
    simulation_report.close()
    print("k0 initial:",round(k0_target,3)," / k0 final:",round(abs(O.forces.f(lateral_plate.id)[0]/(upper_plate.state.pos[2]*Dy)*(lateral_plate.state.pos[0]*Dy)/O.forces.f(upper_plate.id)[2]),3))
    print(int(counter_bond_broken_diss),"bonds broken during dissolution steps ("+str(int(counter_bond_broken_diss/counter_bond0*100)),"% of initial number of bonds)")
    print(int(counter_bond_broken_load),"bonds broken during loading steps ("+str(int(counter_bond_broken_load/counter_bond0*100)),"% of initial number of bonds)")

    # save simulation
    os.mkdir('../AcidOedo_Rock_data/'+O.tags['d.id'])
    shutil.copytree('data','../AcidOedo_Rock_data/'+O.tags['d.id']+'/data')
    shutil.copytree('plot','../AcidOedo_Rock_data/'+O.tags['d.id']+'/plot')
    shutil.copytree('save','../AcidOedo_Rock_data/'+O.tags['d.id']+'/save')
    shutil.copy('AcidOedo_Rock.py','../AcidOedo_Rock_data/'+O.tags['d.id']+'/AcidOedo_Rock.py')
    shutil.copy(O.tags['d.id']+'_report.txt','../AcidOedo_Rock_data/'+O.tags['d.id']+'/'+O.tags['d.id']+'_report.txt')

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

def addPlotData():
    """
    Save data in plot.
    """
    # add forces applied on wall x and z
    sz = O.forces.f(upper_plate.id)[2]/(Dy*lateral_plate.state.pos[0])
    sx = O.forces.f(lateral_plate.id)[0]/(Dy*upper_plate.state.pos[2])
    # compute the k0 = sigma_x/sigma_z, = 0 if no sigma_z
    if sz != 0:
        k0 = abs(sx/sz)
    else :
        k0 = 0
    # add data
    plot.addData(i=O.iter-iter_0, porosity=porosity(), coordination=avgNumInteractions(), unbalanced=unbalancedForce(), \
                counter_bond=counter_bond, counter_bond_broken_diss=counter_bond_broken_diss, counter_bond_broken_load=counter_bond_broken_load,\
                Sx=sx, Sz=sz, Z_plate=upper_plate.state.pos[2], conf_verified=sz/P_load*100, k0=k0,\
                w=upper_plate.state.pos[2]-upper_plate.state.refPos[2], vert_strain=100*(upper_plate.state.pos[2]-upper_plate.state.refPos[2])/upper_plate.state.refPos[2],
                s_bond_diss=s_bond_diss)

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

def saveData():
    """
    Save data in .txt file during the steps.
    """
    addPlotData()
    plot.saveDataTxt('data/'+O.tags['d.id']+'.txt')
    # post-proccess
    L_s_bond_diss = []
    L_k0 = []
    L_counter_bond = []
    L_counter_bond_broken_diss = []
    L_counter_bond_broken_load = []
    L_vert_strain = []
    L_porosity = []
    L_coordination = []
    file = 'data/'+O.tags['d.id']+'.txt'
    data = np.genfromtxt(file, skip_header=1)
    file_read = open(file, 'r')
    lines = file_read.readlines()
    file_read.close()
    if len(lines) >= 3:
        for i in range(len(data)):
            L_coordination.append(data[i][4])
            L_counter_bond.append(data[i][5])
            L_counter_bond_broken_diss.append(data[i][6])
            L_counter_bond_broken_load.append(data[i][7])
            L_k0.append(data[i][9])
            L_porosity.append(data[i][10])
            L_s_bond_diss.append(data[i][11])
            L_vert_strain.append(data[i][13])

        # plot
        fig, ((ax1, ax2, ax3), (ax4, ax5, ax6)) = plt.subplots(2,3, figsize=(16,9),num=1)

        ax1.plot(L_s_bond_diss, L_k0)
        ax1.set_title(r'$k_0$ (-)')

        ax2.plot(L_s_bond_diss, L_counter_bond)
        ax2.set_title('Number of bond (-)')

        ax3.plot(L_s_bond_diss, L_counter_bond_broken_diss, label='during dissolution')
        ax3.plot(L_s_bond_diss, L_counter_bond_broken_load, label='during loading')
        ax3.set_title('Number of bonds broken (-)')
        ax3.legend()

        ax4.plot(L_s_bond_diss, L_vert_strain)
        ax4.set_title(r'$\epsilon_z$ (%)')

        ax5.plot(L_s_bond_diss, L_porosity)
        ax5.set_title('Porosity (-)')

        ax6.plot(L_s_bond_diss, L_coordination)
        ax6.set_title('Coordination (-)')

        plt.suptitle(r'Trackers - bond surface reduction (m$^2$)')
        plt.savefig('plot/'+O.tags['d.id']+'.png')
        plt.close()

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

def plotPSD():
    """
    This function can be called to plot the evolution of the psd.
    """
    plt.figure(1, figsize=(16,9))
    for i_psd in range(len(L_L_psd_binsSizes)):
        binsSizes = L_L_psd_binsSizes[i_psd]
        binsProc = L_L_psd_binsProc[i_psd]
        plt.plot(binsSizes, binsProc)
    plt.title('Particle Size Distribution')
    plt.savefig('plot/PSD.png')
    plt.close()

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

def whereAmI():
    """
    This function can be called during a simulation to give information to the user.
    """
    print()
    print("Where am I ?")
    print()
    if 'Current Step' in O.tags.keys():
        print('Iteration',O.iter)
        print(O.tags['Current Step'],'dissolutions done :')
        print('Sample description :')
        print('\tNumber of bonds =', int(counter_bond),'(/)',int(counter_bond0))
        print('\tepsilon_z =', round(100*(upper_plate.state.pos[2]-upper_plate.state.refPos[2])/upper_plate.state.refPos[2],2),'(%)')
        print('\tPorosity =', round(porosity(),3),'(-)')
        print('\tCoordination =', round(avgNumInteractions(),1),'(-)')
    else :
        print('Initialisation')

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

O.run()

Functions

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 unbalancedForce() > .1:
        return
    # increase the radius of particles
    if int(O.tags['Step ic']) < n_steps_ic :
        print('IC step '+O.tags['Step ic']+'/'+str(n_steps_ic)+' done')
        O.tags['Step ic'] = str(int(O.tags['Step ic'])+1)
        i_L_r = 0
        for b in O.bodies :
            if isinstance(b.shape, Sphere):
                growParticle(b.id, int(O.tags['Step ic'])/n_steps_ic*L_r[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
    global L_L_psd_binsSizes, L_L_psd_binsProc
    L_L_psd_binsSizes = []
    L_L_psd_binsProc = []
    binsSizes, binsProc, binsSumCum = psd(bins=10)
    L_L_psd_binsSizes.append(binsSizes)
    L_L_psd_binsProc.append(binsProc)
    plotPSD()
    # characterize the ic algorithm
    global tic
    global iter_0
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    tic = tac
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("IC Generated : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    simulation_report.write(str(O.iter-iter_0)+' Iterations\n')
    simulation_report.write(str(n_grains)+' grains\n\n')
    simulation_report.close()
    print("\nIC Generated : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    # save
    #O.save('save/simu_ic.yade.bz2')
    # next time, do not call this function anymore, but the next one instead
    iter_0 = O.iter
    checker.command = 'checkUnbalanced_load_cementation_ic()'
    checker.iterPeriod = 500
    # control top wall
    O.engines = O.engines + [PyRunner(command='controlTopWall_ic()', iterPeriod = 1)]
    # switch on the gravity
    Newton.gravity = [0, 0, -9.81]
def controlTopWall_ic()

Control the upper wall to applied a defined confinement force.

The displacement of the wall depends on the force difference. A maximum value is defined.
Expand source code

def controlTopWall_ic():
    '''
    Control the upper wall to applied a defined confinement force.

    The displacement of the wall depends on the force difference. A maximum value is defined.
    '''
    Fz = O.forces.f(upper_plate.id)[2]
    if Fz == 0:
        upper_plate.state.pos =  (lateral_plate.state.pos[0]/2, Dy/2, max([b.state.pos[2]+0.99*b.shape.radius for b in O.bodies if isinstance(b.shape, Sphere)]))
    else :
        dF = Fz - P_cementation*lateral_plate.state.pos[0]*Dy
        v_plate_max = rMean*0.00005/O.dt
        v_try_abs = abs(kp*dF)/O.dt
        # maximal speed is applied to top wall
        if v_try_abs < v_plate_max :
            upper_plate.state.vel = (0, 0, np.sign(dF)*v_try_abs)
        else :
            upper_plate.state.vel = (0, 0, np.sign(dF)*v_plate_max)
def checkUnbalanced_load_cementation_ic()

Wait to reach the vertical pressure targetted for cementation.

Expand source code

def checkUnbalanced_load_cementation_ic():
    '''
    Wait to reach the vertical pressure targetted for cementation.
    '''
    addPlotData_cementation_ic()
    saveData_ic()
    # check the force applied
    if abs(O.forces.f(upper_plate.id)[2]-P_cementation*Dx*Dy)/(P_cementation*Dx*Dy) > 0.005:
        return
    if unbalancedForce() > unbalancedForce_criteria :
        return
    # characterize the ic algorithm
    global tic
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    tic = tac
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Pressure (Cementation) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    simulation_report.write(str(n_grains)+' grains\n\n')
    simulation_report.close()
    print("\nPressure (Cementation) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    # switch on friction, bending resistance and twisting resistance between particles
    O.materials[-1].frictionAngle = frictionAngleReal
    O.materials[-1].alphaKr = alphaKrReal
    O.materials[-1].alphaKtw = alphaKtwReal
    # for existing contacts, clear them
    O.interactions.clear()
    # calm down particles
    for b in O.bodies:
        if isinstance(b.shape,Sphere):
            b.state.angVel = Vector3(0,0,0)
            b.state.vel = Vector3(0,0,0)
    # switch off damping
    Newton.damping = 0
    # next time, do not call this function anymore, but the next one instead
    checker.command = 'checkUnbalanced_param_ic()'
def checkUnbalanced_param_ic()

Wait to reach the equilibrium after switching on the friction and the rolling resistances.

Expand source code

def checkUnbalanced_param_ic():
    '''
    Wait to reach the equilibrium after switching on the friction and the rolling resistances.
    '''
    addPlotData_cementation_ic()
    saveData_ic()
    # check the force applied
    if abs(O.forces.f(upper_plate.id)[2]-P_cementation*Dx*Dy)/(P_cementation*Dx*Dy) > 0.005:
        return
    if unbalancedForce() > unbalancedForce_criteria :
        return
    # characterize the ic algorithm
    global tic
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    tic = tac
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Parameters applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n\n")
    simulation_report.close()
    print("\nParameters applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    # save
    #O.save('save/'+O.tags['d.id']+'_ic.yade.bz2')
    # next time, do not call this function anymore, but the next one instead
    checker.command = 'cementation()'
    checker.iterPeriod = 10
def pdf_lognormal(x, m_log, s_log)

Return the probability of a value x with a log normal function defined by the mean m_log and the variance s_log.

Expand source code

def pdf_lognormal(x,m_log,s_log):
    '''
    Return the probability of a value x with a log normal function defined by the mean m_log and the variance s_log.
    '''
    p = np.exp(-(np.log(x)-m_log)**2/(2*s_log**2))/(x*s_log*np.sqrt(2*np.pi))
    return p
def cementation()

Generate cementation between grains.

Expand source code

def cementation():
    '''
    Generate cementation between grains.
    '''
    # generate the list of cohesive surface area and its list of weight
    x_min = 1e2 # µm2
    x_max = 4e4 # µm2
    n_x = 20000
    x_L = np.linspace(x_min, x_max, n_x)
    p_x_L = []
    for x in x_L :
        p_x_L.append(pdf_lognormal(x,m_log,s_log))
    # counter
    global counter_bond, counter_bond0, counter_bond_broken_diss, counter_bond_broken_load
    counter_bond = 0
    counter_bond_broken_diss = 0
    counter_bond_broken_load = 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) :
            # only a fraction of the contact is cemented
            if random.uniform(0,1) < f_cemented :
                counter_bond = counter_bond + 1
                # creation of cohesion
                i.phys.cohesionBroken = False
                # determine the cohesive surface
                cohesiveSurface = random.choices(x_L,p_x_L)[0]*1e-12 # µm2
                # set normal and shear adhesions
                i.phys.normalAdhesion = tensileCohesion*cohesiveSurface
                i.phys.shearAdhesion = shearCohesion*cohesiveSurface
    counter_bond0 = counter_bond
    # write in the report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write(str(counter_bond)+" contacts cemented\n\n")
    simulation_report.close()
    print('\n'+str(counter_bond)+" contacts cemented\n")

    # next time, do not call this function anymore, but the next one instead
    checker.command = 'checkUnbalanced_load_confinement_ic()'
    checker.iterPeriod = 200
    # activate Young reduction
    if considerYoungReduction :
        O.engines = O.engines[:-1] + [PyRunner(command='YoungReduction()', iterPeriod = 100)] + [O.engines[-1]]
    # change the vertical pressure applied
    O.engines = O.engines[:-1] + [PyRunner(command='controlTopWall()', iterPeriod = 1)]
def checkUnbalanced_load_confinement_ic()

Wait to reach the vertical pressure targetted for confinement.

Expand source code

def checkUnbalanced_load_confinement_ic():
    '''
    Wait to reach the vertical pressure targetted for confinement.
    '''
    addPlotData_confinement_ic()
    saveData_ic()
    # check the force applied
    if abs(O.forces.f(upper_plate.id)[2]-P_load*Dx*Dy)/(P_load*Dx*Dy) > 0.005:
        return
    if unbalancedForce() > unbalancedForce_criteria :
        return
    # characterize the ic algorithm
    global tic
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    tic = tac
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Pressure (Confinement) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    simulation_report.write(str(n_grains)+' grains\n\n')
    simulation_report.close()
    print("\nPressure (Confinement) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")

    # next time, do not call this function anymore, but the next one instead
    checker.command = 'checkUnbalanced_load_k0_ic()'
    # control lateral wall
    O.engines = O.engines + [PyRunner(command='controlLateralWall_ic()', iterPeriod = 1, label='k0_checker')]
def controlLateralWall_ic()

Control the lateral wall to applied a defined confinement force.

The displacement of the wall depends on the force difference. A maximum value is defined.
Expand source code

def controlLateralWall_ic():
    '''
    Control the lateral wall to applied a defined confinement force.

    The displacement of the wall depends on the force difference. A maximum value is defined.
    '''
    Fx = O.forces.f(lateral_plate.id)[0]
    if Fx == 0:
        lateral_plate.state.pos =  (max([b.state.pos[0]+0.99*b.shape.radius for b in O.bodies if isinstance(b.shape, Sphere)]), Dy/2, upper_plate.state.pos[2]/2)
    else :
        dF = Fx - k0_target*P_load*upper_plate.state.pos[2]*Dy
        v_plate_max = rMean*0.00002/O.dt
        v_try_abs = abs(kp*dF)/O.dt
        # maximal speed is applied to top wall
        if v_try_abs < v_plate_max :
            lateral_plate.state.vel = (np.sign(dF)*v_try_abs, 0, 0)
        else :
            lateral_plate.state.vel = (np.sign(dF)*v_plate_max, 0, 0)
def DoNotControlLateralWall_ic()

Switch off the control of the lateral wall.

Expand source code

def DoNotControlLateralWall_ic():
    '''
    Switch off the control of the lateral wall.
    '''
    lateral_plate.state.vel = (0,0,0)
    O.engines = O.engines[:-1]
def checkUnbalanced_load_k0_ic()

Wait to reach the earth pressure coefficient targetted.

k0 = s_II/s_I, where s_I is the vertical pressure and s_II is the lateral pressure.
Expand source code

def checkUnbalanced_load_k0_ic():
    '''
    Wait to reach the earth pressure coefficient targetted.

    k0 = s_II/s_I, where s_I is the vertical pressure and s_II is the lateral pressure.
    '''
    addPlotData_confinement_ic()
    saveData_ic()
    # check the force applied
    if abs(O.forces.f(upper_plate.id)[2]  - P_load*lateral_plate.state.pos[0]*Dy)/(P_load*lateral_plate.state.pos[0]*Dy) > 0.005 or\
    abs(O.forces.f(lateral_plate.id)[0] - k0_target*P_load*upper_plate.state.pos[2]*Dy)/(k0_target*P_load*upper_plate.state.pos[2]*Dy) > 0.005:
        return
    if unbalancedForce() > unbalancedForce_criteria :
        return

    # characterize the ic algorithm
    global tic, iter_0
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    tic = tac
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Pressure (k0) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    simulation_report.write('IC generation ends\n')
    simulation_report.write(str(O.iter-iter_0)+' Iterations\n')
    simulation_report.write(str(n_grains)+' grains\n\n')
    simulation_report.close()
    print("\nPressure (k0) applied : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")

    # reset plot (IC done, simulation starts)
    plot.reset()
    # switch off control lateral wall
    O.engines = O.engines[:-1]+[PyRunner(command='DoNotControlLateralWall_ic()', iterPeriod = 1)]
    # save new reference position for upper wall
    upper_plate.state.refPos = upper_plate.state.pos
    # next time, do not call this function anymore, but the next one instead
    iter_0 = O.iter
    checker.command = 'checkUnbalanced()'
    checker.iterPeriod = 500
    # label step
    O.tags['Current Step']='0'
    # trackers
    global L_unbalanced_ite, L_k0_ite, L_confinement_ite, L_count_bond, n_try
    n_try = 0
    L_unbalanced_ite = []
    L_k0_ite = []
    L_confinement_ite = []
    L_count_bond = []
def addPlotData_cementation_ic()

Save data in plot.

Expand source code

def addPlotData_cementation_ic():
    """
    Save data in plot.
    """
    # add forces applied on wall x and z
    sz = O.forces.f(upper_plate.id)[2]/(lateral_plate.state.pos[0]*Dy)
    sx = O.forces.f(lateral_plate.id)[0]/(Dy*upper_plate.state.pos[2])
    # add data
    plot.addData(i=O.iter-iter_0, porosity=porosity(), coordination=avgNumInteractions(), unbalanced=unbalancedForce(),\
                 Sx=sx, Sz=sz, conf_verified=sz/P_cementation*100, n_bond=0,\
                 vert_strain=100*(upper_plate.state.pos[2]-upper_plate.state.refPos[2])/upper_plate.state.refPos[2], lat_strain=100*(lateral_plate.state.pos[0]-lateral_plate.state.refPos[0])/lateral_plate.state.refPos[0])
def addPlotData_confinement_ic()

Save data in plot.

Expand source code

def addPlotData_confinement_ic():
    """
    Save data in plot.
    """
    # add forces applied on wall x and z
    sz = O.forces.f(upper_plate.id)[2]/(lateral_plate.state.pos[0]*Dy)
    sx = O.forces.f(lateral_plate.id)[0]/(Dy*upper_plate.state.pos[2])
    # count the number the bond
    n_bond = 0
    for i in O.interactions:
        if isinstance(O.bodies[i.id1].shape, Sphere) and isinstance(O.bodies[i.id2].shape, Sphere):
            if not i.phys.cohesionBroken :
                n_bond = n_bond + 1
    # add data
    plot.addData(i=O.iter-iter_0, porosity=porosity(), coordination=avgNumInteractions(), unbalanced=unbalancedForce(),\
                 Sx=sx, Sz=sz, conf_verified=sz/P_load*100, n_bond=n_bond,\
                 vert_strain=100*(upper_plate.state.pos[2]-upper_plate.state.refPos[2])/upper_plate.state.refPos[2], lat_strain=100*(lateral_plate.state.pos[0]-lateral_plate.state.refPos[0])/lateral_plate.state.refPos[0])
def saveData_ic()

Save data in .txt file during the ic.

Expand source code

def saveData_ic():
    """
    Save data in .txt file during the ic.
    """
    plot.saveDataTxt('data/IC_'+O.tags['d.id']+'.txt')
    # post-proccess
    L_sigma_x = []
    L_sigma_z = []
    L_confinement = []
    L_coordination = []
    L_unbalanced = []
    L_ite  = []
    L_vert_strain = []
    L_lat_strain = []
    L_porosity = []
    L_n_bond = []
    file = 'data/IC_'+O.tags['d.id']+'.txt'
    data = np.genfromtxt(file, skip_header=1)
    file_read = open(file, 'r')
    lines = file_read.readlines()
    file_read.close()
    if len(lines) >= 3:
        for i in range(len(data)):
            L_sigma_x.append(abs(data[i][0]))
            L_sigma_z.append(abs(data[i][1]))
            L_confinement.append(data[i][2])
            L_coordination.append(data[i][3])
            L_ite.append(data[i][4])
            L_lat_strain.append(data[i][5])
            L_n_bond.append(data[i][6])
            L_porosity.append(data[i][7])
            L_unbalanced.append(data[i][8])
            L_vert_strain.append(data[i][9])

        # plot
        fig, ((ax1, ax2, ax3), (ax4, ax5, ax6)) = plt.subplots(2,3, figsize=(20,10),num=1)

        ax1.plot(L_ite, L_sigma_x, label = r'$\sigma_x$')
        ax1.plot(L_ite, L_sigma_z, label = r'$\sigma_z$')
        ax1.legend()
        ax1.set_title('Stresses (Pa)')

        ax2.plot(L_ite, L_unbalanced, 'b')
        ax2.set_ylabel('Unbalanced (-)', color='b')
        ax2.set_ylim(ymin=0, ymax=2*unbalancedForce_criteria)
        ax2b = ax2.twinx()
        ax2b.plot(L_ite, L_confinement, 'r')
        ax2b.set_ylabel('Confinement (%)', color='r')
        ax2b.set_title('Steady-state indices')

        ax3.plot(L_ite, L_n_bond)
        ax3.set_title('Number of bond (-)')

        ax4.plot(L_ite, L_lat_strain, label=r'$\epsilon_x$ (%)')
        ax4.plot(L_ite, L_vert_strain, label=r'$\epsilon_z$ (%)')
        ax4.legend()
        ax4.set_title('Strains (%)')

        ax5.plot(L_ite, L_porosity)
        ax5.set_title('Porosity (-)')

        ax6.plot(L_ite, L_coordination)
        ax6.set_title('Coordination number (-)')

        plt.savefig('plot/IC_'+O.tags['d.id']+'.png')

        plt.close()
def YoungReduction()

Reduce the Young modulus with the number of bond dissolved.

E = (E0-E1)*f_diss + E1
Expand source code

def YoungReduction():
    '''
    Reduce the Young modulus with the number of bond dissolved.

    E = (E0-E1)*f_diss + E1
    '''
    # count the number of bond dissolved
    global counter_bond, counter_bond_broken_load
    counter_bond = 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) :
            if not i.phys.cohesionBroken :
                counter_bond = counter_bond + 1
    counter_bond_broken_load = (counter_bond0-counter_bond) - counter_bond_broken_diss
    # compute the new Young modulus
    f_bond_diss = (counter_bond_broken_diss+counter_bond_broken_load)/counter_bond0
    NewYoungModulus = (YoungModulus-80e6)*(1-f_bond_diss) + 80e6
    # update material
    for mat in O.materials :
        mat.young = NewYoungModulus
    # update the interactions
    for inter in O.interactions :
        if isinstance(O.bodies[inter.id1].shape, Sphere) and isinstance(O.bodies[inter.id2].shape, Sphere):
            inter.phys.kn = NewYoungModulus*(O.bodies[inter.id1].shape.radius*2*O.bodies[inter.id2].shape.radius*2)/(O.bodies[inter.id1].shape.radius*2+O.bodies[inter.id2].shape.radius*2)
            inter.phys.ks = 0.25*NewYoungModulus*(O.bodies[inter.id1].shape.radius*2*O.bodies[inter.id2].shape.radius*2)/(O.bodies[inter.id1].shape.radius*2+O.bodies[inter.id2].shape.radius*2) # 0.25 is the Poisson ratio
            inter.phys.kr = inter.phys.ks*alphaKrReal*O.bodies[inter.id1].shape.radius*O.bodies[inter.id2].shape.radius
            inter.phys.ktw = inter.phys.ks*alphaKtwReal*O.bodies[inter.id1].shape.radius*O.bodies[inter.id2].shape.radius
        else : # Sphere-Wall contact
            if isinstance(O.bodies[inter.id1].shape, Sphere):
                grain = O.bodies[inter.id1]
            else:
                grain = O.bodies[inter.id2]
            # diameter of the wall is equivalent of the diameter of the sphere
            inter.phys.kn = NewYoungModulus*(grain.shape.radius*2*grain.shape.radius*2)/(grain.shape.radius*2+grain.shape.radius*2)
            inter.phys.ks = 0.25*NewYoungModulus*(grain.shape.radius*2*grain.shape.radius*2)/(grain.shape.radius*2+grain.shape.radius*2) # 0.25 is the Poisson ratio
            # no moment/twist for sphere-wall
    # update time step because the Young modulus change
    O.dt = factor_dt_crit * PWaveTimeStep()
def controlTopWall()

Control the upper wall to applied a defined confinement force.

The displacement of the wall depends on the force difference. A maximum value is defined.
Expand source code

def controlTopWall():
    '''
    Control the upper wall to applied a defined confinement force.

    The displacement of the wall depends on the force difference. A maximum value is defined.
    '''
    Fz = O.forces.f(upper_plate.id)[2]
    if Fz == 0:
        upper_plate.state.pos =  (lateral_plate.state.pos[0]/2, Dy/2, max([b.state.pos[2]+0.99*b.shape.radius for b in O.bodies if isinstance(b.shape, Sphere)]))
    else :
        dF = Fz - P_load*lateral_plate.state.pos[0]*Dy
        v_plate_max = rMean*0.00005/O.dt
        v_try_abs = abs(kp*dF)/O.dt
        # maximal speed is applied to top wall
        if v_try_abs < v_plate_max :
            upper_plate.state.vel = (0, 0, np.sign(dF)*v_try_abs)
        else :
            upper_plate.state.vel = (0, 0, np.sign(dF)*v_plate_max)
def count_bond()

Count the number of bond.

Expand source code

def count_bond():
    '''
    Count the number of bond.
    '''
    counter_bond = 0
    for i in O.interactions:
        if isinstance(O.bodies[i.id1].shape, Sphere) and isinstance(O.bodies[i.id2].shape, Sphere):
            if not i.phys.cohesionBroken :
                counter_bond = counter_bond + 1
    return counter_bond
def checkUnbalanced()

Look for the equilibrium during the loading phase.

Expand source code

def checkUnbalanced():
    """
    Look for the equilibrium during the loading phase.
    """
    global L_unbalanced_ite, L_k0_ite, L_confinement_ite, L_count_bond, n_try
    # count the number of tries
    n_try = n_try + 1
    # track and plot unbalanced
    L_unbalanced_ite.append(unbalancedForce())
    if O.forces.f(upper_plate.id)[2] != 0:
        k0 = abs(O.forces.f(lateral_plate.id)[0]/(upper_plate.state.pos[2]*Dy)*(lateral_plate.state.pos[0]*Dy)/O.forces.f(upper_plate.id)[2])
    else :
        k0 = 0
    L_k0_ite.append(k0)
    L_confinement_ite.append(O.forces.f(upper_plate.id)[2]/(P_load*lateral_plate.state.pos[0]*Dy)*100)
    L_count_bond.append(count_bond())

    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2,2, figsize=(16,9),num=1)

    ax1.plot(L_unbalanced_ite)
    ax1.set_title('unbalanced force (-)')

    ax2.plot(L_k0_ite)
    ax2.set_title(r'$k_0$ (-)')

    ax3.plot(L_confinement_ite)
    ax3.set_title('confinement (%)')

    ax4.plot(L_count_bond)
    ax4.set_title('Number of bond (-)')

    fig.savefig('plot/tracking_ite.png')
    plt.close()

    # verify confinement pressure applied
    if not abs(O.forces.f(upper_plate.id)[2]-P_load*lateral_plate.state.pos[0]*Dy) < 0.005*P_load*lateral_plate.state.pos[0]*Dy:
        return
    # verify unbalanced force criteria
    # a limit is set for number of tries
    if unbalancedForce() < unbalancedForce_criteria or n_try > 30:

        # reset trackers
        n_try = 0
        L_unbalanced_ite = []
        L_k0_ite = []
        L_confinement_ite = []
        L_count_bond = []

        if counter_bond0*f_n_bond_stop < counter_bond:
            dissolve()
        else :
            stopLoad()
def dissolve()

Dissolve bond with a constant surface reduction.

Expand source code

def dissolve():
    """
    Dissolve bond with a constant surface reduction.
    """
    O.tags['Current Step'] = str(int(O.tags['Current Step'])+1)
    # save at the end
    saveData()
    # count the number of bond
    global counter_bond, counter_bond_broken_diss, s_bond_diss
    counter_bond = 0
    counter_bond_broken = 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) :
            if not i.phys.cohesionBroken :
                counter_bond = counter_bond + 1
                # set normal and shear adhesions
                if (counter_bond_broken_diss+counter_bond_broken_load)/counter_bond0 < diss_level_1_2 :
                    i.phys.normalAdhesion = i.phys.normalAdhesion - tensileCohesion*dSc_dissolved_1
                    i.phys.shearAdhesion = i.phys.shearAdhesion - shearCohesion*dSc_dissolved_1
                else :
                    i.phys.normalAdhesion = i.phys.normalAdhesion - tensileCohesion*dSc_dissolved_2
                    i.phys.shearAdhesion = i.phys.shearAdhesion - shearCohesion*dSc_dissolved_2
                if i.phys.normalAdhesion <= 0 or i.phys.shearAdhesion <=0 :
                    # bond brokes
                    counter_bond = counter_bond - 1
                    counter_bond_broken = counter_bond_broken + 1
                    i.phys.cohesionBroken = True
                    i.phys.normalAdhesion = 0
                    i.phys.shearAdhesion = 0
    # update bond surface dissolved tracker
    if (counter_bond_broken_diss+counter_bond_broken_load)/counter_bond0 < diss_level_1_2 :
        s_bond_diss = s_bond_diss + dSc_dissolved_1
    else :
        s_bond_diss = s_bond_diss + dSc_dissolved_2
    # update the counter of bond dissolved during the dissolution step
    counter_bond_broken_diss = counter_bond_broken_diss + counter_bond_broken
def stopLoad()

Close simulation.

Expand source code

def stopLoad():
    """
    Close simulation.
    """
    # save at the converged iteration
    saveData()
    # close yade
    O.pause()
    # characterize the dem step
    tac = time.perf_counter()
    hours = (tac-tic)//(60*60)
    minutes = (tac-tic -hours*60*60)//(60)
    seconds = int(tac-tic -hours*60*60 -minutes*60)
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Oedometric test : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")
    simulation_report.close()
    print("Oedometric test : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds")
    # characterize the last DEM step and the simulation
    hours = (tac-tic_0)//(60*60)
    minutes = (tac-tic_0 -hours*60*60)//(60)
    seconds = int(tac-tic_0 -hours*60*60 -minutes*60)
    # report
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("Simulation time : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n\n")
    simulation_report.close()
    print("\nSimulation time : "+str(hours)+" hours "+str(minutes)+" minutes "+str(seconds)+" seconds\n")

    # give final values
    simulation_report = open(simulation_report_name, 'a')
    simulation_report.write("k0 initial: "+str(round(k0_target,3))+" / k0 final: "+str(round(abs(O.forces.f(lateral_plate.id)[0]/(upper_plate.state.pos[2]*Dy)*(lateral_plate.state.pos[0]*Dy)/O.forces.f(upper_plate.id)[2]),3))+"\n")
    simulation_report.write(str(int(counter_bond_broken_diss))+" bonds broken during dissolution steps ("+str(int(counter_bond_broken_diss/counter_bond0*100))+" % of initial number of bonds)\n")
    simulation_report.write(str(int(counter_bond_broken_load))+" bonds broken during loading steps ("+str(int(counter_bond_broken_load/counter_bond0*100))+" % of initial number of bonds)\n")
    simulation_report.close()
    print("k0 initial:",round(k0_target,3)," / k0 final:",round(abs(O.forces.f(lateral_plate.id)[0]/(upper_plate.state.pos[2]*Dy)*(lateral_plate.state.pos[0]*Dy)/O.forces.f(upper_plate.id)[2]),3))
    print(int(counter_bond_broken_diss),"bonds broken during dissolution steps ("+str(int(counter_bond_broken_diss/counter_bond0*100)),"% of initial number of bonds)")
    print(int(counter_bond_broken_load),"bonds broken during loading steps ("+str(int(counter_bond_broken_load/counter_bond0*100)),"% of initial number of bonds)")

    # save simulation
    os.mkdir('../AcidOedo_Rock_data/'+O.tags['d.id'])
    shutil.copytree('data','../AcidOedo_Rock_data/'+O.tags['d.id']+'/data')
    shutil.copytree('plot','../AcidOedo_Rock_data/'+O.tags['d.id']+'/plot')
    shutil.copytree('save','../AcidOedo_Rock_data/'+O.tags['d.id']+'/save')
    shutil.copy('AcidOedo_Rock.py','../AcidOedo_Rock_data/'+O.tags['d.id']+'/AcidOedo_Rock.py')
    shutil.copy(O.tags['d.id']+'_report.txt','../AcidOedo_Rock_data/'+O.tags['d.id']+'/'+O.tags['d.id']+'_report.txt')
def addPlotData()

Save data in plot.

Expand source code

def addPlotData():
    """
    Save data in plot.
    """
    # add forces applied on wall x and z
    sz = O.forces.f(upper_plate.id)[2]/(Dy*lateral_plate.state.pos[0])
    sx = O.forces.f(lateral_plate.id)[0]/(Dy*upper_plate.state.pos[2])
    # compute the k0 = sigma_x/sigma_z, = 0 if no sigma_z
    if sz != 0:
        k0 = abs(sx/sz)
    else :
        k0 = 0
    # add data
    plot.addData(i=O.iter-iter_0, porosity=porosity(), coordination=avgNumInteractions(), unbalanced=unbalancedForce(), \
                counter_bond=counter_bond, counter_bond_broken_diss=counter_bond_broken_diss, counter_bond_broken_load=counter_bond_broken_load,\
                Sx=sx, Sz=sz, Z_plate=upper_plate.state.pos[2], conf_verified=sz/P_load*100, k0=k0,\
                w=upper_plate.state.pos[2]-upper_plate.state.refPos[2], vert_strain=100*(upper_plate.state.pos[2]-upper_plate.state.refPos[2])/upper_plate.state.refPos[2],
                s_bond_diss=s_bond_diss)
def saveData()

Save data in .txt file during the steps.

Expand source code

def saveData():
    """
    Save data in .txt file during the steps.
    """
    addPlotData()
    plot.saveDataTxt('data/'+O.tags['d.id']+'.txt')
    # post-proccess
    L_s_bond_diss = []
    L_k0 = []
    L_counter_bond = []
    L_counter_bond_broken_diss = []
    L_counter_bond_broken_load = []
    L_vert_strain = []
    L_porosity = []
    L_coordination = []
    file = 'data/'+O.tags['d.id']+'.txt'
    data = np.genfromtxt(file, skip_header=1)
    file_read = open(file, 'r')
    lines = file_read.readlines()
    file_read.close()
    if len(lines) >= 3:
        for i in range(len(data)):
            L_coordination.append(data[i][4])
            L_counter_bond.append(data[i][5])
            L_counter_bond_broken_diss.append(data[i][6])
            L_counter_bond_broken_load.append(data[i][7])
            L_k0.append(data[i][9])
            L_porosity.append(data[i][10])
            L_s_bond_diss.append(data[i][11])
            L_vert_strain.append(data[i][13])

        # plot
        fig, ((ax1, ax2, ax3), (ax4, ax5, ax6)) = plt.subplots(2,3, figsize=(16,9),num=1)

        ax1.plot(L_s_bond_diss, L_k0)
        ax1.set_title(r'$k_0$ (-)')

        ax2.plot(L_s_bond_diss, L_counter_bond)
        ax2.set_title('Number of bond (-)')

        ax3.plot(L_s_bond_diss, L_counter_bond_broken_diss, label='during dissolution')
        ax3.plot(L_s_bond_diss, L_counter_bond_broken_load, label='during loading')
        ax3.set_title('Number of bonds broken (-)')
        ax3.legend()

        ax4.plot(L_s_bond_diss, L_vert_strain)
        ax4.set_title(r'$\epsilon_z$ (%)')

        ax5.plot(L_s_bond_diss, L_porosity)
        ax5.set_title('Porosity (-)')

        ax6.plot(L_s_bond_diss, L_coordination)
        ax6.set_title('Coordination (-)')

        plt.suptitle(r'Trackers - bond surface reduction (m$^2$)')
        plt.savefig('plot/'+O.tags['d.id']+'.png')
        plt.close()
def plotPSD()

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

Expand source code

def plotPSD():
    """
    This function can be called to plot the evolution of the psd.
    """
    plt.figure(1, figsize=(16,9))
    for i_psd in range(len(L_L_psd_binsSizes)):
        binsSizes = L_L_psd_binsSizes[i_psd]
        binsProc = L_L_psd_binsProc[i_psd]
        plt.plot(binsSizes, binsProc)
    plt.title('Particle Size Distribution')
    plt.savefig('plot/PSD.png')
    plt.close()
def whereAmI()

This function can be called during a simulation to give information to the user.

Expand source code

def whereAmI():
    """
    This function can be called during a simulation to give information to the user.
    """
    print()
    print("Where am I ?")
    print()
    if 'Current Step' in O.tags.keys():
        print('Iteration',O.iter)
        print(O.tags['Current Step'],'dissolutions done :')
        print('Sample description :')
        print('\tNumber of bonds =', int(counter_bond),'(/)',int(counter_bond0))
        print('\tepsilon_z =', round(100*(upper_plate.state.pos[2]-upper_plate.state.refPos[2])/upper_plate.state.refPos[2],2),'(%)')
        print('\tPorosity =', round(porosity(),3),'(-)')
        print('\tCoordination =', round(avgNumInteractions(),1),'(-)')
    else :
        print('Initialisation')