# Comparisons with PFC3D

## Contents |

## Introduction

This page gives comparisons of CPU times between YADE and Itasca's PFC3D, kindly reported by Christian Jakob.

## Model setup

The model for determining calculation speed consists of a regular cubic assembly of particles and a slight angular planar wall underneath.

A linear contact model (without viscous damping) with stiffness of 10^{6} for the particles and 10^{8} for the walls was chosen. The friction angle is 26,6° (= friction coefficient 0,5) for the wall and the particles. The density of the particles was set to 1000 [kg/m^{3}]. Gravity acts in negative z-direction with 9,81 [m/s^{2}]. A local damping constant of 0,7 was set. The time step was kept constant to 10^{ − 3} [s] during calculations.

## Realization of speed test

To make the results comparably the calculations were performed without graphical user interface. The tests were performed on an Intel Xeon X5460 3,16 GHz (only one core was used). Also no other calculations were performed during the speed test. The same geometries, parameters and model specifications were used for both programs.

## Results

The speed test was done with 10x10x10 = 1000 spheres, 20x20x20 = 8000 spheres, ... , 60x60x60 = 216000 spheres. All calculations were performed 20 times with 1000 steps. The average values of the calculation times (in seconds) and the calculation speed (in steps/second) are shown in the following table. Also the relative differences of the calculation times are included (positive, when YADE needed less calc. times).

model dimensions -- number of spheres | calc. time PFC in [s] | calc. time YADE in [s] | steps/second PFC | steps/second YADE | difference in [%] |
---|---|---|---|---|---|

10x10x10 -- 1000 | 0,97 | 1,06 | 1034 | 946,4 | -9 |

20x20x20 -- 8000 | 29,5 | 30,3 | 33,8 | 33 | -2,8 |

30x30x30 -- 27000 | 113,1 | 107,1 | 8,8 | 9,3 | 5,3 |

40x40x40 -- 64000 | 284 | 279 | 3,5 | 3,6 | 1,7 |

50x50x50 -- 125000 | 594 | 567 | 1,7 | 1,77 | 4,6 |

60x60x60 -- 216000 | 1130 | 1007 | 0,89 | 0,99 | 10,9 |

**Calculation times:**

**Calculation speed (semilogy):**

## Discussion

For 1000 spheres the calc. times with PFC are 9% lower than with YADE. For 8000 to 125000 spheres calc. times are nearly the same for both programs. With a high number of particles YADE is up to 10% faster than PFC.

## Scripts

**The script for Yade:**

```
```

```
```#!/usr/bin/python
# -*- coding: utf-8 -*-

num_balls1D = 20
num_balls = num_balls1D*num_balls1D*num_balls1D
logfilename = 'time-and-speed%iballs.out' % (int(num_balls))
origin_wall = num_balls1D/2
x_pos = 0
y_pos = 0
z_pos = 0
friction=0.5
angle=atan(friction)

id_WallMat=O.materials.append(ViscElMat(kn=1e8,ks=1e8,cn=0.0,cs=0.0,density=1000,frictionAngle=angle))
id_SphereMat=O.materials.append(ViscElMat(kn=1e6,ks=1e6,cn=0.0,cs=0.0,density=1000,frictionAngle=angle))

WallMat=O.materials[id_WallMat]
SphereMat=O.materials[id_SphereMat]

O.engines=[
ForceResetter(),
InsertionSortCollider([Bo1_Sphere_Aabb(),Bo1_Box_Aabb()]),
InteractionLoop(
[Ig2_Sphere_Sphere_ScGeom(),Ig2_Box_Sphere_ScGeom()],
[Ip2_ViscElMat_ViscElMat_ViscElPhys()],
[Law2_ScGeom_ViscElPhys_Basic()],
),
NewtonIntegrator(damping=0.7,gravity=(0,0,-9.81)),
]

#rotation quaternion:
orientationWall = Quaternion(Vector3(.01,.01,1),math.pi)
#create box:
id_box=O.bodies.append(utils.box((origin_wall,origin_wall,-.5),(200,200,.5),orientationWall,fixed=True,material=WallMat))

for ii in range(1,num_balls1D+1):
x_pos = x_pos + 1
y_pos = 0
z_pos = 0
for jj in range(1,num_balls1D+1):
y_pos = y_pos + 1
z_pos = 0
for kk in range(1,num_balls1D+1):
z_pos = z_pos + 1
O.bodies.append(utils.sphere([x_pos,y_pos,z_pos], material=SphereMat, radius=0.5,))

O.dt=1e-3

O.saveTmp()

#### run 20 times 1000 steps to determine average calc. time and calc. speed

sum_time = 0.0
sum_speed = 0.0
for ii in range(1,21):

if ii != 1:
O.loadTmp()
O.run(100,True) #to get sure, that initial steps do not influence the calculation time measurement
start_time = O.realtime
O.run(1000,True)
stop_time = O.realtime
calc_time_sec = stop_time-start_time
calc_speed = 1000/calc_time_sec
sum_time = sum_time + calc_time_sec
sum_speed = sum_speed + calc_speed

print ('------------------- run %i of 20 -------------------'% ii)
print calc_time_sec

av_speed = sum_speed/20
av_time = sum_time/20

#write output file
f = open(logfilename, 'w')
f.write('av. time: %e / av. speed: %e'% (av_time, av_speed))
f.close()

```
```

**The script for PFC:**```
```

```
```new
call %fist%\2d_3d\fishcall.fis

set echo off
set notice off

def pre_settings
num_balls1D = 20
num_balls = num_balls1D*num_balls1D*num_balls1D
logfilename = 'time-and-speed'+string(num_balls)+'balls.out'
helpfilename = 'help.out'
origin_wall = num_balls1D/2
c_comp_time = 0
c_write = 0
id_counter = 0
sum_time = 0
sum_speed = 0
x_pos = 0
y_pos = 0
z_pos = 0
array help(3)
end

pre_settings
def write_help_file
help(1) = c_write
help(2) = sum_time
help(3) = sum_speed
status=open(helpfilename,1,0)
status=write(help,3)
status=close
end

write_help_file

wall id 1 kn 1e8 ks 1e8 friction 0.5 normal 0.02 0.02 1 origin origin_wall origin_wall 0

def generate_balls
loop ii (1,num_balls1D)
x_pos = x_pos + 1
y_pos = 0
z_pos = 0
loop jj (1,num_balls1D)
y_pos = y_pos + 1
z_pos = 0
loop kk (1,num_balls1D)
z_pos = z_pos + 1
id_counter = id_counter + 1
command
ball id id_counter rad 0.5 x x_pos y y_pos z z_pos density 1000
property kn 1e6 ks 1e6 friction 0.5 range id id_counter
end_command
end_loop
end_loop
end_loop
end

generate_balls

set grav 0 0 -9.81
set dt 1e-3

def get_computer_time
c_comp_time = c_comp_time + 1
comp_time_now = clock/100.0
if c_comp_time = 1
comp_time_start = comp_time_now
else
comp_time_end = comp_time_now
calc_time_sec = comp_time_end - comp_time_start
calc_time_min = calc_time_sec/60.0
calc_time_hour = calc_time_min/60.0
end_if
end

def lets_go
command
get_computer_time
cycle 1000
get_computer_time
end_command

status=open(helpfilename,0,0)
status=read(help,3)
c_write = help(1)
sum_time = help(2)
sum_speed = help(3)
status=close

c_write = c_write + 1
calc_speed = 1000/calc_time_sec
sum_time = sum_time + calc_time_sec
sum_speed = sum_speed + calc_speed
help(1) = c_write
help(2) = sum_time
help(3) = sum_speed
status=open(helpfilename,1,0)
status=write(help,3)
status=close
oo = out('------------------- run '+string(c_write)+' of 20 -------------------')
end

cycle 100

save model-speed-test-start-condition.sav

;#### run 20 times 1000 steps to determine average calc. time and calc. speed

lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go ;run 5

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go ;run 10

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go ;run 15

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go

restore model-speed-test-start-condition.sav
lets_go ;run 20

def write_output
av_speed = sum_speed/c_write
av_time = sum_time/c_write
help(1)='av. time: '+string(av_time)+' / av. speed: '+string(av_speed)
status=open(logfilename,1,1)
status=write(help,1)
status=close
end

write_output

```
```