# Fragmentation Analysis during a Uniaxial Compression with Crack Tracking Using Fractures

Problem Statement

Note

The project file for this example may be viewed/run in PFC.[1] The data files used are shown at the end of this example.

A simple bonded assembly is generated, then brought to failure under uniaxial, unconfined compression. The purpose of this example is to illustrate how fragmentation may be tracked using built-in PFC tools, not to detail the process of building a bonded assembly. For the latter, please refer to the tutorial “Generating a Bonded Assembly” and to the material-modeling support package.

PFC3D Model

A PFC3D bonded assembly is first generated using an approach similar to the one detailed in the “Generating a Bonded Assembly” section. The initial state before compression is shown in Figure 1. In this view, the contacts are colored by the value of the property pb_state (a value of 3 means that parallel bonds are installed) and translated for convenience.

Figure 1: The initial state. Contacts colored by pb_state are translated for convenience.

To perform uniaxial compression, the assembly is confined between top and bottom wall platens (which are assigned equal and opposite velocities along the $$z$$-direction), and the model is cycled to a target age limit. The magnitude of the forces exerted by the specimen on the platens is monitored via two histories as a measure of the compressive stress applied to the specimen.

The compression is performed three times with different settings.

• CASE 0 corresponds to a regular run.
• For CASE 1, the fragment logic is activated and the ball results command is used to save partial state information every 0.1 [time-unit] during the course of the simulation.
• For CASE 2, in addition to the use of the fragment logic and the ball results command, a FISH function is registered with the linear parallel bond bond_break callback event, which monitors bond breakage events and creates fractures using the Discrete Fracture Network logic to store their location, size, and orientation. Additionally, the fragments are computed only if a bond breakage event occurred, and ball result states are stored only if the number of fragments in the system changed. The FISH functions used to set up this crack-tracking environment are implemented in “fracture.fis”.

Model Results

All three cases produce the same results. The final state for CASE 2 is shown in Figure 2, where contacts and fractures are translated to the right-hand side for convenience. A combination of tensile and shear failures occurred in selected bonded contacts and resulted in the macroscopic failure of the specimen. Forces applied on the platens are identical in magnitude, as shown in Figure 3, and demonstrate the brittleness of the specimen.

Figure 2: State of the system after loading (CASE 3). Right: balls (colored by fragment index). Left: contacts (colored by pb_state) and fractures (colored by shear/tension failure modes) translated for convenience.

Figure 3: Forces (magnitude) measured on the platens vs. time.

The ability to color the balls by their fragment index is a result of the activation of the fragment logic. This logic embeds additional utilities (please refer to the “Fragment” section for a detailed description). For instance, Figure 4 shows the evolution of the volume of the largest fragment (which consists of the whole assembly at the initial state) relative to its initial value, monitored during the course of the simulation.

Figure 4: Evolution of the largest fragment volume relative to its initial value.

For CASE 2 and CASE 3, ball configurations and fragment indexes have been saved intermittently during the course of the simulation. Animations can therefore be created using the FISH functions make_movie_case1 (for CASE 1) and make_movie_case2 (for CASE 2). This is an illustration of how the fragment logic and the ball results command can be used together to allow in-depth analysis of the results. Note, however, the costs in terms of memory usage of these two utilities: the sizes of the final model state (SAV) files for CASE 1 and CASE 2 are larger than for CASE 0 (more than 10 times larger for CASE 1). Using custom functions to store model results as performed in CASE 2 can save memory; alternatively, result configurations can be saved as binary files (instead of being stored in RAM) to maintain the memory-usage of the model at a reasonable level.

Conclusion

This example illustrates the steps that may be taken in order to perform a uniaxial compression test on a bonded-particle model with PFC. Moreover, this example describes how the Fragment and Discrete Fracture Network logic can be used, in combination with the ball results command, to track fragmentation and intermittently store pertinent information during the course of the simulation for reuse in a later post-processing step.

fragment.dat

; fname: fragment.dat
;
; Illustrate how the Fragment, DFN and Model Result logic can be used
; to study the fragmentation of a Bonded Particle Model.
;
;=============================================================================
model new
model large-strain on
model precision 15
model title 'Fragmentation of a Bonded Particle Model'

;-----------------------------------------------------------------------------
; --------------- INITIAL PHASE: CREATION OF A BONDED SPECIMEN ---------------
;-----------------------------------------------------------------------------

model domain extent -5 5 -5 5 -7.5 7.5 condition destroy
contact cmat default model linear property kn 1e7
contact cmat default type ball-ball model linearpbond ...
property kn 5e6 proximity 0.1

wall generate box -2.5 2.5 -2.5 2.5 -5 5
model random 10001
ball distribute porosity 0.3 radius 0.25 0.3 box -2.5 2.5 -2.5 2.5 -5 5
ball attribute density 1.0e3 damp 0.7
model cycle 2000 calm 10
model solve calm 1000 ratio-average 5e-3

contact method bond gap 0.1
contact property pb_kn 1e7 pb_ks 1e7 pb_ten 4e5 pb_coh 4e5 pb_fa 20.0

wall delete
contact cmat default type ball-facet model linear ...
property kn 1e7 ks 1e7 fric 0.5

wall generate id 1 name 'top' polygon -4.0 -4.0 5.0 ...
-4.0  4.0 5.0 ...
4.0  4.0 5.0 ...
4.0 -4.0 5.0

wall generate id 2 name 'bottom' polygon -4.0 -4.0 -5.0 ...
-4.0  4.0 -5.0 ...
4.0  4.0 -5.0 ...
4.0 -4.0 -5.0

wall attribute rotation-center 0.0 0.0  5.0 range position-z  5.0
wall attribute rotation-center 0.0 0.0 -5.0 range position-z -5.0
model clean
model cycle 10
model solve elastic ratio-average 5e-3
wall attribute velocity-z 0.0 gradient 0.0 0.0 -0.001
model history name '1' mechanical time-total
wall history name '2' force-contact id 1
wall history name '3' force-contact id 2
model save 'initial'

;-----------------------------------------------------------------------------
; ------------ CASE 0: NO TRACKING OF FRAGMENTS - NO BALL RESULTS ------------
;-----------------------------------------------------------------------------
model solve time 50.0
model save 'final-case0'

;-----------------------------------------------------------------------------
; ------------ CASE 1: CONTINUOUS UPDATE OF FRAGMENTS WITH BALL RESULTS ------
;-----------------------------------------------------------------------------
model restore 'initial'

fragment register ball-ball
fragment activate time 0.1
model mechanical time-total 0.0
model result interval mechanical 0.1
model solve time 50.0
model save 'final-case1'

;program call post_treat.fis
;[make_movie_case1]

;-----------------------------------------------------------------------------
; ------------ CASE 2: CUSTOM UPDATE OF FRAGMENTS AND BALL RESULTS -----------
;-----------------------------------------------------------------------------
model restore 'initial'

program call 'fracture.fis'
[track_init]
model solve time 50.0
[track_end]
model save 'final-case2'

;program call post_treat.fis
;[make_movie_case2]

program return
;=============================================================================
; eof: fragment.dat


fracture.fis

; fname: fracture.fis
;
; Simple environment to track fragmentation in a BPM.
; Monitor LinearPBond model "bond_break" events and turn them into fractures.
; Use the Fragment and Ball Result logic to record fragment IDs
;
;=============================================================================

local contact    = entries(1)
local mode       = entries(2)
local strength   = entries(3)
local frac_pos   = contact.pos(contact)
local norm       = contact.normal(contact)
local dfn_label  = 'crack'
local frac_dip
local frac_dipdir

local len2 = math.sqrt(comp.x(norm)*comp.x(norm)+comp.y(norm)*comp.y(norm))
if len2 = 0
len2 = 1
endif
local len3 = math.sqrt(len2*len2+comp.z(norm)*comp.z(norm))
if len3 = 0
len3 = 1
endif
if frac_dip<0.0
frac_dip = -frac_dip
frac_dipdir = frac_dipdir + 180.0
endif
if frac_dip>90.0
frac_dip = 180.0-frac_dip
frac_dipdir = frac_dipdir + 180.0
endif
if frac_dipdir<0.0
frac_dipdir = frac_dipdir + 360.0
endif
if frac_dipdir > 360.0
frac_dipdir = frac_dipdir - 360.0
endif
if mode = 1 then
; failed in tension
dfn_label = dfn_label + '_tension'
else if mode = 2 then
; failed in shear
dfn_label = dfn_label + '_shear'
endif
local arg = array.create(5)
arg(1) = 'disk'
arg(2) = frac_pos
arg(3) = frac_size
arg(4) = frac_dip
arg(5) = frac_dipdir

global dfn = dfn.find(dfn_label)
if dfn = null then
dfn = dfn.create(dfn_label)
endif
local fnew = fracture.create(dfn,arg)
fracture.prop(fnew,'failure_strength') = strength
fracture.prop(fnew,'age')  = mech.time.total
end

fish define track_fname
track_fcount += 1
track_fname = string.build('frag%1.p3result',track_fcount)
end

fish define track_init
command
fracture delete
model result clear-map
fragment clear
fragment register ball-ball
endcommand
; activate fishcalls
command
fish callback remove add_crack event bond_break
fish callback remove monitor 100.0
endcommand
; reset global variables
global track_fcount = 0
command
fragment compute
model result export [track_fname]
endcommand
global track_map = map(mech.time.total,fragment.num(1))
end

fish define monitor
local numfrag = fragment.num()
command
fragment compute
endcommand
if fragment.num() # numfrag then
command
model result export [track_fname]
endcommand
endif
endif
end

fish define make_table
local frag1 = fragment.find(1)
local tab = table.create('volfrag1')
loop foreach local value map.keys(track_map)
table(tab,value) = ...
fragment.vol(frag1,fragment.catalog.num(value)) / fragment.vol(frag1,1)
endloop
end

fish define track_end
local numfrag = fragment.num()
command
fragment compute
endcommand
if fragment.num() # numfrag then
command
model result export [track_fname]
endcommand
endif
make_table
end

;=============================================================================
; eof: fracture.p3fis


post_treat.fis

fish define make_movie_case1
command
set echo off
ball result map [track_map]
endcommand
local count = 1
loop foreach local value track_map
global fname = string.build('fragments_case1_%1.png',count)
command
domain extent -10 10
ball result load [count] nothrow on
wall result load [count] nothrow on
plot current view 'the system'
plot bitmap filename [fname]
endcommand
count += 1
endloop
command
set echo on
endcommand
end

fish define make_movie_case2
local keys = map.keys(track_map)
local count = 1
command
set echo off
endcommand
loop foreach value keys
local fname = string.build('fragments_case2_%1.png',count)
command
domain extent -10 10
ball result load [count] nothrow on
wall result load [count] nothrow on
plot current view 'Fragments and Cracks'
plot mod 4 dfn colorby numericproperty "age" coloropt scaled ...
ramp max [value] above none map translate 10 0 0
plot mod 5 table xaxis max [value]
plot bitmap filename [fname]
endcommand
count += 1
endloop
command
set echo on
endcommand
end


Endnotes

 [1] To view this project in PFC, use the program menu. Help ▼ Examples…   ⮡   PFC     ⮡   Examples       ⮡   Fragmentation         ⮡   Fragment.prj