File: bodytext.tex

package info (click to toggle)
esys-particle 2.3.5%2Bdfsg2-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 13,132 kB
  • sloc: cpp: 81,480; python: 5,872; makefile: 1,259; sh: 313; perl: 225
file content (2159 lines) | stat: -rw-r--r-- 185,874 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
\chapter[Introduction]{Introduction to the ESyS-Particle DEM simulation software}


The Discrete Element Method (DEM; Cundall and Strack~1979) is a popular numerical method for simulating the dynamics of brittle-elastic or granular materials. Materials are represented as assemblies of spherical particles, each of which may interact with neighbouring particles or other objects (such as planar walls) via simplified force-displacement interaction laws. The numerical solution involves computing the net force acting on each particle at a given time, then updating particle velocities and positions via an explicit finite difference integration scheme. Depending on the application of interest, many thousands (or even millions) of particles may be required and simulations may consist of up to millions of timesteps. The heavy computational burden of the DEM relative to other numerical methods is often the single most limiting factor determining the quality and utility of simulation results. 

ESyS-Particle is an Open Source implementation of the DEM designed for execution on multi-core Personal Computers (PCs), clusters or supercomputers running Linux-based operating systems. A modular, object-oriented DEM simulation engine written in C++ comprises the core of the software. Spatial domain decomposition is implemented using a master-slave strategy with inter-process communications using the Message Passing Interface (MPI) 1.0 standard. A verlet list neighbour search algorithm is implemented for detecting neighbouring particles and a variety of interaction laws are implemented for bonded or unbonded interactions between particles. Particles may have up to three translational and three rotational degrees of freedom as well as thermal properties. An explicit first-order finite difference time integration scheme is employed. Provision is made for file storage of both the entire model state or specific field variables during simulations.

Since the applications for the DEM are broad and varied, ESyS-Particle provides a simple Application Programming Interface (API) allowing users to design simulations via scripts written in the \link{Python}{http://www.python.org} programming language. For numerous applications, there is no need to modify the C++ simulation engine or re-compile the software. The Python API allows users to
\begin{itemize}
\item specify the initial locations and properties of particles and walls, 
\item define the types of interactions acting on these objects,
\item select the types and frequency of data output during simulations, and
\item perform user-defined computations at regular intervals via \texttt{Runnable} modules.
\end{itemize}

This User's Guide provides a tutorial-based introduction to DEM modelling using ESyS-Particle. The focus is on usage of the Python API to design and execute DEM simulations. In later chapters of the tutorial, instruction is provided on the use of post-processing tools packaged with ESyS-Particle, as well as third-party software or libraries for simulation construction and post-analysis. New users are encouraged to install ESyS-Particle on a PC and work through the following chapters consecutively. Additional features and tools are introduced gradually in the context of a number of common applications for the DEM, namely:
\begin{itemize}
\item ideal gas dynamics involving collisions between indivisible particles,
\item gravitational acceleration of individual particles or bonded groups of particles,
\item sandpiles and landslides,
\item hopper (or silo) flow, 
\item brittle failure of solids under uniaxial compression, and
\item shear of granular media within an annular shear cell apparatus.
\end{itemize}

\vskip 5mm
\begin{minipage}{5.75in}
\emph{\textbf{DISCLAIMER:} The simulation scripts described in this Guide and provided in the appendices are for instructional purposes only. These scripts are not considered production-ready for applied numerical modelling, and parameter values used may not lie in physical ranges for the application areas discussed. No warranty or guarantee is given by the authors as to the utility of these scripts for research and development purposes. It is the responsibility of the user to ensure ESyS-Particle simulations are verifiable and validated for any particular application.}
\end{minipage}
\vskip 5mm

\chapter{Simple Models}
\section{A simple simulation: collision of two particles}

This section introduces the basic features of ESyS-Particle simulation scripts via a simple example: collision of two indivisible particles. The simulation consists of two particles of differing mass whose initial velocities are selected to ensure that the particles will collide. This example serves to illustrate the main components of any ESyS-Particle simulation script, namely:

\begin{itemize}
   \item Initialisation of an ESyS-Particle simulation object
   \item Specification of the spatial domain
   \item Particle creation and initialisation
   \item Definition of inter-particle interactions
   \item Execution of time integration
\end{itemize}

The complete code-listing for this example is provided in Appendix~\ref{code:bingle}, entitled \texttt{bingle.py}\footnote{``Bingle'' is slang for a minor car crash or collision. Given our simulation setup, it seemed an appropriate name.}. It is recommended that you work through each section below, copying the code fragments as you go rather than simply copying the complete script. Code-fragments are identifiable by the \texttt{teletext font}. Shell commands will be prepended with the \$ symbol.

\subsection{Initialisation of an ESyS-Particle simulation}

In order to use the ESyS-Particle simulation libraries in Python, we must first import the modules we wish to use. For this first example, only the following two import statements are required:

\begin{verbatim}
from esys.lsm import *
from esys.lsm.util import Vec3, BoundingBox
\end{verbatim}

The first import statement loads a number of relevent classes and subroutines required for all ESyS-Particle simulations. The second statement imports the \texttt{Vec3} and \texttt{BoundingBox} classes. Objects of the \texttt{Vec3} class are 3-component vectors, useful for specifying position, velocity or acceleration vectors in 3D. \texttt{BoundingBox} objects specify a rectangular region of 3D space and typically denote the spatial extents of a domain or particle assembly.

Every ESyS-Particle simulation commences with the creation of an ESyS-Particle simulation object called \texttt{LsmMpi}. This object provides a means to define and run a simulation and can be thought of as a container to which we add simulation components such as a list of particles, walls, different types of interactions and data output components. The following code-fragment creates a simulation object:

\begin{verbatim} 
sim = LsmMpi(numWorkerProcesses=1, mpiDimList=[1,1,1])
sim.initNeighbourSearch(
   particleType="NRotSphere",
   gridSpacing=2.5,
   verletDist=0.5
)
\end{verbatim} 

The first statement creates an \texttt{LsmMpi} object and takes two arguments. The \texttt{numWorker\+Processes} argument specifies the number of MPI processes to use for calculations. In this example we choose to run a serial simulation (with only one worker process).  However we could just as easily set this argument to a larger value for a MPI-parallel simulation (if you have access to a computer with multiple processor cores/CPUs). The second argument (\texttt{mpiDimList}) specifies the manner in which the domain will be divided amongst the worker processes. The first coordinate refers to the number of subdivisions in the x-direction whilst the second and third coordinates specify the number of subdivisions in the y- and z-directions respectively. It is important that you set \texttt{numWorkerProcesses} to be equal to the total number of subdomains specified by the \texttt{mpiDimList}.

The second statement (\texttt{sim.initNeighbourSearch}) specifies the type of particles used in the simulation. The two most common particle types are \texttt{NRotSphere} and \texttt{RotSphere}. \texttt{sim.initNeighbourSearch} also sets two parameters for the contact detection algorithm. The \texttt{gridSpacing} parameter defines the size of cubic cells used to identify contacting particles. This parameter needs to be larger than the maximum particle diameter. The \texttt{verletDist} parameter determines the frequency with which the contact lists are updated. If any particle moves a distance greater than \texttt{verletDist} the lists are updated. Optimal values for these two parameters satify the inequality \texttt{gridSpacing} $>$ $2$ $\times$ \texttt{maxRadius} $+$ \texttt{verletDist}. Reducing the \texttt{verletDist} will result in more accurate force calculations (because new contacts will be detected earlier) but the lists will be updated more frequently, which is computationally expensive. In most 
cases, the \texttt{gridSpacing} should be set to approximately $2.5 \times$ the maximum particle radius and the \texttt{verletDist} should be approximately $0.2 \times$ the minimum particle radius.

These two statements result in the construction of a suitable ESyS-Particle simulation object called \texttt{sim}. The simulation object now becomes a container to which we can add particles, walls, and various types of interactions. Before we do that, we need to specify how many timesteps to compute during the simulation and the timestep increment (in seconds):

\begin{verbatim}
sim.setNumTimeSteps(10000)
sim.setTimeStepSize(0.001)
\end{verbatim}

These two statements are relatively self-explanatory. A total of $10000$ timesteps will be computed, with a time increment of $0.001$~sec between each timestep. It is usually a good idea to set the timestep increment before creating particles or interactions. In some cases, the timestep increment is needed internally to correctly initialise interactions. 

\subsection{Specification of the spatial domain}

Prior to addition of particles, the simulation object must be assigned a valid spatial domain. Any particles or walls residing outside this domain are eliminated from force calculations and time integration.  The following code-fragment specifies the spatial domain for a simulation:

\begin{verbatim}
sim.setSpatialDomain (
   BoundingBox(Vec3(-20,-20,-20), Vec3(20,20,20))
)
\end{verbatim}

\noindent
The spatial domain is defined by a rectangular \texttt{BoundingBox}. In this example the spatial domain is a cube with side length of $40.0$~metres, with the left, bottom, back corner at the position $(x,y,z) = (-20, -20, -20)$. 

\subsection{Particle creation and initialisation}

There are a number of ways to create and add assemblies of particles to a simulation object. We will encounter a few of the most common methods in subsequent tutorials. For this example, we will insert two particles individually. The following code-fragment creates a particle and initialises its linear velocity:

\begin{verbatim}
particle=NRotSphere(id=0, posn=Vec3(-5,5,-5), radius=1.0, mass=1.0)
particle.setLinearVelocity(Vec3(1.0,-1.0,1.0))
sim.createParticle(particle)
\end{verbatim}

The first statement creates a \texttt{NRotSphere} object called \texttt{particle} whose \texttt{id} will be set to $0$ with a \texttt{radius} of $1.0$ metre, a \texttt{mass} of $1.0$ kilogram, and initially centred on the point $(x,y,z) = (-5,5,-5)$. The second statement initialises the linear velocity of the particle to $(V_x,V_y,V_z) = (1.0,-1.0,1.0)$~metres/second. Finally the third statement adds the newly created particle to our simulation object.

\vskip 5mm
\noindent \textbf{\emph{EXERCISE:} Add a second particle to the simulation object with \texttt{id} of $1$, a \texttt{radius} of $1.5$~m and a \texttt{mass} of $2.0$~kg. Set the initial position of the particle to $(5,5,5)$ and its linear velocity as $(-1,-1,-1)$ (HINT: simply copy the code-fragment above and modify as necessary).}


\subsection{Definition of inter-particle interactions}

Having added two particles to our simulation object, we must now specify the type of interactions between the particles if they should come into contact (which they will due to the carefully selected initial positions and velocities above). There are a number of different types of particle-particle interactions that may be used. We will encounter most of the more common interaction types in this and subsequent tutorials. For this example we will choose the simplest type of interaction -- linear elastic repulsion between non-rotational spheres. The following code-fragment achieves this:

\begin{verbatim}
sim.createInteractionGroup(
   NRotElasticPrms(
      name = "elastic_repulsion",
      normalK = 10000.0,
      scaling = True
   )
)
\end{verbatim}

This statement creates a so-called \texttt{InteractionGroup} for our simulation object. The particular type of interaction group created depends upon the parameter set provided as an argument. In this case the \texttt{NRotElasticPrms} parameter set specifies purely elastic interactions between unbonded non-rotational spheres, with an elastic stiffness of $10000$~N/m.  When \texttt{scaling} is \texttt{True} (the default), the normal stiffness scales with particle size.  The \texttt{name} argument assigns the \texttt{InteractionGroup} a unique user-defined label which can be used to extract information about particle pairs undergoing this type of interaction. At this stage we will ignore the \texttt{name}, but we will return to this in a later tutorial.

Depending upon the parameter set provided as an argument to the \texttt{cre\+ateIn\+ter\+ac\+tionGroup} subroutine, different types of interactions may be specified including body forces (e.g. gravity), particle-wall interactions and various types of particle-particle interactions. Some of these different interaction types will also be encountered in subsequent tutorials.

\subsection{Execution of time integration}

We have now done everything necessary to set up the simulation. All that remains is to instruct Python to go ahead and do all the computations:

\begin{verbatim}
sim.run()
\end{verbatim}

\noindent
Although this statement might appear to be something of an anti-climax, it is the most important, instructing ESyS-Particle to commence the simulation and compute forces and update particle positions repeatedly until the specified number of timesteps are completed. Once this command is executed, the C++ simulation engine takes control and no subsequent Python commands will be executed until the simulation is completed. In the next tutorial, we will encounter three different ways to instruct ESyS-Particle to execute additional python commands during the simulation.

\subsection{Running an ESyS-Particle simulation from the commandline}

Using your favorite text editor copy all the code-fragments above into a text file and save it as \texttt{bingle.py}. Make sure you insert all the code-fragments in the order they appear here (and don't forget to add the second particle). Having saved the script, we can run the simulation using the following shell command:

\begin{verbatim}
$ mpirun -np 2 `which esysparticle` bingle.py
\end{verbatim}

Since ESyS-Particle is designed to run in parallel using MPI, we need this complicated commandline. If you have a multi-processor computer, you can increase the number of processes (\texttt{np}) to run the simulation in parallel. (You will also need to suitably modify the \texttt{numWorkerProcesses} and \texttt{mpiDimList} arguments in the script.) The number after \texttt{np} should always be equal to (\texttt{numWorkerProcesses} + 1). This is because ESyS-Particle uses a master-slave parallelisation strategy. \texttt{numWorkerProcesses} specifies the number of slaves to use but MPI must initialise one extra process to act as the master.

\subsection*{What's next?}

No doubt if you got this far you are somewhat disappointed that the simulation did not appear to do anything. I can assure you that (unless you got some weird error messages\footnote{If you did receive errors, carefully compare your script with the complete code-listing in Appendix~\ref{code:bingle} to identify any typographical mistakes. Also make sure you indent the code exactly as shown here because Python is particular about indentation.}) the simulation has correctly computed the trajectories of both particles for an interval of $10$~sec. During that time the particles collided then bounced off each other, changing the velocities and directions of each particle. Of course all this happened only in computer memory. We did not instruct the simulation to output any data to file or the screen.  The following tutorial will describe three different ways that data can be output during simulations. At the end of this tutorial, you will have written a re-usable module for creating snapshot images of the 
particles in motion and will know how to find out information about individual particles during a simulation.

\newpage
\section{Data output during simulations}

This tutorial extends the \texttt{bingle.py} simulation constructed in the previous tutorial. The primary aim here is to introduce three ways to output data during simulations. We will first examine the simplest way to output data -- printing particle positions to the screen. The second method involves output of data to a text file using a built-in feature called a CheckPointer. Although these techniques are often very useful for testing purposes or postprocessing of simulation data, they are certainly not as visually satisfying as producing glossy images or movies of the particles in motion. Finally we will examine how one of the ESyS-Particle visualisation modules can be used to generate images of the particle assembly. 

A secondary aim of this tutorial is to demonstrate three different approaches for including your own code in ESyS-Particle scripts. These approaches are:
\begin{itemize}
\item direct insertion in the time-integration loop,
\item writing and calling a user-defined subroutine, and 
\item use of a user-defined \texttt{Runnable} module.
\end{itemize}

\noindent
The use of \texttt{Runnable} modules is arguably the best method, even though it appears complicated at first. \texttt{Runnables} have the advantage that they can be very easily reused for other simulations with little or no modification. In this way a user can build a library of helpful utilities for analysis and visualisation of simulation data. We will explain how to prepare and import your own modules, as well as how to configure your shell environment so that Python can find your modules easily.

\subsection{Printing simulation data to screen}

This example modifies our original \texttt{bingle.py} script so that the positions of both particles can be output to screen every 100 timesteps. This represents one of the most basic of data output operations and allows us to do our first real analysis of the simulation results (e.g.\ by utilising external software to plot particle positions vs.\ time). The complete code-listing for this example is provided in Appendix~\ref{code:bingle_output}, called \texttt{bingle\_output.py}. 

Replace the last line (\texttt{sim.run()}) of \texttt{bingle.py} with the following code-fragment:

\begin{verbatim}
N_max = sim.getNumTimeSteps()
for n in range (N_max):
   sim.runTimeStep()

   if (n%100==0):
      particles = sim.getParticleList()
      p1 = particles[0].getPosn()
      p2 = particles[1].getPosn()
      print n,p1,p2

sim.exit()
\end{verbatim}

\noindent
This time, rather than simply computing all $10000$ timesteps with a single subroutine call (\texttt{sim.run()}), we explicitly implement the time integration loop (via the \texttt{for}-statement). The \texttt{sim.runTimeStep()} subroutine call instructs the simulation object to compute only one timestep of the simulation.  

After the timestep is completed, we check whether 100 timesteps have passed (via the \texttt{if}-statement). If so, we firstly obtain a \texttt{particles} list from the simulation object by calling \texttt{sim.getParticleList()}. \texttt{particles} is a Python list data structure where each member of the list is an ESyS-Particle \texttt{NRotSphere} object. \texttt{NRotSphere} objects have a number of subroutines that permit the user to obtain various information about individual particles (e.g.\ particle position, velocity, mass, radius, etc.). For more information about the available subroutines refer to the \link{\texttt{NRotSphere} class documentation}{http://esys.geocomp.uq.edu.au/esys-particle_python_doc/current/pythonapi/html/esys.lsm.LsmPy.NRotSphere-class.html}.

\vskip 5mm
\begin{minipage}{5.75in}
\emph{\textbf{WARNING:} Care should be taken when using \texttt{sim.getParticleList()} in a simulation containing a very large number of particles. For large simulations, the amount of memory required to store the list by the master process may be very large. It is usually best to avoid using \texttt{sim.getParticleList()} if possible. In the next tutorial we will encounter another method (the \texttt{CheckPointer}) to store information about particles that avoids using a large amount of memory. }
\end{minipage}
\vskip 5mm


Having obtained the \texttt{particles} list, we can extract the current positions of each particle via the \texttt{particles[\#].getPosn()} subroutine calls. These subroutine calls return a \texttt{Vec3} vector containing the x-, y- and z-coordinates of a particle's centre-of-mass. Finally we output the timestep number (\texttt{n}) and the positions of both particles to screen (via the usual Python \texttt{print} statement).

\begin{figure}
\resizebox{\textwidth}{!}{
\includegraphics{bingle_output.jpg}}
\caption{A plot of particle trajectories using the text output (from \texttt{bingle\_output.py})} \label{fig:bingle_output}
\end{figure}

Save your modified script to a text file called \texttt{bingle\_output.py}, then run the simulation using the following shell command:

\begin{verbatim}
$ mpirun -np 2 `which esysparticle` bingle_output.py 
\end{verbatim}

\noindent
This shell command will run the simulation and output to screen the particle positions every 100 timesteps. An extract of 10 lines of output looks like:

\begin{verbatim}
1000 -3.999 3.999 -3.999 3.999 3.999 3.999
1100 -3.899 3.899 -3.899 3.899 3.899 3.899
1200 -3.799 3.799 -3.799 3.799 3.799 3.799
1300 -3.699 3.699 -3.699 3.699 3.699 3.699
1400 -3.599 3.599 -3.599 3.599 3.599 3.599
1500 -3.499 3.499 -3.499 3.499 3.499 3.499
1600 -3.399 3.399 -3.399 3.399 3.399 3.399
1700 -3.299 3.299 -3.299 3.299 3.299 3.299
1800 -3.199 3.199 -3.199 3.199 3.199 3.199
1900 -3.099 3.099 -3.099 3.099 3.099 3.099
\end{verbatim}

Re-run the simulation with the following shell command (typed on one line) to redirect the screen output to a text file called \texttt{simdata.csv}:

\begin{verbatim}
$ mpirun -np 2 `which esysparticle` bingle_output.py > simdata.csv
\end{verbatim}

\noindent
One can then use this text file to plot the trajectories of each particle, utilising your favourite graphing software. For example, import \texttt{simdata.csv} into a spreadsheet program and produce an XY plot of column two versus column one and column five versus column one. Your plot should be similar to Figure~\ref{fig:bingle_output}. Note that the two trajectories do not intersect although the particles are deflected in a manner consistent with collision. This is because the two particles are of finite radius and they collide only at their surfaces (whereas we are only plotting the trajectories of the centre-of-mass of the particles).

\subsection{Data output using the ESyS-Particle CheckPointer}

The simple example in the previous section is quite useful for debugging purposes, when one wishes only to monitor the movement of a few particles. In simulations involving many thousands of particles, one would prefer to output the positions, velocities and accelerations of all the particles at regular intervals so the data may be post-processed in various ways. ESyS-Particle includes a module known as a CheckPointer for this purpose. The CheckPointer is a special case of a group of modules known as FieldSavers, which we will discuss in a future tutorial. FieldSavers provide a mechanism to selectively output information on particles (such as position or kinetic energy), interactions (e.g.\ potential energy) and walls (including their position and the net force acting on a wall). 

A CheckPointer is designed to write text files at regular intervals containing the positions, velocities and accelerations of all particles. To implement a CheckPointer in a simulation is a relatively simple procedure. Return to your \texttt{bingle.py} script created in the first tutorial. Just before the \texttt{sim.run()} subroutine call, add the following code fragment:

\begin{verbatim}
sim.createCheckPointer (
   CheckPointPrms (
      fileNamePrefix = "bingle_data",
      beginTimeStep = 0,
      endTimeStep = 10000,
      timeStepIncr = 100
   )
)
\end{verbatim}

\noindent
The CheckPointer takes four parameters: 
\begin{itemize}
\item \texttt{fileNamePrefix}: specifying the filename prefix for all the output files to be written during the simulation,
\item \texttt{beginTimeStep}: the timestep number to commence saving data,
\item \texttt{endTimeStep}: the timestep number to conclude saving data, and
\item \texttt{timeStepIncr}: the number of timesteps to complete between each save.
\end{itemize}

\noindent
The example above will save simulation data every 100 timesteps, commencing at the first timestep (timestep \texttt{0}) and concluding at the last timestep (timestep \texttt{10000}). Often it is advantageous to save data for only a portion of the simulation at more regular intervals, particularly in cases where the interesting part of the simulation commences after a period of time (e.g.\ after the particles have settled under gravity). 

Append the code fragment above to \texttt{bingle.py} and save the script as \texttt{bingle\_chk.py}. Execute the simulation using the following command:

\begin{verbatim}
$ mpirun -np 2 `which esysparticle` bingle_chk.py
\end{verbatim}

Upon completion of the simulation, type \texttt{ls} at the command prompt. Failing any errors, you should have a listing similar to the following:

\begin{verbatim}
bingle_chk.py
bingle_data_t=0_0.txt
bingle_data_t=0_1.txt
bingle_data_t=10000_0.txt
bingle_data_t=10000_1.txt
bingle_data_t=1000_0.txt
bingle_data_t=1000_1.txt
bingle_data_t=100_0.txt
bingle_data_t=100_1.txt
bingle_data_t=1100_0.txt
bingle_data_t=1100_1.txt
bingle_data_t=1200_0.txt
bingle_data_t=1200_1.txt
...
\end{verbatim}

\noindent
You should notice there are two files generated at each designated save time: \texttt{bin\+gle\_\+da\+ta\_\+t=N\_0.txt} and \texttt{bingle\_data\_t=N\_1.txt}, where \texttt{N} denotes the timestep number when the file was saved. The first (ending with \texttt{\_0.txt}) is a header file containing information about the format of the corresponding data file (ending with \texttt{\_1.txt}). Depending upon the type of particles used, whether or not you have mesh walls etc., the format of the checkpoint files changes so that all relevant information about the simulation is saved at the specified times. 

Let's examine one of the output files from our simulation. Type \texttt{cat bin\+gle\_\+da\+ta\_\+t=0\_1.txt} at the command prompt. The result should look like this:

\begin{verbatim}
2
-5 5 -5 1 0 -1 1 -5 5 -5 -5 5 -5 1 -1 1 0 0 0 0 0 0
5 5 5 1.5 0 -1 2 5 5 5 5 5 5 -1 -1 -1 0 0 0 0 0 0
0
TMIG 0
\end{verbatim}

\noindent
The first line of the file specifies the number of particles in the simulation (only 2 in our case). The following 2 lines provide data on each of the particles, with one line per particle. We will examine the meaning of each number on these lines in a moment. After listing the data for each particle, the next line specifies the number of triangle mesh walls and the last line specifies the number of triangle mesh wall interaction groups. In our example we have no mesh walls or mesh wall interaction groups, hence the \texttt{0} on each line. We will revisit mesh walls in a later tutorial on hopper flow simulation.

Returning to the lines providing data on each particle, for simulations using \texttt{NRot\+Sphere} particles (as we are using here), the fields in each data line correspond to the following:
\begin{itemize}
\item fields 1, 2 \& 3: the X, Y and Z-coordinates of the \emph{current} particle position
\item field 4: the particle radius
\item field 5: the particle ID
\item field 6: the particle tag (more on tags later)
\item field 7: the particle mass (recall that we set the mass of the second particle to be \texttt{2.0})
\item fields 8, 9 \& 10: the X, Y and Z-coordinates of the \emph{initial} particle position
\item fields 11, 12 \& 13: the X, Y and Z-coordinates of the \emph{previous} particle position
\item fields 14, 15 \& 16: the X, Y and Z-components of the particle velocity
\item fields 17, 18 \& 19: the X, Y and Z-components of the net force acting on the particle
\item fields 20, 21 \& 22: (used with circular or periodic boundaries) specifies the circular shift to be added in the X, Y and Z-directions 
\end{itemize}

Your first impression may be that this is a lot of information to output for each particle when you may only be interested in, say, the velocities of each particle. The CheckPointer is designed to be a multi-purpose data output mechanism which records every piece of information on the current state of particles in the simulation. It is an ideal mechanism for outputing data when you intend to perform a number of different post-processing operations on your simulation data. The downside is that the CheckPointer can use a lot of disk space. To circumvent this problem, FieldSavers provide a mechanism to selectively output only certain data to disk. One needs to exercise caution when using FieldSavers because if you forget to store important information, you need to re-run the entire simulation. We will examine how to set up FieldSavers in a later tutorial on uniaxial compression of elastic-brittle material.

\subsection{Generation of particle snapshots (via subroutine calls)}

The previous two examples illustrated simple ways to output data from ESyS-Particle simulations. In this example we will introduce a way to generate beautiful snapshots of the particle assembly at various times during a simulation. This is achieved using the ESyS-Particle \texttt{esys.lsm.vis.povray} module and an external package called \link{POVray}{http://www.povray.org} that is ideal for generating images of 3D objects or scenes\footnote{Take a break for the moment and enjoy the amazing computer-generated images in the \link{POVray Hall of Fame}{http://hof.povray.org/}!}. The ESyS-Particle \texttt{povray} module provides a relatively simple interface between particle simulations and POVray. 

This part of the tutorial involves implementation of a Python subroutine. If you are not already familiar with writing subroutines in Python, I recommend you first study \link{Chapter 4 of the Python Language Tutorial}{http://docs.python.org/tut/node6.html}.  We will modify \texttt{bingle\_output.py} for this example.  The complete code-listing is available in Appendix~\ref{code:bingle_vis} entitled \texttt{bingle\_vis.py}.

Since we will be making use of the ESyS-Particle \texttt{povray} module, we need to add a third import statement to the top of our script: 

\begin{verbatim}
from esys.lsm.vis import povray
\end{verbatim}

\noindent
ESyS-Particle currently has two visualisation modules -- \texttt{povray} and \texttt{vtk}. Both utilise external libraries for rendering images of simulation data (\link{POVray}{http://www.povray.org} and \link{VTK}{http://www.vtk.org} respectively). The ESyS-Particle visualisation modules are designed to provide a common interface to these two rendering engines (so that either package may be used with minimal changes to your Python script). Each module has both advantages and disadvantages. \texttt{povray} produces very beautiful images of particle assemblies with the possibility of rendering particles with various textures and special effects, but it lacks tools specifically designed for scientific visualisation of datasets (e.g.\ for generating isosurfaces or contours). \texttt{vtk} is the opposite, providing great scientific visualisation tools and an interactive graphical interface but lacking strong support for rendering particles nicely. 

Having imported the \texttt{povray} module, we now implement a Python subroutine called \texttt{snapshot()} designed to render an image of the particle assembly and store it as a file. The code-fragment for implementing this subroutine is as follows:

\begin{verbatim}
def snapshot(particles=None, index=0):
   pkg = povray

   scene = pkg.Scene()

   for pp in particles:
      povsphere = pkg.Sphere(pp.getPosn(), pp.getRadius())
      povsphere.apply(pkg.Colors.Red)
      scene.add(povsphere)

   camera = scene.getCamera()
   camera.setLookAt(Vec3(0,0,0))
   camera.setPosn(Vec3(0,0,20))
   camera.setZoom(0.1)

   scene.render(
      offScreen=True, 
      interactive=False, 
      fileName="snap_%.4d.png" % (index), 
      size=[800,600]
   )
   return
\end{verbatim}

\noindent
The first line of this code-fragment defines the name of the subroutine (\texttt{snapshot}) and specifies that it accept two keyword arguments: 
\begin{itemize}
\item \texttt{particles} -- a pointer to a list of particles (e.g.\ obtained from the \texttt{sim.getPar\+tic\+leList()} subroutine), and
\item \texttt{index} -- a unique identifier used to specify the name of the file in which to store the rendered image. 
\end{itemize}

Within the subroutine, we firstly specify that the visualisation package (\texttt{pkg}) will be \texttt{povray}. We could just as easily have replaced \texttt{povray} with \texttt{vtk} to use the other renderer. Having selected the package, we then construct a \texttt{Scene} object via the \texttt{scene = pkg.Scene()} subroutine call. \texttt{Scene} objects are containers for \emph{actors}, a \emph{camera} and (optionally) a \emph{light-source}. \emph{Actors} can be any three-dimensional object we wish to see in the Scene. When defining actors, we specify the geometrical shape of the actor, its colour or surface texture, and its position and orientation in the Scene. 

ESyS-Particle visualisation modules provide support for a number of different primitive shapes that we can use as actors in our scene (e.g.\ spheres, cones, cylinders and boxes). In this tutorial we will stick with simple spheres to represent the particles in our simulation. To specify how our scene will look, we need to add spheres to the scene via the code-fragment:

\begin{verbatim}
   for pp in particles:
      povsphere = pkg.Sphere(pp.getPosn(), pp.getRadius())
      povsphere.apply(pkg.Colors.Red)
      scene.add(povsphere)
\end{verbatim}

\noindent
This code-fragment loops over each particle in the list provided by the \texttt{particles} argument. For each particle in the list, we create a sphere via the \texttt{pkg.Sphere(..)} subroutine, specify the colour of the sphere using \texttt{Sphere.apply(..)} then add the sphere to the scene using \texttt{scene.add(..)}. The \texttt{pkg.Sphere(..)} subroutine takes two mandatory arguments -- the position of the sphere (as a \texttt{Vec3} vector) and the radius of the sphere. In most cases we can simply use the original coordinate system and sizes of particles from our simulation. 

Next we must initialise the camera we will use to take a picture of our scene. This is an important step: if we do not initialise the camera correctly our rendered image may be empty or we may only see a portion of the scene. Typically you will need to experiment with the camera settings to achieve the desired result. In this case I have selected reasonable values for visualising our \texttt{bingle.py} simulation. The appropriate camera setup code-fragment is:

\begin{verbatim}
   camera = scene.getCamera()
   camera.setLookAt(Vec3(0,0,0))
   camera.setPosn(Vec3(0,0,20))
   camera.setZoom(0.1)
\end{verbatim}

\noindent
The first statement returns a pointer to our scene's camera which we call \texttt{camera}. Next we specify the point in 3D space we would like to be the focus of the camera (via the \texttt{camera.setLookAt(..)} subroutine). We also specify the position of the camera itself (\texttt{camera.setPosn(..)}) and finally we choose the zoom factor (\texttt{camera.setZoom(..)}). Changing the zoom factor is a good starting point if your rendered image is empty or doesn't look the way you desire.

Finally, we can instruct the \texttt{Scene} object to create an image of the scene we have just constructed:

\begin{verbatim}
   scene.render(
      offScreen=True,
      interactive=False,
      fileName="snap_%.4d.png" % (index),
      size=[800,600]
   )
\end{verbatim}

\noindent
This subroutine call instructs ESyS-Particle to communicate with the renderer library (either via a temporary script or directly via a socket interface) to generate an image of the scene. A number of optional keyword arguments may be provided to control various aspects of the final image:
\begin{itemize}
\item \texttt{offScreen} -- a Boolean variable that determines whether the image will appear in a window onscreen or be rendered offscreen (in the background). 
\item \texttt{interactive} -- a Boolean variable specifying whether the user is permitted to interact with the rendered image. If this argument is set to \texttt{True} and \texttt{offScreen=False}, a window will appear and the user will be able to pan and zoom the image using the mouse or keyboard. Only the \texttt{vtk} package provides interactive features.
\item \texttt{filename} -- a text string specifying the name of the file in which to save the image. The filename extension determines the image format. Both \texttt{povray} and \texttt{vtk} support a large range of common image formats. 
\item \texttt{size} -- a tuple specifying the pixel resolution of the final image. Changing the aspect ratio of this tuple can help you render the extremities of a particle assembly that is not 4:3 aspect ratio.
\end{itemize}

\noindent
Notice that the filename argument above uses Python string formatting syntax to construct a filename of the form \texttt{snap\_000\#.png} where \texttt{\#} is the index provided as the second argument of our \texttt{snapshot} subroutine. This naming convention is very handy for producing a sequence of images that can later be combined into a movie of the simulation.

In a copy of \texttt{bingle\_output.py} append the import statement and subroutine definition above directly after the two existing import statements. Having done this, all that remains is to replace the \texttt{if}-statement in the time integration loop with the following:

\begin{verbatim}
   if (n%100==0):
      particles = sim.getParticleList()
      snapshot(particles=particles, index=n)
\end{verbatim}

\noindent
Notice that we no longer \texttt{print} the particle positions to screen. Instead we call our new \texttt{snapshot()} subroutine providing, as arguments, the current list of particles returned by \texttt{sim.getParticleList()} and the current timestep number (\texttt{n}) as the index for naming the image file rendered when the subroutine is called.

Save the resulting script to a text file called \texttt{bingle\_vis.py} and run the simulation from the shell using a similar command as before. All being well, your simulation should now produce a sequence of 100 image files named \texttt{snap\_0000.png} through to \texttt{snap\_9900.png}. Figure~\ref{fig:bingle_vis} contains a few of these snapshots from various times during the simulation. The approach, collision and rebound of the particles is now clearly evident.

\begin{figure}
\begin{center}
\resizebox{5in}{!}{
\includegraphics{bingle_vis_snaps_2.jpg}}
\end{center}
\caption{A sequence of snapshots (from \texttt{bingle\_vis.py})} \label{fig:bingle_vis}
\end{figure}

This tutorial example illustrates only very basic visualisation. However the \texttt{povray} package can be used to produce much more attractive images by using some of its more advanced features. In the next example we will describe how to write your own module for producing image snapshots that you can reuse for subsequent simulations with relatively minor additions to your simulation script.

\subsection{A \texttt{Runnable} module for generating snapshots}

Thus far in our data output examples we have had to make use of an explicit time-integration loop (instead of the simple and elegant \texttt{sim.run()} call), used a \texttt{CheckPointer} to store particle data and, in the previous example, have added an additional subroutine to our simulation script. This has resulted in a rather lengthy script. To compound the issue, we would need to do this for every simulation script we write, resulting in many large scripts containing much duplicate code. From a design and implementation perspective this is unwieldy and multiplies the possibility for errors in the code. 

ESyS-Particle provides a mechanism to resolve many of these issues in the form of a \texttt{Runnable} class. A \texttt{Runnable} is a user-defined class that can be called by a simulation object once per timestep either before or after the force computations and time-integration. \texttt{Runnable} classes can be implemented as modules that can be reused in subsequent simulations simply by adding an import statement and some lines of initialisation code to your simulation script. This is a powerful feature of ESyS-Particle, offering the user the possibility to develop runtime data analysis and visualisation utilities that can be reused whenever they are needed with little or no modification to the \texttt{Runnable} itself.  

The aim for this tutorial example is to implement the \texttt{snapshot()} subroutine from the previous example as a \texttt{Runnable} module. We will discuss how to call this from within your simulation script and how to deploy the module so that it can be easily reused in subsequent simulation scripts. If you are not already familiar with classes and inheritance in Python, I recommend you first study \link{Chapter 9 of the Python Language Tutorial}{http://docs.python.org/tut/node11.html}. We will also touch on some topics covered in \link{Chapter 6 of the Python Tutorial}{http://docs.python.org/tut/node8.html}. The complete code-listings for this example are found in Appendix~\ref{code:POVsnaps} \texttt{POVsnaps.py} and Appendix~\ref{code:bingle_Runnable} \texttt{bingle\_Runnable.py}.

\subsubsection{Implementation of a snapshot \texttt{Runnable}}

A \texttt{Runnable} is best implemented in its own text file rather than inserting it in a simulation script (although you may do this if you wish). We will implement our \texttt{Runnable} in a text file called \texttt{POVsnaps.py}. Like any other ESyS-Particle script, we commence with relevent import statements:

\begin{verbatim}
from esys.lsm import *
from esys.lsm.util import Vec3, BoundingBox
from esys.lsm.vis import povray
\end{verbatim}

A user-defined \texttt{Runnable} is a class that inherits from an ESyS-Particle base class called \texttt{Runnable}. As per any Python class, we must implement an initialisation subroutine (\texttt{self.\_\_init\_\_()}) that is called whenever we construct an instance of this class. The initialisation subroutine prepares an instance of the class for use in a simulation. The following code-fragment implements the class definition and initialisation subroutine:


\begin{verbatim}
class POVsnaps (Runnable):
   def __init__(self, sim, interval):
      Runnable.__init__(self)
      self.sim = sim
      self.interval = interval
      self.count = 0
      self.configure()
\end{verbatim}


The first line defines the name of the class \texttt{POVsnaps} and specifies that it inherits from the \texttt{Runnable} base class. Next we encounter the definition of the \texttt{\_\_init\_\_(..)} subroutine. Note that we have stipulated that this subroutine accepts two mandatory arguments: a pointer to the simulation object to which the class is attached (\texttt{sim}) and an integer (\texttt{interval}) specifying the number of timesteps between successive images. The \texttt{\_\_init\_\_(..)} subroutine first calls the equivalent base class subroutine to do some default initialisation of our \texttt{Runnable}, then stores the \texttt{sim} pointer and \texttt{interval} as data members of the class, as well as initialising a counter (\texttt{self.count}) that keeps track of the total number of images rendered. (We use this later to define the image filenames.) A \texttt{self.configure()} subroutine is also called, the implementation of which is as follows: 

\begin{verbatim}
   def configure(
      self, 
      lookAt=Vec3(0,0,0), 
      camPosn=Vec3(0,0,20), 
      zoomFactor=0.1,
      imageSize=[800,600]
   ):
      self.lookAt=lookAt
      self.camPosn=camPosn
      self.zoomFactor=zoomFactor
      self.imageSize=imageSize
\end{verbatim}

Our \texttt{self.configure()} subroutine accepts four optional keyword arguments: \texttt{lookAt}, \texttt{camPosn}, \texttt{zoomFactor} and \texttt{imageSize}. Each of these arguments is provided with a default value in the subroutine definition (the first line). When this subroutine is called, any keyword argument that is not assigned a value by the calling statement will be set to the default value specified here. The call to \texttt{self.configure()} in the initialisation subroutine specifies no keyword arguments, so all of these arguments will be set to their default values. The implementation of this subroutine is straight-forward: we simply create an internal data member to store the value for each argument. These data members will be used in the \texttt{self.snapshot()} subroutine later. 

\vskip 5mm
\begin{minipage}{5.75in}
\emph{\textbf{ASIDE:} You may be wondering why we have implemented an extra subroutine for configuring these data members. We could have included these as keyword arguments of the initialisation subroutine. The rationale for implementing this extra subroutine is that we can use this subroutine to reconfigure our \texttt{POVsnaps Runnable} during a simulation. This feature might be used, for example, to change the camera position during a simulation to produce a fly-through animation of the particle assembly. The initialisation subroutine can only be called once but the \texttt{configure(..)} subroutine can be called many times.}
\end{minipage}
\vskip 5mm

ESyS-Particle \texttt{Runnable} subclasses must also implement a \texttt{self.run()} subroutine. This subroutine is called by simulation objects once per timestep and it is here that we implement the code that should be executed by the \texttt{Runnable} each timestep. The following code-fragment implements the mandatory \texttt{self.run()} subroutine:

\begin{verbatim}
   def run(self):
      if ((self.sim.getTimeStep() % self.interval) == 0):
         self.snapshot()
         self.count += 1
\end{verbatim}

The \texttt{self.run()} subroutine simply waits until \texttt{self.interval} timesteps have elapsed, then calls the \texttt{self.snapshot()} subroutine prior to incrementing the counter. This code-fragment is reminiscent of the code we added to the time-integration loop of \texttt{bin\+gle\_\+vis.py}. Not surprisingly, the \texttt{self.snapshot()} subroutine looks very similar to the subroutine we encountered in that example: 

\begin{verbatim}
   def snapshot(self):
      pkg = povray
      Scene = pkg.Scene()
      plist = self.sim.getParticleList()

      for pp in plist:
         povsphere = pkg.Sphere(pp.getPosn(), pp.getRadius())
         povsphere.apply(pkg.Colors.Red)
         Scene.add(povsphere)

      camera = Scene.getCamera()
      camera.setLookAt(self.lookAt)
      camera.setPosn(self.camPosn)
      camera.setZoom(self.zoomFactor)

      fname = "snap_%.4d.png" % (self.count)
      Scene.render(
         offScreen=True, 
         interactive=False, 
         fileName=fname, 
         size=self.imageSize
      )
\end{verbatim}

\noindent
You will notice only a few minor differences though. We now use the data members defined in the \texttt{self.configure()} subroutine to initialise the camera and the image size. By using these data members we can easily configure our snapshot \texttt{Runnable} for different simulations or perhaps reconfigure the camera during a simulation if we would like, for example, to create a fly-through animation.

\subsubsection{Use of the snapshot \texttt{Runnable} in a simulation}

Having copied all the code-fragments into a text file called \texttt{POVsnaps.py}, we are ready to use our \texttt{Runnable} in a simulation. We will modify \texttt{bingle.py}, the script we wrote that didn't produce any data output. Firstly, add an import statement to the top of the script:

\begin{verbatim} 
from POVsnaps import POVsnaps
\end{verbatim} 

\noindent
This statement will instruct Python to search for a file called \texttt{POVsnaps.py} and import the \texttt{POVsnaps} class implemented therein. Having done this, Python now knows where to find the implementation of this class when we wish to use it. 

Now scroll down your simulation script and insert the following code-fragment just before the \texttt{sim.run()} statement:

\begin{verbatim}
povcam = POVsnaps(sim=sim, interval=100)
sim.addPostTimeStepRunnable(povcam)
\end{verbatim}

\noindent
The first statement creates a POVsnaps object called \texttt{povcam}. We have specified that it will be attached to our simulation object (\texttt{sim}) and snapshots will be taken every 100 timesteps. The second statement is all-important. This statement instructs the simulation object to add our \texttt{Runnable} as a ``\texttt{PostTimeStepRunnable}''. In other words, the \texttt{povcam.run()} subroutine will be called each timestep after the simulation object has completed force calculations and updated particle positions and velocities. We also have the possibility of adding a \texttt{Runnable} whose \texttt{run()} subroutine is called before force calculations. This is a ``\texttt{PreTimeStepRunnable}''.

Save your simulation script in a text file called \texttt{bingle\_Runnable.py} and run the simulation from the shell. The simulation will output snapshots of the particle assembly in much the same way as the \texttt{bingle\_vis.py} script in the previous example. Note that this time we only needed to add 3 statements to our simulation script to utilise the \texttt{Runnable} (as compared with \texttt{bingle\_vis.py} where we had to add approximately 19 lines of code, including the subroutine definition). We have not only avoided tiring out our fingers typing all those lines of code; we have created a module that can be reused in other simulation scripts just by adding the 3 lines of code above. 

\subsubsection{Deploying a \texttt{Runnable} as a reusable module}

Whenever Python encounters an import statement, it will search a number of default directories for the appropriate python scripts implementing the imported modules. These default directories are typically places like \texttt{/usr/lib/Python2.4/site-packages} or \linebreak \texttt{/usr/local/lib/Python2.4/site-packages}. If Python cannot find the script, it will search the current working directory. If Python still cannot find the script, it will crash with an error message. 

As a user, you have two options for reusing your \texttt{Runnable} module. Firstly you could copy your \texttt{Runnable} module script (e.g.\ \texttt{POVsnaps.py}) into the current working directory whenever you wish you use it. This can get tedious, so a better way is to copy the script to a directory where you intend to store all your useful \texttt{Runnable} module scripts. To do this first create a directory to store the scripts and copy your script into that directory:

\begin{verbatim}
$ mkdir /home/my_username/Runnable_scripts/
$ cp POVsnaps.py /home/my_username/Runnable_scripts
\end{verbatim}

\noindent 
(Don't forget to replace the word ``\texttt{my\_username}'' with you actual username!) Having done that you must add this directory to an environment variable called \texttt{PYTHONPATH}. In the bash shell, it is done this way:

\begin{verbatim}
$ export PYTHONPATH=/home/my_username/Runnable_scripts/:$PYTHONPATH
\end{verbatim}

\noindent
If you use a different shell, consult your shell documentation or ask your local linux guru or system administrator for help. You may wish to add the shell command above to a file that is executed whenever you open a shell (e.g.\ \texttt{/home/my\_username/.bashrc}).

Once your \texttt{Runnable\_scripts} directory is in your \texttt{PYTHONPATH}, Python will be able to find your module whenever it encounters an import statement like \texttt{from POVsnaps import POVsnaps}. You have now successfully deployed your \texttt{Runnable} as a reusable Python module. From now on, pretty snapshots of your particle simulations are only 3 lines of code away!

\vskip 5mm
\noindent \textbf{\emph{EXERCISE:} Another very useful \texttt{Runnable} is one that stores the particle positions to a text file at specified intervals. Write a \texttt{Runnable} to achieve this and store it in your \texttt{Runnable\_scripts} directory for later use (HINT: you will only need to implement a simple \texttt{self.\_\_init\_\_()} subroutine and a \texttt{self.run()} subroutine for this. If you are unfamiliar with writing text files in Python, refer to \link{Section 7.2 of the Python Language Tutorial}{http://docs.python.org/tut/node9.html\#SECTION009200000000000000000}).}

\vskip 5mm
\begin{minipage}{5.75in}
\emph{\textbf{ASIDE:} It is worth noting at this stage that the concept of a \texttt{Runnable} is really quite general. It is not only useful for data analysis and output during simulations, but we could also write \texttt{Runnable} modules designed to change the positions (or velocities) of particles (or walls) during a simulation. One such \texttt{Runnable} could be written to implement a so-called \emph{servo-wall} that maintains a constant pressure on the particle assembly by opposing the repulsive force due to the particles touching the wall. This is particularly useful in uniaxial compression simulations, the topic of a subsequent tutorial.}
\end{minipage}
\vskip 5mm

\subsection*{What's next?}

The first two tutorials in this Guide have introduced a simple two-particle ESyS-Particle simulation and provided tools for visualising the particles in motion. We are now well prepared to start examining some more complicated simulations including ones with gravity, walls, blocks of both bonded and unbonded particles, and differing inter-particle or particle-wall interactions. The next tutorials will introduce these techniques in the context of progressively more interesting examples of particle simulations including a bouncing cube made of particles and collapse of a loosely bonded prism of random-sized particles. Our \texttt{POVsnaps Runnable} will be very handy to visualise our simulation results as the simulations increase in complexity. To use \texttt{POVsnaps} we will only need to add the import and initialisation code to our scripts (and possibly change the camera parameters via the \texttt{configure()} subroutine).


\newpage
\section{Bouncing balls: adding gravity, walls and bonded particles}

This tutorial aims to build upon the techniques introduced previously, to demonstrate more of the basic components of ESyS-Particle simulations. In particular, we will examine how body forces (such as gravity and viscosity), planar walls and bonded clusters of particles are implemented. By way of motivation, we will consider the problem of simulating an elastic body dropped from a height upon a frictionless table under the influence of gravity and air resistance. Intuitively we expect the body to bounce off the table-top, rise up to a height lower than the original height, then fall back towards the table. After numerous bounces, the body should come to rest on the table-top. To make the simulation more interesting we will start with a single particle representing our elastic body then replace this with a cubic cluster of bonded particles. This relatively simple problem serves to illustrate some new techniques that have wide applicability in particle simulations. As always complete code-listings are provided 
in Appendix~\ref{code}.

\vskip 5mm
\noindent \textbf{\emph{EXERCISE:} Write a script that initialises a simulation object and adds a single particle that is initially at rest, located at $(x,y,z) = (0,5,0)$. Assign an initial velocity of $(V_x, V_y, V_z) = (1.0, 10.0, 1.0)$. You may use the same spatial domain as the \texttt{bingle.py} tutorial, i.e., a cubic region with opposite corners at $(-20,-20,-20)$ and $(20,20,20)$. At this stage you do not need to add any \texttt{InteractionGroups}. Save the script in a text file called \texttt{gravity.py}}.
\vskip 5mm

\subsection{Implementation of body forces: gravity and bulk viscosity}

There are three basic types of interactions in ESyS-Particle: inter-particle interactions, body forces, and particle-wall interactions. We have already encountered inter-particle interactions in our two-particle collision tutorial. This section will introduce two of the most common body forces: gravity and bulk viscosity. The following section introduces walls and particle-wall interactions.

\subsubsection{Gravitational Interactions}

Gravity is implemented in ESyS-Particle via an \texttt{InteractionGroup} specified by a parameter set called \texttt{GravityPrms}. A typical code-fragment for implementing gravity is:

\begin{verbatim}
sim.createInteractionGroup(
   GravityPrms(name="earth-gravity", acceleration=Vec3(0,-9.81,0))
)
\end{verbatim}

\noindent \texttt{GravityPrms} accepts two keyword arguments. The first argument (\texttt{name}) is a user-defined label for the \texttt{InteractionGroup} and the second is a \texttt{Vec3} vector specifying the direction and magnitude of the gravitational \texttt{acceleration}. In this case, we have specified that the gravitational acceleration is $9.81$~m/s/s in the negative y-direction. This is the usual value for simulations in which the y-axis is assumed to be vertical.

\subsubsection{Bulk Viscosity}

In a great many particle simulations it is advantageous to include bulk viscosity (a damping force proportional to the instantaneous velocity of each particle acting in the direction that opposes motion). A relatively large bulk viscosity may be used for quasi-static simulations in which only the steady-state dynamics are of interest. A small bulk viscosity in a driven, elastodynamic simulation will attenuate propagating stress waves and eliminate catastrophic accumulation of kinetic energy without significantly altering the numerical solution to the original elastodynamic problem. For our bouncing ball simulation here, bulk viscosity is a crude proxy for air resistance. 

In much the same manner as for gravity, bulk viscosity is implemented in ESyS-Particle via an \texttt{InteractionGroup}. In this case, the \texttt{LinDampingPrms} parameter set defines the pertinent parameters for this type of body force. A typical code-fragment is:

\begin{verbatim}
sim.createInteractionGroup(
    LinDampingPrms(
        name="linDamping",
        viscosity=0.1,
        maxIterations=100
    )
)
\end{verbatim}

\noindent
Once again, a unique \texttt{name} is provided for the interaction group, a coefficient of \texttt{viscosity} is initialised and we specify a maximum number of iterations (\texttt{maxIterations}) for the iterative velocity-Verlet solver used by ESyS-Particle when viscosity is included. Typically this last argument does not need to be very large as the iterative solver converges rapidly in most cases (less than about 10 iterations).

Choosing an appropriate coefficient of viscosity depends upon the particular problem to be solved. For elastodynamic problems, a small value is sufficient (\texttt{viscosity}~$< 0.05$) whereas for quasi-static problems a value as large as \texttt{viscosity} $\sim 0.5$ might be appropriate. For the bouncing ball example, a \texttt{viscosity}~$= 0.1$ was selected by \emph{trial-and-error} to produce sufficient damping of the ball's motion over an interval of $20$~sec.\ to simulate damped oscillations of a bouncing ball. 

\subsection{Implementation of infinite planar walls and particle-wall interactions}

Frequently one would like to incorporate fixed or movable walls in particle simulations. Walls may be planar, piece-wise planar, or perhaps an arbitrary shape. ESyS-Particle implements three types of walls:

\begin{itemize}
\item \emph{Planar walls} -- infinite planar walls specified by a point and a normal vector. 
\item \emph{Linear meshes} -- a piece-wise linear mesh of line segments for arbitrarily shaped walls in 2D simulations.
\item \emph{Triangular meshes} -- a mesh of triangles used to define surfaces in 3D simulations.
\end{itemize}

\vskip 5mm
\begin{minipage}{5.75in}
\emph{\textbf{IMPORTANT:} All three types of wall have an \textbf{active} side and an \textbf{inactive} side. For the case of an infinite wall, the normal vector points to the active side of the wall. Particles impinging on a wall from the active side will bounce off the wall. However particles impinging on a wall from the inactive side will accelerate through the wall in an unphysical manner. Both types of mesh walls have an active side determined by the order in which vertices are specified for line-segments or triangles. Caution should be exercised when inserting walls to ensure they are correctly orientated (lest you get unexpected results). We will demonstrate how to use triangle mesh walls in the tutorial on hopper flow simuation.}
\end{minipage}
\vskip 5mm

For our bouncing ball simulation, we will implement an infinite planar wall in the XZ-plane (i.e. normal in the positive y-direction) located at $Y=-10$. The appropriate code-fragment for inserting our planar wall is the following:

\begin{verbatim}
sim.createWall(
   name="floor",
   posn=Vec3(0,-10,0),
   normal=Vec3(0,1,0)
)
\end{verbatim}

\noindent
By now, the \texttt{name} argument is familiar, providing a unique label for our wall. We will use this label in a moment. The second argument (\texttt{posn}) is a \texttt{Vec3} vector specifying a point lying in the plane of the wall. Finally the \texttt{normal} argument specifies a \texttt{Vec3} normal vector for the wall. Since this vector points in the positive y-direction, the wall lies in the XZ-plane.

Simply inserting a wall into a simulation object is insufficient. We must also define the type of interactions between particles and walls. There are two common types of interactions: elastic repulsion and bonded interactions. At this stage we only consider elastic repulsion, implemented via the following code-fragment:

\begin{verbatim}
sim.createInteractionGroup(
   NRotElasticWallPrms(
      name = "elasticWall",
      wallName = "floor",
      normalK = 10000.0
   )
)
\end{verbatim}

\noindent
Particle-wall interactions are also implemented via an \texttt{InteractionGroup}. This time we pass a \texttt{NRotElasticWallPrms} parameter set used to specify elastic repulsion of non-rotational spheres from a wall. The \texttt{wallName} argument specifies to which wall this particle-wall interaction refers. We make use of the wall \texttt{name} here to identify the wall under consideration. The last argument (\texttt{normalK}) specifies the elastic stiffness of the particle-wall interaction in units of N/m.

The choice of elastic stiffness is not arbitrary. We need to assign an elastic stiffness sufficiently large that the wall can support the weight of the particle with a relatively small indentation (or overlap). If the elastic stiffness is too small the particle will continue to fall through the wall and eventually fall out the other side!\footnote{Try it yourself: For a ball of mass $m$ and radius $r$ in a gravitational field with acceleration $g$, the elastic stiffness must be $K_{wall} > mg/r$ to prevent the particle falling through the wall.}

Having created this particle-wall interaction group, the simulation will now track the locations of particles relative to the wall and apply an elastic restoring force to any particle that comes into contact with the wall. Add all the code-fragments above (for gravity, viscosity and the wall) to your \texttt{gravity.py} script and run a simulation consisting of 20000 timesteps with a timestep increment of $0.001$~sec. Use your \texttt{POVsnaps Runnable} to make snapshots of the particle during the simulation. You may need to call the \texttt{povcam.configure()} subroutine when you initialise your \texttt{POVsnaps} object to change the camera position and/or look-at point. If all goes well, you should see the particle bounce off the wall a few times, with a reducing bounce height after successive bounces. Figure~\ref{fig:bouncy_ball} illustrates the movement of the ball graphically.

\begin{figure}
\begin{center}
\resizebox{5in}{!}{
\includegraphics{gravity_plot.jpg}}
\end{center}
\caption{Trajectory of a ball bouncing under gravity with linear viscosity (from \texttt{gravity.py})} \label{fig:bouncy_ball}
\end{figure}

\subsection{Generating a bonded lattice of particles}

In the next example we will modify our bouncing ball script to replace the single particle with a bonded cube of particles. This serves to illustrate the three steps for generating an assembly of particles:

\begin{enumerate}
\item generating a block of unbonded particles,
\item creating bonds between neighbouring particles, and
\item specifying the type of interactions between bonded particle-pairs.
\end{enumerate}

\noindent 
The complete code-listing for this example is provided in Appendix~\ref{code}, entitled \texttt{grav\+i\+ty\_\+cube.py}. We will replace the code-fragment (in \texttt{gravity.py}) for creating a single particle with the code below. 

\subsubsection{Generating a block of unbonded particles}

ESyS-Particle provides four methods for generating a block of particles: 

\begin{itemize}
\item \texttt{SimpleBlock} -- generates a block of particles whose centres-of-mass reside on the vertices of a regular cubic lattice. This is the simplest configuration but is typically not the best choice for serious simulations since the particle-packing is not ideal (the porosity is very high).
\item \texttt{CubicBlock} -- generates a Face-Centred Cubic (FCC) lattice of particles with a dense packing arrangement. 
\item \texttt{HexagBlock} -- generates a Hexagonal Close Packing (HCP) of particles.
\item \texttt{RandomBoxPacker} -- generates a block of particles with radii randomly distributed in a specified range.
\end{itemize}

\noindent For illustrative purposes, we will use the \texttt{CubicBlock} in our example. The \texttt{SimpleBlock} and \texttt{HexagBlock} are implemented in the same manner. Generation of the block of random-sized particles is somewhat different and is covered in the next tutorial.

To begin using \texttt{CubicBlock} add the following line to the other import lines at the start of the code:

\begin{verbatim}
from esys.lsm.geometry import CubicBlock, ConnectionFinder
\end{verbatim}

The following code-fragment generates an FCC cubic block of particles and adds them to the simulation object:

\begin{verbatim}
cube = CubicBlock(dimCount=[6,6,6], radius=0.5)
cube.rotate(axis=Vec3(0,0,3.141592654/6.0),axisPt=Vec3(0,0,0))
sim.createParticles(cube)
\end{verbatim}

\noindent A \texttt{CubicBlock} object is instantiated via two arguments: \texttt{dimCount} and \texttt{radius}. \texttt{dimCount} is a three-element list specifying the number of particles to insert in each axis direction (in this case, the cube will comprise 6 particles per side). The \texttt{radius} of the particles is determined by the second argument. The physical dimensions of the \texttt{CubicBlock} are thus governed by the combination of the \texttt{radius} and the \texttt{dimCount} (in this case the cube will be $6\mathrm{m} \times 6\mathrm{m} \times 6\mathrm{m}$).

The second command (\texttt{cube.rotate(..)}) rotates the cube by 30 degrees about the Z-axis. This is done so the cube will first strike the floor on an angle rather than flat on one face. This should make the subsequent bounces a bit more interesting! The final command adds our cube of particles to the simulation object.

\subsubsection{Creation of inter-particle bonds}

By default the \texttt{CubicBlock} class creates an unbonded particle assembly. In order to construct a bonded particle assembly we must explicitly inform the simulation object that particles are to be bonded together. The following code-fragment achieves this:

\begin{verbatim}
sim.createConnections(
   ConnectionFinder(
      maxDist = 0.005,
      bondTag = 1,
      pList = cube
   )
)
\end{verbatim}

\noindent
The aim of this code is to construct a list of particle-pairs that are to be bonded together. The \texttt{ConnectionFinder} is an object that searches the list of particles provided as the \texttt{pList} argument (our \texttt{cube} in this case) looking for pairs of particles that are within a  specified distance (\texttt{maxDist}) of each other. The chosen distance in this case is $0.005$~m. Each pair of particles found will be added to a list and assigned a \texttt{bondTag} which we will use in the next section to specify the type of interactions between bonded particles.


\subsubsection{Specification of bonded-particle interactions}

Once we have inserted the block of particles into the simulation object and bonded the particles together, we must specify the type of interactions between bonded particles and initialise the interaction parameters. Once again, we utilise an \texttt{InteractionGroup} with an appropriate parameter set. For this example we will create non-rotational elastic bonds between bonded particles as specified in the \texttt{NRotBondPrms} parameter set:

\begin{verbatim}
bondGrp = sim.createInteractionGroup(
   NRotBondPrms(
      name = "sphereBonds",
      normalK = 10000.0,
      breakDistance = 50.0,
      tag = 1,
      scaling = True
   )
)
\end{verbatim}

\noindent 
\texttt{NRotBondPrms} contains five parameters: a bond \texttt{tag} specifying which bonded particles will undergo this interaction, a unique \texttt{name} for the interaction group, the elastic stiffness (\texttt{normalK}) of the bonds, a boolean (\texttt{scaling}) to specify whether to scale the stiffness with particle size, and a \texttt{breakDistance} specifying the separation distance that must be exceeded in order to break a bond between two particles. When a particle-pair exceeds the \texttt{breakDistance} the bond is removed and those two particles thereafter interact according to the interactions specified for unbonded particle pairs. In a later tutorial we will illustrate how to model elastic-brittle fracture of a bonded particle assembly. In this example we have chosen a very large \texttt{breakDistance} in order to prevent fracture of our cubic block when it bounces.

Modify \texttt{gravity.py} and replace the three lines that created the single particle with the code-fragments above, then run the simulation. This time you should obtain snapshots of a cube of particles bouncing off the table. Figure~\ref{fig:gravity_cube} provides a few snapshots from this simulation.

\begin{figure}
\resizebox{\textwidth}{!}{
\includegraphics{gravity_cube_snaps.jpg}}
\caption{A sequence of snapshots of a bouncing cube of particles (from \texttt{grav\+i\+ty\_\+cube.py})} \label{fig:gravity_cube}
\end{figure}

It is instructive to note here that although we are using non-rotational spheres and interactions, the cube itself does rotate during the simulation. This is because forces between the wall and individual particles comprising the cube impart a torque to the cube as a whole when these forces do not point in the direction of the centre-of-mass of the entire cube. This is physically what we expect for this configuration and this fact can be exploited to simulate rotational dynamics without using the more computationally expensive particle-scale rotational interactions implemented in ESyS-Particle. The downside of this approach is that we must bond together a number of particles to represent an individual discrete entity in the simulation, thus increasing the total number of particles and simulation time. The decision as to which approach to use depends upon the scenario you wish to simulate. Some simulations of rotating, interacting entities do not require particle-scale rotational dynamics to achieve 
physically reasonable results.

\subsection*{What's next?}

In this section we learned how to add body forces (such as gravity and bulk viscosity) and simple walls to ESyS-Particle simulations. We also demonstrated how to bond particles together to simulate rigid bodies made out of an aggregate of spheres. The rotational dynamics observed for a cube of particles bouncing on a table arises simply by bonding the constituent particles with simple elastic bonds. The elastic bonds themselves are translational but when particles are clustered, bulk rotational dynamics (of the particle assembly) can be achieved.

The following tutorial builds upon the techniques learned in this section, introducing some new techniques for simulating two slightly more complicated scenarios: collapse of a pile of material under gravity and flow of loose granular material from a hopper or silo.  We will illustrate how to create blocks comprised of particles with variable radii, how to implement simple frictional interactions between unbonded particles, and how to exploit symmetries to improve simulation results without increasing the number of particles and hence the total computation time of simulations. We will also describe how to use triangle meshes to create walls with complicated shapes or containing holes. A hopper flow simulation will be used to illustrate how mesh walls are incorporated in ESyS-Particle simulations.

\chapter{Advanced Models}
\section{Slope failure \& hopper flow: variable particle sizes, friction \& mesh walls}

So far we have encountered examples of all the basic elements making up an ESyS-Particle simulation: a simulation object, particle assemblies, particle-particle interactions, body forces, walls and particle-wall interactions. Along the way we have learnt how to output simulation data to disk, make snapshots of particles in motion, and create re-usable \texttt{Runnables} to achieve user-defined functions during simulations. The aim of this and subsequent tutorials is to introduce a few more techniques commonly used in particle simulations including frictional interactions, brittle failure of elastic bonds, movement of walls, saving data using FieldSavers and the use of mesh walls to simulate complicated boundary geometries.

This tutorial uses slope failure and hopper flow as the motivation for the introduction of blocks comprised of particles of various sizes, frictional interactions between unbonded particles, a simple method to simulate frictional walls and the inclusion of mesh walls. We also discuss how symmetries in the problem to be solved can often be exploited to improve simulation results without greatly increasing computation time. Slope failure is an important problem in natural hazards (e.g.\ landslides), in handling of granular materials (e.g.\ forming piles of sand or grains) and in minerals processing (e.g.\ the stock-piling of ore in muckpiles). The study of hopper flow also plays its role in materials handling (e.g.\ grain storage in silos). Along the way, the examples in this tutorial will illustrate some of the key features of sand-piles, as well as introducing a few more ESyS-Particle tricks and techniques.

\subsection{Splash-down: collapse of a block of particles with variable sizes}

Until this point we have dealt predominantly with assemblies of particles with a constant particle radius. Experience has shown that often the use of uniform particle sizes and regular particle arrangements introduces artifacts such as preferential movement along lattice planes. A simple way to remove such artifacts and to model more realistic granular materials is to use particle assemblies in which the positions and radii of particles are selected randomly. ESyS-Particle provides a simple mechanism for constructing a rectangular block of particles with random positions and radii. The following code fragment achieves this:

\begin{verbatim}
geoRandomBlock = RandomBoxPacker (
   minRadius = 0.2000,
   maxRadius = 0.5000,
   cubicPackRadius = 2.2000,
   maxInsertFails = 1000,
   bBox = BoundingBox(
      Vec3(-5.0000, 0.0000,-5.0000),
      Vec3(5.0000, 10.0000, 5.0000)
   ),
   circDimList = [False, False, False],
   tolerance = 1.0000e-05
)
geoRandomBlock.generate()
geoRandomBlock_particles = geoRandomBlock.getSimpleSphereCollection()
sim.createParticles(geoRandomBlock_particles)
\end{verbatim}

To construct a random block of particles, we first create a \texttt{RandomBoxPacker} by providing a number of parameters defining the range of particle radii (\texttt{minRadius} and \texttt{maxRadius}), the geometry of the block to fill with particles (\texttt{bBox}) and some additional parameters related to the algorithm used to fill the block. We will discuss the particle packing algorithm in a few moments. The example above will construct a cube 10 metres on a side containing particles whose radii range from 0.2 metres to 0.5 metres. One corner of the cube will be located at $(X,Y,Z) = (-5,0,-5)$. 

\subsubsection{Algorithm for packing particles into a prescribed region}

The \texttt{RandomBoxPacker} and some other ESyS-Particle \texttt{Packer} modules make use of an iterative, geometrical space-filling algorithm to insert particles within a prescribed volume (a rectangular prism in the case of \texttt{RandomBoxPacker}). The algorithm may be summarised as follows:

\begin{enumerate}
\item Insert a number of seed particles at random locations within the volume ensuring they do not overlap.
\item Identify 4 adjacent particles 
\begin{enumerate}
\item compute the centroid of the tetrahedron defined by the 4 particles 
\item compute the radius of a particle that touches all 4 particles
\item if the radius of that particle is within the specified range and it is entirely within the prescribed volume, insert the particle
\end{enumerate}
\item Repeat step 2 until the number of failed insertion attempts reaches \texttt{maxInsertFails}
\end{enumerate}

\noindent
The \texttt{tolerance} parameter defines what is meant by \emph{touching} in the algorithm above. If particles overlap by less than the prescribed \texttt{tolerance}, they are said to be touching. The \texttt{cubicPackRadius} is a parameter for setting up the neighbours table used to track relative locations of adjacent particles. The optimal value for this is approximately $2.2 \times$ \texttt{maxRadius}. \texttt{circDimList} informs the packing algorithm of any circular (or periodic) boundaries so particles will be fitted together along these boundaries rather than being fitted to straight walls.

This algorithm for filling a volume with spherical particles has some distinct advantages over some other methods but also some limitations. The algorithm requires no equilibriation simulation like some dynamical methods (e.g.\ expanding particle algorithms) to achieve a close packing of relatively low porosity. The algorithm is also easily adapted for filling quite arbitrary-shaped volumes, a feature exploited in the \texttt{GenGeo} add-on library for ESyS-Particle (which we will discuss later). On the downside, the user has little control over the final distribution of particle sizes (apart from specifying the range of sizes). Experience as shown that for a broad range of sizes (e.g.\ $[0.1, 1.0]$) the final particle size distribution is a power-law. For a narrow range of sizes (e.g. $[0.4,0.6]$) the size distribution is approximately a uniform random distribution. 

\subsubsection{Simulating collapse of a cube of unbonded particles}

The aim of this example is to demonstrate the simulation of collapse of a cube of unbonded particles under the action of gravity. Suppose one had a container of loose, dry powder (such as sand or dirt) upturned on a table. When one lifts the container, one expects the powder to fall outwards forming a pile. Rather than going straight to a complete simulation of the process, we will progress in a number of stages so as to illustrate some important points about both the dynamics of sand-piles and about tricks that can be applied to simulate phenomena realistically using the Discrete Element Method.  

Write an ESyS-Particle script comprised of a cube of particles sitting atop a wall representing the table. Implement simple elastic repulsion between the particles, and between particles and the wall. In addition, implement gravity and viscosity, and add a \texttt{Runnable} to store snapshots every 1000 timesteps. Run the simulation for 50000 timesteps with a timestep increment of $10^{-4}$. If you need some help, refer to the \texttt{slope\_fail.py} code-listing in Appendix~\ref{code}. Once you have run the simulation, you should have a sequence of snapshots similar to those illustrated in Figure~\ref{fig:slope_fail_pics}. 

The most striking feature of the simulation results is the non-existence of a pile at the end of the simulation. In fact, a movie of the simulation snapshots looks remarkably like a fluid splashing down on the table (hence the name of this example: Splash-down). The reason for the discrepancy between the simulated results and our expected results lies in the simplified physics we have implemented in the model. It is well-known that the formation of sand-piles is governed by frictional interactions between the sand grains. Indeed the steepness of the pile (known as the \emph{angle of repose}) is directly related to the coefficient of inter-granular friction. In the next example we will attempt to improve our model by incorporating frictional interactions between unbonded particles.

\begin{figure}
\resizebox{\textwidth}{!}{
\includegraphics{slope_fail_snaps.jpg}}
\caption{A sequence of snapshots from a frictionless slope collapse simulation (from \texttt{slope\_\+fail.py})} \label{fig:slope_fail_pics}
\end{figure}

\subsection{Sand-piles: adding frictional interactions}

\subsubsection{Frictional interactions between unbonded particles}

As we saw in the previous example, the use of simple elastic repulsion between particles is insufficient to model formation of a sand-pile. The reason for this is postulated to be the lack of frictional interactions between the particles. In this example we will replace the \texttt{NRotElastic} Interaction Group with a new Interaction Group (called \texttt{NRotFriction}) incorporating both elastic repulsion and frictional resistance between touching particles. The following code-fragment illustrates how to incorporate frictional interactions in a simulation:

\begin{verbatim}
sim.createInteractionGroup (
   NRotFrictionPrms (
      name = "friction",
      normalK = 1000.0,
      dynamicMu = 0.6,
      shearK = 100.0,
      scaling = True
   )
)
\end{verbatim}

\noindent
In addition to the \texttt{name}, \texttt{normalK} and \texttt{scaling} parameters with which we are already familiar, \texttt{NRot\+Fric\+tionPrms} also requires two more parameters. The first parameter (\texttt{dynamicMu}) defines the coefficient of friction for touching particles whilst the second parameter (\texttt{shearK}) defines the shear stiffness at the contact point. When two particles first touch, a shear spring is created at the contact point. Forces from surrounding particles will cause the two particles to commence sliding past one-another with the shear spring resisting the motion. When the shear force exceeds the normal force multiplied by the coefficient of friction, dynamic sliding commences (as the maximum shear force is governed by the normal force and the friction coefficient). This is a simple yet effective method to simulate both static deformation at frictional contacts and dynamic frictional sliding. 

Change your \texttt{slope\_fail.py} script and replace the \texttt{NRotElasticPrms} interaction group with the code-fragment above (c.f.\ \texttt{slope\_friction.py} in Appendix~\ref{code}). Re-run the simulation and study the snapshots carefully. You should obtain a snapshot sequence similar to that of Figure~\ref{fig:slope_friction_pics}. My guess is that you will still be a little disappointed with the results. Although a pile begins to form in the early stages of the simulation, the pile eventually collapses (albeit a bit later than in the first example). Whilst the inter-particle friction has clearly had an impact, collapse of the pile is inevitable because the table is frictionless. To maintain the pile, we require frictional interactions between the particles and the table also. In the next example we will demonstrate a way to simulate frictional walls. After that we will discuss how often symmetry in the problem of interest can be exploited to produce more useful results without increasing the total 
number of particles in a simulation.

\begin{figure}
\resizebox{\textwidth}{!}{
\includegraphics{slope_friction_snaps.jpg}}
\caption{A sequence of snapshots of slope collapse with frictional particles (from \texttt{slope\_\+fric\+tion.py})} \label{fig:slope_friction_pics}
\end{figure}

\subsubsection{Frictional interactions between particles and walls}

As we saw in the previous example, adding friction between particles certainly helps to form sand-piles but the lack of friction between particles and the table means the pile soon collapses. Once particles hit the table they slide away on the frictionless surface rather than remaining as support for particles rolling down on top. What we need is a frictional surface representing the table-top. At the time of writing this tutorial, ESyS-Particle has no Interaction Group specifically for frictional interactions between particles and walls. Consequently we need to think a little laterally to model a frictional surface.

One way would be to make the wall entirely out of a slab of stationary particles that interact with the falling particles via a \texttt{NRotFriction} interaction. I'll leave it as an exercise to try that yourself. Another alternative is to attach particles at the base of the cube to the wall using the \texttt{NRotBondedWall} interaction group. Particles attached to the wall will be constrained to remain in place, oscillating around a point if large forces are applied by surrounding particles. The main reason for choosing this approach is to demonstrate another useful feature of ESyS-Particle: tagging of groups of particles so we selectively apply interactions to those particles. This is a handy feature that will be useful in a number of different scenarios.

We aim to make use of the \texttt{NRotBondedWall} Interaction Group to bond particles to the wall with unbreakable elastic bonds. The code fragment for implementing such interactions is as follows:

\begin{verbatim}
sim.createInteractionGroup (
   NRotBondedWallPrms (
      name = "floor_bonds",
      wallName = "floor",
      normalK = 10000.0,
      particleTag = 12321
   )
)
\end{verbatim}

\noindent
The first three parameters should now be familiar. The fourth parameter (\texttt{particleTag}) specifies the tag of particles that will undergo bonded elastic interactions with the wall denoted by \texttt{wallName}. All other particles that touch the wall will undergo unbonded elastic repulsion, just like previous examples. 

In order to bond specific particles to the wall, we need to assign a specific tag to those particles prior to inserting them into the simulation object. We can do this by individually inserting each particle within our random cube of particles and checking as we go whether those particles are close to the wall or not. The following code fragment achieves this:

\begin{verbatim}
for pp in geoRandomBlock_particles:
   centre = pp.getPosn()
   radius = pp.getRadius()
   Y = centre[1]
   if (Y < 1.1*radius):
      pp.setTag(12321)       # tag particles nearest to the floor
   sim.createParticle(pp)    # add the particle to the simulation object
\end{verbatim}

\noindent
Rather than simply inserting the \texttt{geoRandomBlock\_particles} directly into the simulation object with a call to \texttt{sim.createParticles(..)}, we read each particle in the list and check whether its centre is within $1.1 \times$ the radius of that particle. If so, we set the tag of the particle to $12321$, denoting this particle as one to bond to the wall. Finally a call to \texttt{sim.createParticle(..)} adds each particle to the simulation object.

Go ahead and modify \texttt{slope\_friction.py} to include the changes above. Remember to replace the \texttt{NRotElasticWall} Interaction Group with the \texttt{NRotBondedWall} Interaction Group. The complete code-listing for the modified script is called \texttt{slope\_\+fric\+tion\_\+floor.py} in Appendix~\ref{code}. Having made the necessary modifications, execute the simulation and record some snapshots. All being well, you should see a sequence of snapshots like Figure~\ref{fig:slope_friction_floor_pics}. Hurray! Finally we have simulated the formation of a sandpile, albeit only above the base of the original cube. A lot of particles still flowed away due to the frictionless surrounding area. 

Whilst one may argue this simulation is still some ways from the scenario we hoped to simulate, we have learnt quite a bit about both sand-piles and some of the features of ESyS-Particle in the process. The results of this simulation demonstrate that by bonding particles to walls we can simulate a rough frictional surface quite effectively (and quite simply). The simulation also further demonstrates that a sand-pile requires a frictional floor in order to hold itself up. Any particles rolling down outside the base of the original cube just slide away and cannot support the weight of particles falling down from above. Of course, we would much prefer to be able to keep more of the particles within the sand-pile rather than losing more than half of them from the pile. The next example achieves this to some extent and, in the process, introduces a technique of great utility in Discrete Element Modelling - the use of symmetry in the problem of interest to reduce the number of particles needed to simulate a 
phenomenon. 

\begin{figure}
\resizebox{\textwidth}{!}{
\includegraphics{slope_friction_wall_snaps.jpg}}
\caption{A sequence of snapshots of slope collapse with frictional particles and a frictional floor (from \texttt{slope\_friction\_floor.py})} \label{fig:slope_friction_floor_pics}
\end{figure}

\subsubsection{Making use of symmetry to improve simulation results}

Often the physical phenomenon one wishes to model contains geometrical symmetries that can be exploited to improve simulation results without needing to increase the total number of particles. For example, in our sand-pile simulations, the particles are initially arranged in a cubic volume which has three symmetry axes parallel with the sides of the cube. The final sand-pile is approximated by a cone which has circular symmetry around the (vertical) axis of the cone. In order to model the transformation of the cubic volume of particles into a conical pile, we really don't need to model the entire volume due to these inherent symmetries. A perfectly reasonable solution would be to model only one quarter of the cube collapsing to form one quarter of a cone. We can then rotate the final solution about the symmetry axis to obtain a reasonable representation for the final sandpile shape. The advantage is that we can model the process at higher resolution without increasing the total number of particles (and hence 
the time taken to run the simulation).

One needs to exercise some caution however to ensure that the physics along symmetry cut-planes is accurately modelled. Typically the dynamics either side of a symmetry cut-plane are the mirror image of each other, known as even symmetry. To achieve this, in most cases the use of frictionless walls along cut-planes achieves the desired result. To see how a quarter-symmetry simulation works, make the following modifications to your previous script (\texttt{slope\_friction\_floor.py}):

\begin{enumerate}
\item Add a wall located at $(X,Y,Z) = (-5,0,0)$ with a normal in the positive X-direction (i.e. $\hat{n} = (1,0,0)$). This wall will be known as the \texttt{left\_wall}. 
\item Add a second wall located at $(X,Y,Z) = (0,0,-5)$ with a normal in the positive Z-direction (i.e. $\hat{n} = (0,0,1)$). This wall will be known as the \texttt{back\_wall}. 
\item Insert two new \texttt{NRotElasticWall} Interaction Groups, one for each of the new walls.
\end{enumerate}

\noindent
Refer to \texttt{slope\_friction\_walls.py} in Appendix~\ref{code} if you are unsure how to do these steps. 

When completed, run your simulation for 100000 timesteps and record snapshots every 1000 timesteps. The results should be similar to those of Figure~\ref{fig:slope_friction_walls_pics}. Notice that we now have simulated formation of a pile that is almost twice as high as the previous example albeit we have only modelled one quarter of the sand-pile. Without increasing the number of particles we have now simulated the formation of a pile that has a basal area effectively four times that of the previous example. Although some particles still slide away on the frictionless floor, we have improved the spatial resolution of our sand-pile model quite considerably without sacrificing computation time. 

\begin{figure}
\resizebox{\textwidth}{!}{
\includegraphics{slope_friction_quarter_snaps.jpg}}
\caption{A sequence of snapshots of slope collapse utilising quarter symmetry (from \texttt{slope\_friction\_walls.py})} \label{fig:slope_friction_walls_pics}
\end{figure}

In many instances exploiting symmetries in the problem at hand can greatly increase the accuracy of results without greatly increasing computation time. However one must be careful to avoid artifacts induced by potentially inaccurate boundary conditions along symmetry cut-planes. In some cases other problem constraints demand that large numbers of particles must be used. In those instances, exploiting symmetry may be the only option. Later we will encounter another useful technique for reducing problem sizes without greatly impacting simulation results - the use of periodic boundary conditions to simulate models that are effectively semi-infinite in one coordinate direction. 

\subsection{Hopper flow: Using quarter symmetry and mesh walls}

Now that you've discovered the joys of slope collapse, there are really only a couple of steps to take this simulation and use it to study hopper flows. The flow of material in silos encounters many problems in the real world. These include rat-holing, stalling of flow around the exit, uneven draw profiles and unexpected segregation of finer material to name a few. The study of granular flow is a dynamic problem requiring many timesteps of simulation. In the previous section we learnt how to use quarter symmetry to improve simulation performance; hopper flow is another application that benefits from the use of symmetry. By using quarter symmetry planes around an exit point and modelling those planes using frictionless walls we can simulate models representative of hoppers of much larger capacity.

\subsubsection{The mesh wall file format}

So far we have only used planar walls in our simulations. Planar walls, by their definition, are infinite in length, making it difficult to simulate problems that require complex wall shapes or walls with holes. Mesh walls overcome this problem but are slightly more complicated to implement in simulations. ESyS-Particle uses a triangulated mesh format to define piecewise segments of a wall. Generation of these meshes can be done through most CAD packages but require a little work to convert into the ESyS-Particle mesh format. An example mesh file is shown below for an "L" shaped floor mesh that we will use in the upcoming example:

\begin{verbatim}
Triangle
3D-Nodes 6
 0  0 0  -5.0  0.0   0.0
 1  1 0  -5.0  0.0   5.0
 2  2 0   0.0  0.0  -5.0
 3  3 0   0.0  0.0   0.0
 4  4 0   5.0  0.0  -5.0
 5  5 0   5.0  0.0   5.0

Tri3 4
 0  0  0  3  1
 1  0  1  3  5
 2  0  5  3  4
 3  0  3  2  4
\end{verbatim}

The mesh file commences with a format header \texttt{Triangle}; the number after \texttt{3D-\+Nodes} specifies how many nodal points exist in the mesh followed by the specification of locations of these points in the format:
\begin{verbatim}
ID Dummy Tag X Y Z
\end{verbatim}

The number after \texttt{Tri3} specifies how many triangle elements exist in the mesh. Normals of triangles are considered to be pointing out of the page using an                                                         anti-clockwise piecewise specification, regarding which see Figure~\ref{fig:trimesh_normal}. The tri-elements link the nodes in the following manner:
\begin{verbatim}
ID Tag Point1 Point2 Point3
\end{verbatim}

\begin{figure}
\begin{center}
\resizebox{3in}{!}{
\includegraphics{trimesh_normal.jpg}}
\end{center}
\caption{Specification of mesh nodes for wall normals} \label{fig:trimesh_normal}
\end{figure}

\subsubsection{Using mesh walls in hopper flow simulations}

Save the triangle mesh wall specification above in a file called \texttt{floorMesh.msh}. We can now read the mesh wall into a simulation object using the following code fragment:

\begin{verbatim}
sim.readMesh(
   fileName = "floorMesh.msh",
   meshName = "floor_mesh_wall"
)
\end{verbatim}

The variable \texttt{meshName} will be used later to create an interaction group between the particles and the mesh wall, just as we do for planar walls. Let's get started by editing our \texttt{slope\_friction\_walls.py} script to replace the bonded floor with a mesh wall:

\begin{enumerate}
\item Replace the current \texttt{floor} wall code with the above code fragment to import the mesh wall instead.
\item Replace the current interaction group \texttt{floor\_bonds} with the following interaction group for mesh walls:

\begin{verbatim}
sim.createInteractionGroup (
   NRotElasticTriMeshPrms (
      name = "floorWall_repell",
      meshName = "floor_mesh_wall",
      normalK = 1.0000e+04
   )
)
\end{verbatim}

This interaction group creates a non-rotational elastic interaction that repels  particles that make contact with the mesh wall, essentially just like a planar wall. We also wish to constrain our hopper so that particles may only exit via the hole in the mesh wall. We can do this by adding front and right walls to our simulation, in addition to the left and back walls added in the previous example:
\item Add a third wall located at $(X,Y,Z) = (5,0,0)$ with a normal in the negative X-direction (i.e. $\hat{n} = (-1,0,0)$). This wall will be known as the \texttt{right\_wall}.
\item Add a fourth wall located at $(X,Y,Z) = (0,0,5)$ with a normal in the negative Z-direction (i.e. $\hat{n} = (0,0,-1)$). This wall will be known as the \texttt{front\_wall}.
\item Insert two more \texttt{NRotElasticWall} Interaction Groups, one for each of the new walls.
\item Finally we will change the tagging of the particles for visualisation purposes. Change the tagging code fragment from the previous example to the following: code to the fragment below will colour the particles in layers of 2m in the Y direction:

\begin{verbatim}
#add particles to simulation one at a time,
#tagging those in layers of 2m
for pp in geoRandomBlock_particles:
   centre = pp.getPosn()
   Y = centre[1]
   if (int(Y%4) < 2):
      pp.setTag(1) # layer 1
   else:
      pp.setTag(2) # layer 2
   sim.createParticle(pp) # add particle to the simulation object
\end{verbatim}

The new tagging code will tag particles with one of two different tags, in layers of 2m in the vertical Y-direction.  As mentioned before, particle tags can be used for a range of simulation-related functions.  In this example we have used tagging to identify the initial location of particles in the vertical direction. This makes it easier for us to visualise the flow of particles later.
\end{enumerate}

Congratulations, you have now made all of the changes to the code required to do a hopper flow simulation. Run your code and you should expect something as in Figure~\ref{fig:hopper_flow}. If not, refer to \texttt{hopper\_flow.py} in Appendix~\ref{code}.

As you have seen, mesh walls are a powerful and flexible feature of ESyS-Particle that allow complex shapes and interactions to be simulated. Discussing the model above, two unrealistic simplifications have been used that results in hopper flow dynamics somewhat different to that of real hoppers. Firstly, we use non-rotational particles as our granular media. This consequence of this is that friction between particles is higher than reality; particles are not able to roll over one another. The consequent higher bulk friction results in more frequent rat-holing or stalling of flow around the outlet. The second simplification is the use of a frictionless mesh wall as the base of the hopper. As with the slope collapse example in the previous section, particles can freely slide along the base surface as there is no shear/frictional component to the interaction. This will result in a wider flow pattern than usual, with particles sliding towards the outlet more easily. A more realistic simulation would include 
frictional interactions at the base of the hopper, perhaps by using the bonding technique introduced in the example above.

\begin{figure}
\begin{center}
\resizebox{2.5in}{!}{
\includegraphics{hopper_flow.jpg}}
\end{center}
\caption{Hopper flow using a mesh base in quarter symmetry (from \texttt{hopper\_flow.py})} \label{fig:hopper_flow}
\end{figure}

\subsection*{What's next?}

This chapter has introduced a number of new features of ESyS-Particle as well as some tricks and hints for obtaining more realistic simulation results without greatly increasing the mathematical complexity of the model. We have seen how to create a block of variable-sized particles at random locations. This avoids unphysical dynamics often encountered when using particles of equal size in regular crystalline packing arrangements. Techniques for tagging particles and bonding these tagged particles to walls, as well as introduction of mesh walls, provide methods to simulate complex boundary conditions including frictional walls of a variety of shapes. For problems that allow it, the use of symmetry-planes can increase the resolution of simulation results without greatly increasing the computation time.  

In the following tutorial we examine elastic-brittle failure of rock samples under uniaxial compression. Simulations of uniaxial compression are particularly important in Discrete Element Modelling, providing a way to calibrate numerical models so that the macroscopic physical properties (such as elastic moduli and failure strength) match those of real rocks. In order to simulate rock breakage under uniaxial loads, we will need to implement movable walls and introduce rotational cementatious elastic-brittle bonds. We will also examine the use of \texttt{FieldSavers} to selectively store simulation data such as wall forces, total strain energy and the number of broken bonds. Particular attention will be paid to how one may calibrate DEM simulations to achieve desired macroscopic properties. 
%This will lead on to the topic of the following tutorial: measurement of the macroscopic friction angle of loose granular materials via shear cell simulations.

\newpage
\section{Uniaxial compression simulations: moving walls, model calibration and \texttt{FieldSavers}}

Quasi-static uniaxial compression of rock samples is a common laboratory technique for measuring the macroscopic properties of rocks such as Young's modulus, Poisson's ratio and the unconfined compressive strength. In uniaxial compression experiments, a sample of rock is slowly compressed by a piston until failure occurs. By measuring the force applied to the sample and the strain, one can measure the Young's modulus of the rock sample. The peak stress at which failure of the sample occurs is known as the Unconfined Compressive Strength (UCS), an important measure of the strength of rocks. 

In this tutorial we will describe how to perform uniaxial compression simulations using ESyS-Particle. Such simulations provide a means to measure the equivalent macroscopic properties of synthetic rock samples and are an important tool for calibrating Discrete Element models. When discussing calibration it is important to distinguish between the microphysical parameters defining interaction laws (e.g.\ elastic stiffnesses, coefficients of friction, etc.) and the macroscopic elastic properties (Young's modulus, macroscopic friction angle, etc.). The later are a function not only of the microphysical parameters, but also the geometrical properties of the network of bonded and unbonded interactions within the particle assembly. There is often no known analytical relationship between the microphysical parameters and the macroscopic properties (except in special cases of regular packings of equal-sized particles). To overcome this, it is common procedure to conduct a series of uniaxial compression simulations to 
tune the microphysical parameters until desired macroscopic properties are obtained.

To simulate rock breakage under compressive loads, we will need to introduce a number of new ESyS-Particle techniques. These include:

\begin{enumerate}
\item implementation of cementatious (rotational) elastic-brittle bonds and rotational friction interactions,
\item moving walls via a \texttt{Runnable}, and
\item selective storage of simulation data using \texttt{FieldSavers}.
\end{enumerate}

\noindent
Rather than incrementally building upon previous examples, this tutorial is written stand-alone, containing all code-fragments needed to implement uniaxial compression simulations. We will also discuss how to measure macroscopic elastic properties using simulation results.

\subsection{Uniaxial compression simulations}

In this example we will discuss how to implement a uniaxial compression simulation using ESyS-Particle. The model will consist of a rectangular prism of particles sandwiched between two piston walls which will be compressed at constant speed. We will utilise \texttt{FieldSavers} to monitor the positions of, and forces acting on, the walls, as well as the total strain energy stored in bonds between particles and the number of broken bonds. These simulation data will then be used to measure the macroscopic elastic properties of the particle model. Full code-listings for uniaxial compression simulations are available in Appendix~\ref{code}.

%Since calibrating microscopic model parameters to achieve desired macroscopic elastic properties is the main objective, we will take more care in this tutorial to choose realistic parameter values. To do this requires that we choose a system of units of measure that is self-consistant and practical. ESyS-Particle is coded so that the choice of units of measure is arbitrary. The user may decide whether to use measurement units in MKS (metres, kilograms and seconds), CGS (centimetres, grams and second) or another system defining the units of Length, Mass and Time. No assumptions are made within ESyS-Particle a priori about the units of measure except that by default, particles have a density equal to 1.0. In the following example we will explicitly set the density of particles. 

%For the following example, we will use millimetres, Newtons and milliseconds as our fundamental units of measure. These are selected primarily for convenience. Laboratory uniaxial compression tests typically utilise rock samples approximately 50mm in diameter and around 100mm long. The Young's moduli of rocks are of order $40-80$GPa and peak strengths are between 100MPa and 250MPa. By using millimeters and Newtons as fundamental units, stresses in our simulation will be in units of N/mm/mm, which is equivalent to MPa. Similarly, by choosing milliseconds as out third unit of measure, velocities will be reported in m/sec and masses will be measured in grams. There is no reason why we could not have used MKS units in these simulations, but this selection just makes measuring results easier for calibration purposes. 

\subsubsection{Initialising the simulation object}

Every ESyS-Particle simulation requires a \texttt{LsmMpi} simulation object. This object serves as a container to which we can add particles, walls, interactions and other modules for storing data or moving walls. The following code-fragment loads the ESyS-Particle Python modules and initialises the simulation object:

\begin{verbatim}
#import the appropriate ESyS-Particle modules:
from esys.lsm import *
from esys.lsm.util import *
from esys.lsm.geometry import *

#instantiate a simulation object:
sim = LsmMpi (numWorkerProcesses = 1, mpiDimList = [1,1,1])

#initialise the neighbour search algorithm:
sim.initNeighbourSearch (
   particleType = "RotSphere",
   gridSpacing = 5.0000,
   verletDist = 0.08000
)

#set the number of timesteps and timestep increment:
sim.setNumTimeSteps (250000)
sim.setTimeStepSize (1.0000e-06)

#specify the spatial domain for the simulation
domain = BoundingBox(Vec3(-20,-20,-20), Vec3(20,20,20))
sim.setSpatialDomain (domain)
\end{verbatim}

\noindent
Most of these commands should now be familiar from previous tutorials. We first import a number of useful ESyS-Particle modules and create an instance of the \texttt{LsmMpi} simulation object. Whilst initialising the neighbour search algorithm, we specify that particles are of type \texttt{RotSphere} (unlike previous examples where we used \texttt{NRotSphere} particles). Subsequently we set the number of timesteps, the timestep size and the extents of the spatial domain. The simulation object is now initialised and ready to insert particles, interactions, walls, etc.

\subsubsection{Creating a block of variable-sized particles}
\label{sec:block_in_sim}

Having initialised the simulation object, we need to insert particles and bond them together. For rock breakage simulations, the best results are obtained using blocks of particles with variable radii and random locations. We will use the \texttt{RandomBoxPacker} first encountered in the previous tutorial:

\begin{verbatim}
#create a prism of spherical particles:
geoRandomBlock = RandomBoxPacker (
   minRadius = 0.400,
   maxRadius = 2.0000,
   cubicPackRadius = 2.2000,
   maxInsertFails = 5000,
   bBox = BoundingBox(
      Vec3(-5.0000, 0.0000,-5.0000),
      Vec3(5.0000, 20.0000, 5.0000)
   ),
   circDimList = [False, False, False],
   tolerance = 1.0000e-05
)
geoRandomBlock.generate()
geoRandomBlock_particles = geoRandomBlock.getSimpleSphereCollection()

#add the particles to the simulation object:
sim.createParticles(geoRandomBlock_particles)

#bond particles together with bondTag = 1:
sim.createConnections(
   ConnectionFinder(
      maxDist = 0.005,
      bondTag = 1,
      pList = geoRandomBlock_particles
   )
)
\end{verbatim}

\noindent
This code-fragment generates a rectangular prism of particles whose radii lie in the range $[0.4, 2.0]$~mm. The prism is $10 \times 20 \times 10$~mm in size with the centre of the base at the origin. The final command adds the prism of particles to the simulation object. For more information about the \texttt{RandomBoxPacker} and the ESyS-Particle particle packing algorithm, please refer to the previous tutorial. The last section of this code-fragment uses a \texttt{ConnectionFinder} to find pairs of particles within a distance of 0.005~mm of each other. Each particle pair is tagged with a \texttt{bondTag} that we will use to specify the type of interactions between bonded particles below. We first encountered the \texttt{ConnectionFinder} in \texttt{gravity\_cube.py} when we created a bonded cube of particles to bounce on the floor.

\subsubsection{Adding walls to the simulation object}

Next we need to add two walls to the simulation object. These walls will serve as pistons for compressing the rock sample. We will add one wall below the sample (the \texttt{bottom\_wall}) and another atop the sample (the \texttt{top\_wall}):

\begin{verbatim}
#create a wall at the bottom of the model:
sim.createWall (
   name = "bottom_wall",
   posn = Vec3(0.0000, 0.0000, 0.0000),
   normal = Vec3(0.0000, 1.0000, 0.0000)
)

#create a wall at the top of the model:
sim.createWall (
   name = "top_wall",
   posn = Vec3(0.0000, 20.0000, 0.0000),
   normal = Vec3(0.0000, -1.0000, 0.0000)
)
\end{verbatim}

\noindent
Simply adding walls to the simulation object is insufficient. We must also define interactions between the walls and particles. For basic uniaxial compression simulations, repulsive elastic interactions are sufficient. If we were interested in tensile loading, we would need to bond the walls to particles at the base and top of the model. Bonding walls to particles was covered in the previous tutorial. For now, the following particle-wall interactions are all that are required:

\begin{verbatim} 
#specify elastic repulsion from the bottom wall:
sim.createInteractionGroup (
   NRotElasticWallPrms (
      name = "bottom_wall_repel",
      wallName = "bottom_wall",
      normalK = 100000.0
   )
)

#specify elastic repulsion from the top wall:
sim.createInteractionGroup (
   NRotElasticWallPrms (
      name = "top_wall_repel",
      wallName = "top_wall",
      normalK = 100000.0
   )
)
\end{verbatim} 

\noindent
These two code-fragments specify elastic repulsion from the \texttt{bottom\_wall} and \texttt{top\_wall}. Each Interaction Group is assigned a unique name that can be used to selectively store various data about interactions using \texttt{FieldSavers}. Elastic repulsion of particles from walls requires specification of a single microphysical parameter, the elastic stiffness (\texttt{normalK}). For this simulation we set the elastic stiffness equal to $100000$~N/mm.

%In the next section we will discuss how to rescale from simulation units to real units in the context of measuring macroscopic elastic properties. 

\subsubsection{Rotational bonds and frictional interactions}

In previous examples we have encountered two types of particle interactions: \texttt{NRotElastic} and \texttt{NRotFriction} interactions. Both of these interactions are designed for particles with only three (translational) degrees of freedom (known as \texttt{NRotSpheres}) and are suitable for granular flow of individual particles or aggregates. To simulate elastic-brittle failure of rocks, more sophisticated particle-pair interactions are required. In particular, we require particle-pair interactions that incorporate both translational and rotational degrees of freedom. As illustrated in Figure~\ref{fig:rot_bond_diagram}, two bonded particles may undergo normal and shear forces, as well as bending and twisting moments. Bonds designed to impart such forces and moments are known as cementatious bonds (or in ESyS-Particle parlance \texttt{BrittleBeamPrms} interactions). 

\begin{figure}
\begin{minipage}{3in}
\begin{center}
\resizebox{2in}{!}{
\includegraphics{rotational_bonds.jpg}}
\end{center}
\end{minipage}
\begin{minipage}{3in}
\caption{Diagram illustrating the forces and moments between particles bonded via rotational elastic-brittle bonds} \label{fig:rot_bond_diagram}
\end{minipage}
\end{figure}


There is also a \texttt{FrictionPrms} interaction specifically designed for frictional interactions between unbonded rotational particles. Unlike the non-rotational equivalent, rotational frictional interactions impart a torque to both particles, causing the particles to rotate relative to each other when in frictional contact. 

The following code-fragment defines the microphysical parameters of rotational bonds: 

\begin{verbatim}
#create rotational elastic-brittle bonds between particles:
pp_bonds = sim.createInteractionGroup (
   BrittleBeamPrms(
      name="pp_bonds",
      youngsModulus=100000.0,
      poissonsRatio=0.25,
      cohesion=100.0,
      tanAngle=1.0,
      tag=1
   )
)
\end{verbatim}

\noindent
The \texttt{tag} parameter is used to specify which particle-pairs should be bonded together. The bond tags were assigned by the \texttt{ConnectionFinder} in the previous section. 

%It should be emphasised that the Young's Modulus, Poisson's ratio, cohesion and internal angle of friction (\texttt{tanAngle}) refer to the microphysical properties of individual brittle-elastic beams connecting two particles. Although the macroscopic elastic properties of the bonded particle assembly are strongly related to these parameters, there is typically not a one-to-one correspondence.

%Rotational brittle-elastic bonds require specification of four parameters: two governing elastic properties of bonds and two governing the breaking strength of bonds. 

The physical interpretation of rotational bonds is that two particles are connected to one another with a cylindrical elastic beam whose radius is the mean of the radii of the bonded particles and whose equilibrium length is the sum of the radii of those particles. The elasticity of bonds is determined by a microscopic Young's modulus (\texttt{youngsModulus} parameter) and a microscopic Poisson's ratio (\texttt{poissonsRatio} parameter). It should be emphasised that the macroscopic elastic properties of an assembly of bonded particles does not necessarily match the microscopic elastic properties of the bonds themselves. The topology of the bond network in a particle assembly also influences its macroscopic elastic properties.

In order to simulate brittle failure of samples, bonds require a failure threshold criterion (or breaking strength). In ESyS-Particle, a Mohr-Coulomb failure criterion is employed. A bond will fail (or break) if the shear stress within the bond exceeds its shear strength ($\tau$) given by:

\begin{equation}
\tau = \mathcal{C} + \sigma_N \tan (\phi_f)
\end{equation}

\noindent
where $\mathcal{C}$ is the cohesive strength of the bond for zero normal stress ($\sigma_N$) and $\phi_f$ is the internal angle of friction of the bond. The \texttt{cohesion} and \texttt{tanAngle} parameters respectively define the cohesive strength and friction angle of bonds.

%These breakage forces determine a threshold failure condition that once exceeded, causes the bond to break irreversibly. The threshold failure condition is defined as follows:

%\begin{equation}
%\frac{| F_n |}{F^{brk}_n} + \frac{| F_s |}{F^{brk}_s} + \frac{| F_b |}{F^{brk}_b} + \frac{| F_t |}{F^{brk}_t} > 1 \ \ \ .
%\end{equation}

%\vskip 5mm
%\begin{minipage}{5.75in}
%\emph{\textbf{IMPORTANT:} Since version $2.0$ of ESyS-Particle, the implementation of rotational bonded and frictional interactions has changed slightly to fix a problem with the scaling of elastic properties of models containing particles with differing radii (as is the case in this tutorial). By default, ESyS-Particle now interprets elastic stiffness parameters (e.g. \texttt{normalK} and \texttt{torsionK}) as elastic contact moduli (in stress) units. Internally ESyS-Particle multiplies these constants by the mean radius of two bonded (or touching) particles to compute a per-bond elastic stiffness. Similarly the breaking force parameters are interpreted as yield stresses. ESyS-Particle multiplies the yield stress parameters by the mean cross-sectional area of bonded particles to compute per-bond breaking forces. If you do not wish to apply this scaling of elastic properties, you need to set \texttt{scaling = False} in the specification of \texttt{RotBondPrms} and \texttt{RotFrictionPrms}. If you do not include the \texttt{scaling} parameter when specifying these types of interactions, ESyS-Particle assumes by default that \texttt{scaling = True} and will issue a warning to remind you about the \texttt{scaling} parameter.
%\end{minipage}
%\vskip 5mm

When a bond between two particles breaks, we need to specify the type of unbonded interactions the particles will experience should they come into contact. Since a broken bond represents a fracture surface, it is appropriate to specify frictional interactions between unbonded particles. The following code-fragment implements frictional interactions between unbonded, touching particles:

\begin{verbatim}
#initialise frictional interactions for unbonded particles:
sim.createInteractionGroup (
   FrictionPrms(
      name="friction",
      youngsModulus=100000.0,
      poissonsRatio=0.25,
      dynamicMu=0.4,
      staticMu=0.6
   )
)
\end{verbatim}

\noindent
Rotational frictional interactions are defined by a microscopic Young's modulus (\texttt{youngs\+Mod\+u\+lus}) and Poisson's ratio (\texttt{poissonsRatio}) and two microscopic coefficients of friction. Typically the Young's modulus and Poisson's ratio for \texttt{FrictionPrms} interactions are set equal to their \texttt{BrittleBeamPrms} counterparts. The \texttt{staticMu} coefficient of friction is applied when two particles are in static frictional contact, i.e., prior to the first time the frictional sliding criterion is met. Thereafter the \texttt{dynamicMu} coefficient of friction is applied. By setting $\texttt{dynamicMu} < \texttt{staticMu}$, one can simulate the physical observation that the frictional force required to maintain sliding is less than the force necessary to initiate sliding.

Finally, we must inform the simulation object that any given particle-pair undergoes either bonded interactions or frictional interactions but not both. This is achieved by specifying an \emph{exclusion} between the two interaction groups:

\begin{verbatim}
#create an exclusion between bonded and frictional interactions:
sim.createExclusion (
   interactionName1 = "pp_bonds",
   interactionName2 = "friction"
)
\end{verbatim}

\subsubsection{Implementation of viscous damping}

Uniaxial compression experiments are usually conducted in the so-called quasi-static regime. In other words, external loads are applied slowly compared with the compressional wavespeed of the sample. Any acoustic emissions generated during fracturing dissipate rapidly compared with the duration of the experiment. To simulate these conditions, we must also incorporate two body forces designed to attenuate translational and rotational oscillations. In previous examples we encountered the \texttt{LinDamping} body force. In the uniaxial compression simulations we will use both \texttt{LinDamping} and \texttt{RotDamping}, the later being designed to attenuate rotational oscillations. The two damping forces are implemented thus:

\begin{verbatim}
#add translational viscous damping:
sim.createInteractionGroup (
   LinDampingPrms(
      name="damping1",
      viscosity=0.002,
      maxIterations=50
   )
)

#add rotational viscous damping:
sim.createInteractionGroup (
   RotDampingPrms(
      name="damping2",
      viscosity=0.002,
      maxIterations=50
   )
)
\end{verbatim}

\noindent
The viscosity coefficients are chosen to be small so that damping has little effect on the elastic response of the simulated rock sample but it is sufficient to attenuate unwanted oscillations. 

\subsubsection{Implementation of movable walls: the \texttt{WallLoader} \texttt{Runnable}}

Only one component remains to be added to the uniaxial compression simulation: a method to move the two walls at constant speed. There are a couple of ways this may be implemented: either  as a subroutine that is called each timestep of the simulation or as a reusable \texttt{Runnable} module. We previously encountered \texttt{Runnables} in the first tutorial. The use of a \texttt{Runnable} is considered superior as the module can be re-used in subsequent simulations whenever movable walls are required. 

There are two steps involved in implementing and using a \texttt{Runnable}:
\begin{enumerate}
\item Write a script containing the implementation of the \texttt{Runnable}, and
\item Add the runnable into the simulation container.
\end{enumerate}

\noindent
The following code (entitled \texttt{WallLoader.py} in Appendix~\ref{code}) implements a \texttt{Runnable} to move a given wall with a specified velocity. To achieve this, the \texttt{Runnable} must be passed a reference to the simulation object, a wall name, a velocity and two additional parameters: \texttt{startTime} and \texttt{rampTime}. Experience has shown that it is best to gradually increase the wall speed from zero to the desired value over a few hundred timesteps. The \texttt{rampTime} parameter specifies the number of timesteps during which the wall accelerates. The velocity of the wall increases linearly over that number of timesteps. The other parameter (\texttt{startTime}) allows the user to commence moving the wall after a specified number of timesteps. 

The \texttt{WallLoader} \texttt{Runnable} has two subroutines:
\begin{itemize} 
\item \texttt{\_\_init\_\_()} to initialise the \texttt{Runnable} and store parameter values, and
\item \texttt{run()} which moves the wall and is called once each timestep of the simulation.
\end{itemize} 

\noindent
The implementation of the \texttt{WallLoader} \texttt{Runnable} is as follows:

\begin{verbatim}
#import the appropriate ESyS-Particle modules:
from esys.lsm import *
from esys.lsm.util import *

class WallLoaderRunnable (Runnable):
   def __init__ (self, 
                 LsmMpi=None, 
                 wallName=None, 
                 vPlate=Vec3(0,0,0), 
                 startTime=0, 
                 rampTime = 200):
      """
      Subroutine to initialise the Runnable and store parameter values.
      """
      Runnable.__init__(self)
      self.sim = LsmMpi
      self.wallName = wallName
      self.Vplate = vPlate
      self.dt = self.sim.getTimeStepSize()
      self.rampTime = rampTime
      self.startTime = startTime
      self.Nt = 0

   def run (self):
      """
      Subroutine to move the specified wall. After self.startTime 
      timesteps, the speed of the wall increases linearly over 
      self.rampTime timesteps until the desired wall speed is achieved. 
      Thereafter the wall is moved at that speed.
      """
      if (self.Nt >= self.startTime):

         #compute the slowdown factor if still accelerating the wall:
         if (self.Nt < (self.startTime + self.rampTime)):
            f = float(self.Nt - self.startTime) / float(self.rampTime)
         else:
            f = 1.0

         #compute the amount by which to move the wall this timestep:
         Dplate = Vec3(
            f*self.Vplate[0]*self.dt, 
            f*self.Vplate[1]*self.dt, 
            f*self.Vplate[2]*self.dt
         )
         #instruct the simulation to move the wall:
         self.sim.moveWallBy (self.wallName, Dplate)

      #count the number of timesteps completed thus far:
      self.Nt += 1
\end{verbatim}

\noindent
Copy the code above into a text file called \texttt{WallLoader.py} and save it in the same directory as the file containing the code for the uniaxial compression simulation (called \texttt{rot\_compress.py} in Appendix~\ref{code}).

To use this \texttt{Runnable} in the uniaxial compression simulation, we must do two things: 
\begin{enumerate}
\item import the Runnable at the start of the script like so:
\begin{verbatim}
from WallLoader import WallLoaderRunnable
\end{verbatim}
\item add a \texttt{WallLoaderRunnable} for each of the two piston walls:
\begin{verbatim}
#add a wall loader to move the top wall:
wall_loader1 = WallLoaderRunnable(
   LsmMpi = sim,
   wallName = "top_wall",
   vPlate = Vec3 (0.0, -0.125, 0.0),
   startTime = 0,
   rampTime = 50000
)
sim.addPreTimeStepRunnable (wall_loader1)

#add a wall loader to move the bottom wall:
wall_loader2 = WallLoaderRunnable(
   LsmMpi = sim,
   wallName = "bottom_wall",
   vPlate = Vec3 (0.0, 0.125, 0.0),
   startTime = 0,
   rampTime = 50000
)
sim.addPreTimeStepRunnable (wall_loader2)
\end{verbatim}
\end{enumerate}

\noindent
Notice that the sign of the wall velocity (\texttt{vPlate}) is opposite for the two walls. The top wall will move downwards and the bottom wall upwards, both at a speed of $0.125$~m/s. Although this rate is significantly higher than that typically used in laboratory uniaxial compression experiments, it is sufficiently small to maintain quasi-static conditions in the simulations. The piston speeds are approximately $20000 \times$ lower than the compressional wavespeed of the simulated rock sample. The initial acceleration of the walls from zero to the desired speed (during the first $50000$ timesteps) also helps ensure the sample is loaded quasi-statically. 

Technically the simulation object now contains all of the components needed for a uniaxial compression simulation.  The only remaining item is to instruct the simulation to execute all timesteps. This is achieved with the following command:

\begin{verbatim}
#execute the simulation:
sim.run()
\end{verbatim}

\noindent
If you have been following the previous tutorials, you may notice that we have not specified how data should be output during the simulation. This is covered in detail in the next section, where we discuss the use of \texttt{FieldSavers} to store only the data of specific interest during uniaxial compression simulations.

\subsection{Measurement of macroscopic elastic properties}

One of the primary reasons for conducting uniaxial compression simulations is to measure the Young's modulus and Unconfined Compressive Strength (UCS) of the rock sample. Both of these macroscopic properties can be obtained from a stress--strain curve, as illustrated in Figure~\ref{fig:stress_strain_diag}. Young's modulus is defined as the slope of the linear section of the stress--strain curve, whilst the UCS is the peak value of the stress. In order to measure these quantities in a simulation, we must construct the stress--strain curve for the simulation. 

\begin{figure}
\begin{center}
\resizebox{5in}{!}{
\includegraphics{stress_strain_diagram.jpg}}
\end{center}
\caption[Diagram illustrating a typical stress-strain curve \& how to measure Young's modulus ($E$) \& the unconfined compressive strength (UCS) from such a curve]{Diagram illustrating a typical stress-strain curve and how to measure Young's modulus ($E$) and the unconfined compressive strength (UCS) from such a curve} \label{fig:stress_strain_diag}
\end{figure}

Suppose the net restoring forces (at time $t$) that particles apply to the top and bottom walls are $\vec{F}^{(t)} (t)$ and $\vec{F}^{(b)} (t)$ respectively, and the unit normal vector of the walls is $\hat{n}^{(t/b)}$. Then the stress exerted on the walls by the particle assembly is:

\begin{equation}
\sigma_{YY} (t) = \frac{\vec{F}^{(t)} . \hat{n}^{(t)} + \vec{F}^{(b)} . \hat{n}^{(b)}}{2 A_c} \ \ \ , \label{eq:stress}
\end{equation}

\noindent
where $A_c$ is contact area between a wall and the particle assembly. Since the peak stress is reached for relatively small axial strain (only a few \% usually), the contact area can be approximated by the undeformed area of base of the particle assembly (in our example, that would be $10 \mathrm{m} \times 10 \mathrm{m} = 100 \mathrm{m}^2$). 

Computing the total strain is also relatively straightforward. Let $\vec{X}^{(t)} (t)$ and $\vec{X}^{(b)} (t)$ be the positions of the top and bottom walls at time $t$. The strain $\varepsilon_{YY} (t)$ is given by:

\begin{eqnarray} 
\varepsilon_{YY} (t) & = & \frac{\left[ \left( \vec{X}^{(t)} (0) - \vec{X}^{(b)} (0) \right) - \left( \vec{X}^{(t)} (t) - \vec{X}^{(b)} (t) \right) \right] . \hat{y}}{\left[ \left( \vec{X}^{(t)} (0) - \vec{X}^{(b)} (0) \right) \right] . \hat{y}} \\
 & = & 1 - \frac{\left[ \vec{X}^{(t)} (t) - \vec{X}^{(b)} (t) \right] . \hat{y}}{\left[ \vec{X}^{(t)} (0) - \vec{X}^{(b)} (0) \right] . \hat{y}} \label{eq:strain}
\end{eqnarray} 

\noindent
where $\hat{y}$ is the unit vector in the direction normal to the bottom wall.

From the two formulae above, it is evident that to compute the stress--strain curve, we need to record the net force acting on each wall and the position of each wall at each timestep of the simulation. The next section describes how to use \texttt{FieldSavers} to store the forces and positions of walls. In the following section, we will examine how to read the output files and construct the stress--strain curve for a simulation.

\subsubsection{Storing wall positions and forces}

ESyS-Particle includes a group of modules called \texttt{FieldSavers} designed to store specific simulation data to disk. \texttt{FieldSavers} are closely related to the \texttt{CheckPointer} encountered in the first tutorial, the main difference being that \texttt{FieldSavers} store only specific data rather than all of the state variables of the particles. \texttt{FieldSavers} can also be used to store data on particles (such as position or kinetic energy), interactions (such as potential energy and the number of broken bonds), and walls (such as the position of a wall and the net force acting on the wall). 

As discussed in the previous section, we need to store the wall forces and positions in order to construct the stress--strain curve for our uniaxial compression simulations. The following code-fragment initialises a \texttt{FieldSaver} to store wall positions:

\begin{verbatim} 
#create a FieldSaver to wall positions:
posn_saver = WallVectorFieldSaverPrms(
   wallName=["bottom_wall", "top_wall"],
   fieldName="Position",
   fileName="out_Position.dat",
   fileFormat="RAW_SERIES",
   beginTimeStep=0,
   endTimeStep=250000,
   timeStepIncr=10
)
sim.createFieldSaver(posn_saver)
\end{verbatim} 

\noindent
This code-fragment should be inserted before \texttt{sim.run()} is called.  \texttt{WallVec\+torFieldSav\+erPrms} takes a number of parameters. These are:

\begin{itemize}
\item \texttt{wallName} -- a list of the names of walls whose position you wish to save
\item \texttt{fieldName} -- the data field to store (in this case ``Position'')
\item \texttt{fileName} -- the name of the text file in which to store the data
\item \texttt{fileFormat} -- the output format (\texttt{RAW\_SERIES} creates an ASCII text file)
\item \texttt{beginTimeStep} -- the timestep number to commence storing data
\item \texttt{endTimeStep} -- the timestep number to conclude storing data
\item \texttt{timeStepIncr} -- the number of timesteps to wait between storing a datum
\end{itemize}

\noindent
Storing wall forces is very similar -- one need only specify a \texttt{fieldName} of \texttt{Force} and change the \texttt{fileName}. The following code fragment will initialise a \texttt{FieldSaver} to store wall forces:

\begin{verbatim}
#create a FieldSaver to wall forces:
force_saver = WallVectorFieldSaverPrms(
   wallName=["bottom_wall", "top_wall"],
   fieldName="Force",
   fileName="out_Force.dat",
   fileFormat="RAW_SERIES",
   beginTimeStep=0,
   endTimeStep=250000,
   timeStepIncr=10
)
sim.createFieldSaver(force_saver)
\end{verbatim}

\subsubsection[Measurement of Young's modulus \& unconfined compressive strength]{Measurement of Young's modulus and unconfined compressive strength}

Once you have written all the code-fragments above into a text file (called \texttt{rot\_\+com\+press.py}) and created a \texttt{WallLoader.py} file containing the implementation of the \texttt{Wall\+Load\+er} \texttt{Runnable}, execute a simulation by typing the following at the command prompt:

\begin{verbatim}
$ mpirun -np 2 `which esysparticle` rot_compress.py
\end{verbatim}

\noindent
The simulation may take some time so feel free to go grab a coffee while you wait! 

When the simulation is completed, two text files should have been written into the current working directory, \texttt{out\_Position.dat} and \texttt{out\_Force.dat}. If you examine one of these files with a text editor, the first few lines should look something like the following:

\begin{verbatim}
0 1.125e-10 0  0 20 0  
0 4.75e-10 0  0 20 0  
0 1.0875e-09 0  0 20 0  
0 1.95e-09 0  0 20 0  
0 3.0625e-09 0  0 20 0  
0 4.425e-09 0  0 20 0  
0 6.0375e-09 0  0 20 0  
0 7.9e-09 0  0 20 0  
0 1.00125e-08 0  0 20 0  
0 1.2375e-08 0  0 20 0
\end{verbatim}

\noindent
The file is formatted so that, for any given timestep, the vector position (or force) of each wall is listed one after the other on the same line. Hence, the first 3 columns are the X-, Y- and Z-components of the position (or force) of the bottom wall and the following 3 columns are those of the top wall. 

The following python script will read both files and output stress and strain at each timestep in a format suitable for plotting using \texttt{gnuplot} or another XY plotting package. The formulae used to compute stress and strain are Equations~\ref{eq:stress} and \ref{eq:strain}. Figure~\ref{fig:UCS_stress_strain} shows the result for a typical uniaxial compression simulation.

\begin{verbatim}
from math import *

posnfile = open("out_Position.dat","r")
posn = posnfile.readlines()
posnfile.close()

forcefile = open("out_Force.dat","r")
force = forcefile.readlines()
forcefile.close()

for i in range (len(posn)):
   Y_bottom = float(posn[i].split()[1])
   Y_top = float(posn[i].split()[4])
   F_bottom = float(force[i].split()[1])
   F_top = float(force[i].split()[4])

   stress = (F_bottom - F_top)/100.0
   strain = 1.0 - (Y_top - Y_bottom)/20.0
   print strain,stress
\end{verbatim}

\noindent
To execute this script, write it to a text file called \texttt{make\_stress\_strain.py}, save it to the same directory where you ran the uniaxial compression simulation, then type the following at the command prompt: 

\begin{verbatim}
$ python make_stress_strain.py > stress_strain.dat
\end{verbatim}

\noindent
A new text file (called \texttt{stress\_strain.dat}) will be created, the first column of which will be strain and the second will be stress. 

Now that we have a stress-strain curve for the uniaxial compression simulation, it is a simple matter to measure the slope of the linear section to estimate Young's modulus and to measure the peak stress -- an estimate for the unconfined compressive strength. You might like to write your own script to compute Young's modulus and UCS directly from \texttt{stress\_strain.dat} or modify the script above.

In the next section we will examine three more useful \texttt{FieldSavers} for uniaxial compression simulations, namely the total kinetic energy of the particles, the total strain energy stored in bonds and the number of broken bonds. These three fields provide information on the internal deformation of the simulated rock sample, quantities not usually directly amenable to measurement in laboratory experiments.

\begin{figure}
\begin{center}
\resizebox{5in}{!}{
\includegraphics{UCS_stress_strain.jpg}}
\end{center}
\caption{Stress-Strain curve obtained from a uniaxial compression simulation (from \texttt{rot\_compress.py})} \label{fig:UCS_stress_strain}
\end{figure}


\subsubsection{Storing information on particles and bonds: kinetic energy, potential energy and number of bonds}

In the previous section we examined how to output wall forces and positions using \texttt{FieldSavers}. Both these data fields are examples of \emph{measurable quantities}, i.e., quantities that can be directly measured in equivalent laboratory experiments. Comparison of measurable quantities from simulations and experiments is a useful technique for validating and calibrating Discrete Element models. However, the primary advantage of computer simulations in scientific research is the ability to examine internal dynamics that are typically not amenable to direct observation in the laboratory or field. In this section we examine three quantities that provide insight on the internal deformation within rock samples subjected to uniaxial compressive loads. 

The first quantity is the total kinetic energy of the particle assembly. The total kinetic energy is simply the sum of the kinetic energy of each particle, i.e., $E_k = \sum_i \frac{1}{2} m_i v_i^2$. It is frequently useful to measure the total kinetic energy in simulations, particularly if one suspects a simulation is numerically unstable. An unbounded growth in the total kinetic energy over time is a good indication that a simulation is not working correctly. The following code-fragment initialises a \texttt{ParticleScalarFieldSaver} to store the total kinetic energy each timestep:

\begin{verbatim}
#create a FieldSaver to store the total kinetic energy of the particles:
sim.createFieldSaver (
   ParticleScalarFieldSaverPrms(
      fieldName="e_kin",
      fileName="ekin.dat",
      fileFormat="SUM",
      beginTimeStep=0,
      endTimeStep=250000,
      timeStepIncr=1
   )
)
\end{verbatim}

\noindent
Most of the \texttt{FieldSaver} parameters should now be familiar.  A new \texttt{fileFormat} called \texttt{SUM} is used here. This file format stores the sum of the kinetic energy of each particle. The output format is an ASCII text file with the total kinetic energy written once per timestep.  A number of other fields may also be output using \texttt{ParticleScalarFieldSavers} or \texttt{ParticleVectorFieldSavers}. A list of valid \texttt{fieldNames} for various types of \texttt{FieldSav\+ers} is provided in Appendix \ref{tables}.  Other helpful information can be found on this \link{ESyS-Particle wiki documentation page}{https://wiki.geocomp.uq.edu.au/index.php/Documentation_and_Presentations}. 

The second quantity is the total strain energy stored within bonds connecting particles. The total strain energy is simply the sum of the potential energies of the rotational bonds connecting each particle-pair. The following code-fragment initialises an \texttt{InteractionScalarFieldSaver} to store the total strain energy each timestep:

\begin{verbatim}
#create a FieldSaver to store potential energy stored in bonds:
sim.createFieldSaver (
   InteractionScalarFieldSaverPrms(
      interactionName="pp_bonds",
      fieldName="potential_energy",
      fileName="epot.dat",
      fileFormat="SUM",
      beginTimeStep=0,
      endTimeStep=250000,
      timeStepIncr=1
   )
)
\end{verbatim}

\noindent
Notice that instead of providing a list of wall names, we now provide the name of the Interaction Group, whose data we wish to store (in this case our \texttt{pp\_bonds} interaction group defining rotational bonds between particles). Once again we specify a \texttt{SUM fileFormat} so that the total potential energy is output to an ASCII text file once per timestep.

Another extremely useful quantity to monitor in elastic-brittle simulations is the number of broken bonds, as this is a measure of the amount of damage the rock sample has suffered due to the external load. Another \texttt{InteractionScalarFieldSaver} is used to output the total number of bonds each timestep. The relevant code-fragment is as follows:

\begin{verbatim}
#create a FieldSaver to store number of bonds:
sim.createFieldSaver (
   InteractionScalarFieldSaverPrms(
      interactionName="pp_bonds",
      fieldName="count",
      fileName="nbonds.dat",
      fileFormat="SUM",
      beginTimeStep=0,
      endTimeStep=250000,
      timeStepIncr=1
   )
)
\end{verbatim}

\noindent
The main difference here is the choice of \texttt{fieldName} -- \texttt{count} instead of \texttt{potential\_energy}. Note that this \texttt{FieldSaver} will output the total number of remaining bonds. If you wish to know how many bonds have broken you need to subtract the total number of remaining bonds from the initial number of bonds. 

\begin{figure}
\begin{center}
\resizebox{5in}{!}{
\includegraphics{UCS_epot.jpg}}
\end{center}
\caption{Time-series of total strain energy stored in bonds during a uniaxial compression simulation (from \texttt{rot\_compress.py})} \label{fig:UCS_PE}
\end{figure}

\begin{figure}
\begin{center}
\resizebox{5in}{!}{
\includegraphics{UCS_nbonds.jpg}}
\end{center}
\caption{Time-series of percentage of bonds broken during a uniaxial compression simulation (from \texttt{rot\_compress.py})} \label{fig:UCS_nbroke}
\end{figure}

\begin{figure}
\begin{center}
\resizebox{5in}{!}{
\includegraphics{UCS_force.jpg}}
\end{center}
\caption{Time-series of net wall force during a uniaxial compression simulation (from \texttt{rot\_compress.py})} \label{fig:UCS_force}
\end{figure}

Time-series of the total strain energy and the number of broken bonds are shown in Figures~\ref{fig:UCS_PE} and \ref{fig:UCS_nbroke} along with the wall force time-series (Figure~\ref{fig:UCS_force}) for comparison. The total strain energy time-series closely resembles the stress--strain curve, suggesting that total internal strain energy is proportional to macroscopic stress. This is not that unexpected given that it is the energy stored in bonds that imparts a force on the piston walls. The time-series of broken bonds is more interesting. Firstly, it is apparent that a significant fraction of bonds break before the peak stress is reached. In other words, the sample undergoes significant irreversible internal damage prior to reaching the unconfined compressive strength. The second interesting observation is that the total number of broken bonds after the peak stress is reached is only approximately $30\%$ of the initial number of bonds. The sample remains largely intact even post-peak. This is exactly what 
one would expect in laboratory uniaxial compression experiments as well. These experiments do not entirely annihilate rocks to atoms, but rather fragment the rock samples into a large number of smaller fragments. In a later tutorial, we will discuss how to post-process \texttt{CheckPointer} output files to measure, amongst other things, the sizes and number of fragments produced during our uniaxial compression simulation.

%\subsubsection{Dimensional analysis and rescaling of simulation results}

%\emph{\textbf{TO DO}}

%\subsection{Triaxial compression simulations}

%\subsubsection{Implementation of servo walls}

%\subsubsection{Measurement of Poisson's ratio}

\subsection*{What's Next?}

In this tutorial we introduced three useful features in ESyS-Particle simulations: the ability to move walls via a \texttt{Runnable} to implement external loading of models, new types of particle-pair interactions for models involving rotational particles, and some FieldSavers, providing another mechanism for outputing specific data about particles, walls and interactions during the course of a simulation. We also discussed some of the issues related to calibration of model parameters in DEM simulations. By this stage we have covered most of the basic features of ESyS-Particle simulations, so you are ready to start designing and executing your own simulations. 

In the following tutorial we will encounter some post-processing tools provided with ESyS-Particle. These tools are designed for post-simulation analysis of data stored in \texttt{CheckPointer} output files to permit visualisation and analysis of data that is not easily computed during simulations. We will demonstrate how to convert \texttt{CheckPoint} files into a format suitable for interactive visualisation using popular third-party software such as ParaView and VisIt. We will also see how to visualise the locations of broken bonds and also the shapes of fragments produced during uniaxial compression simulations. 

%In the following tutorials we will start to discuss some of the more advanced features of ESyS-Particle. These include the use of a external helper module called \texttt{LSMGenGeo} that provides the capacity to create particle models of siginificantly greater geometrical complexity than the basic tools covered already (such as \texttt{CubicBlock} and \texttt{RandomBoxPacker}).  We will also introduce some post-processing tools provided with ESyS-Particle designed for post-simulation analysis of data stored in checkpoint files. These tools include a conversion tool to permit interactive visualisation of ESyS-Particle checkpoint files using third-party software such as \link{ParaView}{http://www.paraview.org} and \link{VisIt}{https://wci.llnl.gov/codes/visit/}. 

\newpage
\section{Post-processing and data visualisation}

ESyS-Particle is primarily designed as a high-performance parallel DEM simulation engine. The Python API makes designing and executing different simulations relatively simple and straightforward, however the data output mechanisms (\texttt{FieldSavers} and \texttt{CheckPointers}) are relatively basic. For most applications, post-processing of simulation output files will be necessary to obtain useful results. ESyS-Particle is packaged with a few tools designed to aid in the post-processing of simulation data to obtain particular results or convert the output into formats that can be visualised using freely available third-party visualisation tools (such as \link{ParaView}{http://www.paraview.org} and \link{VisIt}{https://wci.llnl.gov/codes/visit/}). 

In this tutorial, we will discuss some of these post-processing tools and how to use them for more advanced visualisation of simulation results. By way of motivation we will use the uniaxial compression simulation (\texttt{rot\_compress.py}) discussed in the previous tutorial. By adding a \texttt{CheckPointer} to our simulation and post-processing the checkpoint files, we will be able to:
\begin{itemize}
\item interactively visualise a variety of simulation data, 
\item calculate the size and shape of rock fragments generated, and 
\item visualise the locations of fractures formed during the compression test.
\end{itemize}

We first encountered the \texttt{CheckPointer} in Chapter~3. \texttt{CheckPointers} provide a convenient way to output data from ESyS-Particle simulations. In combination with the post-processing tools described below (amongst others), quite advanced visualisation and analysis of simulation data may be achieved. To begin, add a \texttt{CheckPointer} to the \texttt{rot\_compress.py} script (just before the \texttt{sim.run()} subroutine call), then re-run the simulation:

\begin{verbatim}
sim.createCheckPointer (
   CheckPointPrms (
      fileNamePrefix = "snapshot",
      beginTimeStep = 0,
      endTimeStep = 250000,
      timeStepIncr = 1000
   )
)
\end{verbatim}

\noindent 
A series of \texttt{CheckPoint} files will now have been written to the working directory, each beginning with the prefix \texttt{snapshot}. These checkpoint files provide the input to a number of post-processing tools packed with ESyS-Particle. In this chapter, we will encounter three of these post-processing tools, namely:

\begin{enumerate}
   \item \texttt{dump2vtk}: a tool to convert \texttt{CheckPoint} files into a format suitable for interactive visualisation,
   \item \texttt{grainextract}: a tool that identifies \emph{clusters} of particles bonded together, and
   \item \texttt{fracextract}: a tool for visualising the location of broken bonds in brittle failure simulations.
\end{enumerate}

\subsection{Interactive visualisation of simulation data}

Perhaps the most versatile of ESyS-Particle's post-processing tools is \texttt{dump2vtk}. This tool will convert a sequence of \texttt{CheckPoint} files (also known as \emph{dump} files) into VTK unstructured mesh files. VTK (short for \link{Visualisation Tool Kit}{http://www.vtk.org}) is a popular library for three-dimensional post-processing and visualisation of scientific datasets. Although designed primarily for advanced visualisation and analysis of mesh-based datasets (e.g. from finite difference or finite element software), VTK has quite some utility for post-analysis of DEM simulation data also. Third-party visualisation software (such as \link{ParaView}{http://www.paraview.org} and \link{VisIt}{https://wci.llnl.gov/codes/visit/}) provide interactive 3D visualisation capabilities that make these packages (in combination with \texttt{dump2vtk}) a powerful addition to an ESyS-Particle modeller's arsenal.  

\begin{figure}
\resizebox{\textwidth}{!}{
\includegraphics{PV_initial_window.png}}
\caption{An example of an ESyS-Particle simulation visualised using ParaView.} 
\label{fig:PV_initial_window}
\end{figure}


For this tutorial, we will focus on visualisation using ParaView to simplify discussion. VisIt is an equally good choice for visualising ESyS-Particle simulations, if you prefer it over ParaView.  The first step to interactively visualise ESyS-Particle simulation data is to convert the \texttt{CheckPoint} files into VTK unstructured mesh files. ESyS-Particle's \texttt{dump2vtk} post-processing tool is designed for this purpose.

\subsubsection{\texttt{dump2vtk}: convert checkpoint files to VTK files}

The following shell command will convert all of the \texttt{rot\_compress.py} \texttt{CheckPoint} files:

\begin{verbatim}
$ dump2vtk -i snapshot -o vtk_snaps_ -rot -t 0 251 1000
\end{verbatim}

\noindent
Having successfully executed this command, $251$ new files will be written to the directory containing the \texttt{CheckPoint} files. A partial directory listing of these files is as follows:

\begin{verbatim}
$ ls *.vtu
vtk_snaps_0.vtu
vtk_snaps_100.vtu
vtk_snaps_101.vtu
vtk_snaps_102.vtu
vtk_snaps_103.vtu
vtk_snaps_104.vtu
vtk_snaps_105.vtu
vtk_snaps_106.vtu
vtk_snaps_107.vtu
vtk_snaps_108.vtu
vtk_snaps_109.vtu
vtk_snaps_10.vtu
[...]
\end{verbatim}

\noindent
These new VTK files are in a suitable format for opening from within ParaView (\texttt{File | Open..}). Having opened the sequence of VTK files, you will be presented with a graphical window similar to Figure~\ref{fig:PV_initial_window}. 

\subsubsection{Interactive visualisation using ParaView}

The default graphical view in ParaView shows only the bonds linking particles, with the colours representing the radius of the particle residing at the intersection points of bonds. The VTK output files from \texttt{dump2vtk} contain a number of different scalar and vector fields that may be selected for visualisation via the \texttt{Display} tab in the \texttt{Object Inspector} pane to the left of the main ParaView window. The scalar and vector fields available for visualisation are:

\begin{itemize}
   \item scalar fields:
   \begin{itemize}
      \item \texttt{radius}: the radius of individual particles,
      \item \texttt{particleTag}: the tag assigned to each particle,
      \item \texttt{Id}: the unique identity number of each particle,
      \item \texttt{bondTag}: the tag assigned to individual bonds,
      \item \texttt{bondStrain}: an indication of the amount of strain stored in each bond,
      \item \texttt{proc\_id}: the unique worker process identity number for visualising parallel subdomain decomposition.
   \end{itemize}
   \item vector fields:
   \begin{itemize}
      \item \texttt{velocity}: the current velocity of each particle,
      \item \texttt{angular velocity}: the current angular velocity of each particle,
      \item \texttt{displacement}: the total displacement of each particle since the simulation commenced, and
      \item \texttt{initial position}: the initial position of each particle.
   \end{itemize}
\end{itemize}

In many cases, useful information can be gained by simply colouring the bonds according to one of the scalar fields above, or the magnitude of one of the vector fields. In other instances it is useful to use a so-called \texttt{Glyph} filter to visualise, for example, the velocity field as an assembly of arrows. Perhaps the most useful \texttt{Glyph} filter is the \texttt{Sphere} filter. This filter inserts a sphere at the location of each particle, the radius of which can be scaled by the particle radius. The spheres can then be coloured according to any of the fields listed above. Examples of visualising the particle assembly and a velocity field are provided in Figure~\ref{fig:PV_examples}.

ParaView also provides a group of controls on the toolbar that allows one to step through the sequence of VTK files in temporal order. Clicking on the \texttt{Play} symbol will automatically step through all the ($251$) snapshot files one at a time. In this manner, the progress of a simulation may be animated. There are also a number of other more advanced features of ParaView that are useful for visualisation of DEM simulation results. It is recommended that one explore ParaView and its documentation to learn about these features.

\begin{figure}
\resizebox{\textwidth}{!}{
\includegraphics{PV_examples.png}}
\caption{Examples of visualisation using \texttt{Glyphs} in ParaView. A) Particles represented as sphere glyphs, coloured by the X-component of velocity; B) Particle velocities represented as arrow glyphs, coloured by the speed of each particle.}
\label{fig:PV_examples}
\end{figure}

\subsection{Calculating the number and size of rock fragments}

\subsubsection{\texttt{grainextract}: analysing rock fragments}

\subsubsection{Visualising rock fragments using ParaView}

\subsection{Visualising cracks formed during fracture simulations}

\subsubsection{\texttt{fracextract}: identifying locations of broken bonds}

\subsubsection{Visualising fractures using ParaView}

\subsection*{What's Next?}

In this tutorial some of the ESyS-Particle post-processing tools were introduced. One of these tools (\texttt{dump2vtk}) converts \texttt{CheckPointer} output files into VTK files, a popular 3D visualisation data format. Having converted checkpoint files to VTK format, third-party visualisation software can be used for advanced interactive visualisation of simulation results. Another post-processing tool (\texttt{grainextract}) permits analysis of the number and size of fragments produced during brittle failure simulations. In combination with \texttt{dump2vtk}, \texttt{grainextract} can also be used to interactively visualise the formation of fragments. The final post-processing tool to be considered was \texttt{fracextract} which calculates the locations and times of bond breakage events. Using this tool in combination with \texttt{dump2vtk} allows fracture patterns to be visualised. The ESyS-Particle post-processing tools are designed to permit advanced analysis and visualisation of simulation data with 
little additional computational expense over and above the burden required to execute simulations.

The following tutorial introduces another popular DEM simulation model, the annular shear cell. Shear cells are often employed in the laboratory to measure the bulk frictional response of sheared granular media. They are also popular for studying the fragmentation of granular media such as occurs within silos, along conveyor belts or within earthquake fault gouge zones. The purpose of the shear cell tutorial is to demonstrate some more features of ESyS-Particle and DEM simulations, namely how to conduct quasi-static two-dimensional simulations, how to implement periodic boundaries in one coordinate direction, and how to apply a constant external loading force to walls. These features are important weapons in the arsenal of a DEM modeller, finding application for simulating a broad range of physical phenomena.

\newpage
\section{Annular shear cells: quasi-static 2D simulations with periodic boundaries and servo walls}

In Chapter 6, uniaxial compression simulations were introduced for the purpose of calibrating the microphysical parameters of DEM models so that the macroscopic elastic properties match those of brittle-elastic materials such as rocks. Whilst uniaxial compression simulations (and their companions, tension and triaxial compression simulations) are quite useful for calibrating a DEM model to simulate brittle failure, such simulations are not as useful if one wishes to calibrate the model to simulate, for example, flow of granular material. 

One of the key macroscopic properties of granular media is the \emph{bulk friction coefficient}, defined as the effective frictional resistance of a volume of granular material under shear loading.  In the laboratory, the bulk friction coefficient is measured via either direct shear tests or annular shear cell tests. We will focus upon the later in this chapter. An annular shear cell consists of two metal rings, the bottom of which contains a groove within which is placed granular material (e.g. sand, gravel or powder). The top ring is then lowered onto the bottom ring and a series of weights and pulleys are used to maintain a constant vertical pressure on the ring of granular material. Subsequently the bottom ring is rotated at constant speed, thus shearing the granular material to a desired total shear strain. By measuring the force required to maintain a constant shearing rate, experimentalists can estimate the effective bulk friction coefficient of the granular material. 

\begin{figure}
\begin{center}
\resizebox{10cm}{!}{
\includegraphics{ShearCellSetup.jpg}}
\end{center}
\caption{Diagram of a two-dimensional annular shear cell simulation employing periodic boundaries in the X-direction (from \texttt{shearcell.py}).} \label{fig:shearcell}
\end{figure}

In the following, we will discuss how to construct a two-dimensional version of the annular shear cell test using ESyS-Particle and demonstrate how the bulk friction coefficient may be calculated in shear cell simulations. The main purpose here is not to describe a production-ready DEM annular shear cell model suitable for calibration, but rather to elucidate a number of useful techniques in DEM modelling and how these are implemented in ESyS-Particle simulations.  As it happens, the annular shear cell is an ideal application with which to introduce these techniques within a practical context.  The key techniques we will discuss are:

\begin{itemize}
   \item how to restrict ESyS-Particle to two-dimensional computations,
   \item how to implement periodic (or circular) boundaries in one coordinate direction,
   \item how to conduct quasi-static simulations, and
   \item how to implement servo walls to maintain a constant external loading force (or stress) on a particle assembly.
\end{itemize}

Figure~\ref{fig:shearcell} illustrates the DEM model we wish to construct. It consists of a two-dimensional assembly of unbonded frictional particles of variable size. A layer of particles top and bottom are bonded elastically to planar walls that will act as driving plates. So as to simulate a ring of particles similar to the annulus of a shear cell, we employ periodic boundaries in the X-direction; particles exitting the model to the right will re-enter the model to the left and vice versa. A constant compressive force will be applied to the top driving plate in the negative Y-direction. In addition, the bottom plate will be moved at constant speed in the X-direction. Since annular shear cell tests are typically conducted under quasi-static conditions, we will also utilise two numerical approximations to achieve quasi-static conditions in the simulations, namely: 
\begin{enumerate}
   \item large viscous damping to remove elastic waves, and 
   \item higher than normal particle densities to ensure particle accelerations remain relatively small throughout the simulation.
\end{enumerate}

\noindent
The complete code-listing for the shear cell simulations may be found in Appendix~\ref{code:shearcell}.

\subsection{Two-dimensional computations and periodic boundaries}

For a number of applications, two-dimensional DEM simulations are sufficient to obtain useful results. Although ESyS-Particle is primarily designed with large, three-dimensional simulations in mind, it is relatively simple to instruct ESyS-Particle to ignore computations in the Z-direction, effectively reducing the simulation to two dimensions. The \texttt{LsmMpi.force2dComputations(..)} subroutine call achieves this. This subroutine call should be made very soon after initialising the simulation object (\texttt{LsmMpi}) and before setting the spatial domain of the simulation. The following code-fragment illustrates the use of \texttt{LsmMpi.force2dComputations(..)}:

\begin{verbatim}
#import the appropriate ESyS-Particle modules:
from esys.lsm import *
from esys.lsm.util import *
from esys.lsm.geometry import *

#create a simulation container object:
#       N.B. there must be at least two sub-divisions 
#       in the X-direction for periodic boundaries
sim = LsmMpi (numWorkerProcesses=2, mpiDimList=[2,1,1])
sim.initNeighbourSearch (
   particleType = "NRotSphere",
   gridSpacing = 2.5,
   verletDist = 0.5
)

#specify the number of timesteps and timestep increment:
sim.setNumTimeSteps(100000)
sim.setTimeStepSize(0.001)

#enforce two-dimensional computations:
sim.force2dComputations (True)
\end{verbatim}

ESyS-Particle also permits the use of periodic boundaries in one coordinate direction (the X-direction). Periodic boundaries in ESyS-Particle are implemented by borrowing the code used for managing transit of particles across parallel subdomain boundaries. Consequently, if one wishes to employ periodic boundaries, one must ensure there are at least two parallel subdivisions in the X-direction. In the code-fragment above, this is achieved by setting \texttt{mpiDimList=[2,1,1]} (and specifying the need for two worker processes via \texttt{numWorkerProcesses=2}). 

It is also necessary to inform ESyS-Particle that periodic boundaries are being used when one sets the spatial domain of the simulation. The code-fragment below illustrates how this is done:

\begin{verbatim}
#specify the spatial domain and direction of periodic boundaries:
domain = BoundingBox ( Vec3 (0,0,0), Vec3 (10,10,0) )
sim.setSpatialDomain (
   bBox = domain,
   circDimList = [True, False, False]
)
\end{verbatim}

Having assigned the appropriate number of parallel subdivisions and correctly set the \texttt{circDimList} argument of \texttt{LsmMpi.setSpatialDomain(..)}, the simulation object is initialised to employ periodic boundaries in the X-direction. A simple test script could easily be constructed in which a single particle is inserted with an initial velocity in the X-direction. As the simulation progresses the particle would simply loop around and around the X-direction, exiting one side of the domain and re-entering the other side. If periodic boundaries were not correctly initialised, the particle would simply disappear from the simulation once it crossed the spatial domain boundary.

For our annular shear cell simulation, we desire a particle assembly consisting of particles of variable size, initially at random locations. As we have seen in previous tutorials, the \texttt{RandomBoxPacker} can be used for this. So that we achieve a dense initial particle packing, the \texttt{RandomBoxPacker} must also be informed of the presence of periodic boundaries. The following code-fragment illustrates:

\begin{verbatim}
#construct a rectangle of unbonded particles:
packer = RandomBoxPacker (
   minRadius = 0.1,
   maxRadius = 0.5,
   cubicPackRadius = 2.2,
   maxInsertFails = 1000,
   bBox = BoundingBox(
      Vec3(0.0, 0.0,0.0),
      Vec3(10.0, 10.0, 0.0)
   ),
   circDimList = [True, False, False],
   tolerance = 1.0e-5
)
packer.generate()
particleList = packer.getSimpleSphereCollection()
\end{verbatim}

\noindent
Note that we have not yet inserted the particles into the simulation object. This is because we wish to tag the particles carefully prior to insertion, as explained in the next section.

\subsection{Quasi-static simulations: local damping and high densities}

Annular shear cell experiments in the laboratory are typically conducted under so-called \emph{quasi-static} conditions. Mathematically, quasi-static conditions imply that particle accelerations are negligible and, hence, inertial effects (such as elastic wave propagation) are neglected. ESyS-Particle employs an explicit finite-difference time-integration scheme in which particle velocities and positions are updated by computing the instantaneous acceleration of each particle (via Newton's Second Law). Such a time-integration scheme is only suitable for \emph{dynamic} conditions in which the accelerations are non-zero. If all particle accelerations were zero, no stationary particles would move and non-stationary particles would move at constant speed (Newton's First Law). 

One way to simulate quasi-static conditions in a DEM model would be to change the time-integration scheme to an implicit (typically iterative) scheme in which particle positions and forces are repeatedly updated until all forces are negligible. To implement such a scheme in ESyS-Particle would require significant re-factoring of the DEM engine and is likely to be quite computationally inefficient. An alternative approach that is often adopted by DEM practitioners involves the use of two numerical approximations that together achieve quasi-static conditions. 

The first numerical approximation is known as the \emph{high density approximation}. It involves assigning unrealistically large densities (or masses) to all particles. Since the instantaneous acceleration of a particle is inversely proportional to its mass ($a = F/m$), a larger mass results in a smaller acceleration, for a given net force acting on a particle. Here the particle mass is being used as a type of \emph{penalty factor} to reduce the amplitudes of particle accelerations. However, given that the particle accelerations will never be zero, some inertial effects will be expected to remain. 

The second numerical approximation, large artificial damping, attempts to reduce the impact of these inertial effects. By utilising a relatively large amount of viscous damping, elastic waves can be efficiently reduced in amplitude over a few time-steps. In this way inertial effects can also be managed to achieve effectively quasi-static conditions in the DEM simulations. 

To implement the high density approximation in ESyS-Particle simulations, we must prescribe large particle densities. The \texttt{LsmMpi.setParticleDensity(..)} subroutine achieves this. As this subroutine sets the density of all particles with a given tag, we must first assign a tag to each particle before insertion into the simulation object. Later we will need to bond a layer of particles top and bottom to driving plates, so we will need three particle tags: one for unbonded particles within the cell (\texttt{tag=1}), one for particles near the bottom plate (\texttt{tag=2}) and one for particles near the top plate (\texttt{tag=3}). The following code-fragment tags the particles accordingly and inserts them into the simulation:

\begin{verbatim}  
#tag particles along base and top of rectangle
#then add the particles to the simulation object:
for pp in particleList:
   centre = pp.getPosn()
   radius = pp.getRadius()
   Y = centre[1]
   if (Y < 1.0):                # particle is near the base (tag=2)
      pp.setTag (2)
   elif (Y > 9.0):              # particle is near the top (tag=3)
      pp.setTag (3)
   else:                        # particle is inside the shear cell (tag=1)
      pp.setTag (1)
   sim.createParticle(pp)       # add the particle to the simulation object
\end{verbatim}  

Having assigned a tag to each particle, the density of the particles can be set using the following syntax:

\begin{verbatim}
#set the density of all particles:
sim.setParticleDensity (
   tag = 1,
   mask = -1,
   Density = 100.0
)
\end{verbatim}

\noindent
Remember to also set the density of particles with \texttt{tag=2} and \texttt{tag=3} using similar subroutine calls.

In previous tutorials, the \texttt{LinDampingPrms} artificial viscosity Interaction Group has already been encountered. By assigning a relatively large value for the \texttt{viscosity} parameter, we can use \texttt{LinDampingPrms} to damp inertial effects. The following code-fragment achieves this in our shear cell simulation:

\begin{verbatim}
#add local damping to avoid accumulating kinetic energy:
sim.createInteractionGroup (
   LinDampingPrms (
      name = "damping",
      viscosity = 1.0,
      maxIterations = 100
   )
)
\end{verbatim}

Due to the way ESyS-Particle implements damping, it is important that the specification of damping interaction groups is placed after that of all other interaction groups in a simulation script. Before adding the code-fragment above to your script, insert the following:

\begin{enumerate}
   \item two walls above and below the particle assembly (at $Y=0$ and $Y=10$),
   \item \texttt{NRotFrictionPrms} interactions between unbonded particles, and
   \item \texttt{NRotBondedWallPrms} to bond appropriately tagged particles to the walls.
\end{enumerate}

\noindent
Once you have added these items and the \texttt{LinDampingPrms} interaction group to your script, we are ready to define the boundary conditions. The next section describes how to do this.

\subsection{Servo walls and constant stress boundary conditions}

As described in the introduction to this chapter, annular shear cells are loaded via a combination of a constant vertical confining pressure applied to the top ring and shear of the bottom ring at a constant rate. To simulate shear at a constant rate, we can simply re-use the \texttt{WallLoaderRunnable} we encountered in Chapter 6. So as to match the typical laboratory conditions, we will only shear the \texttt{bottom\_wall}. The following code-fragment achieves that:

\begin{verbatim}
#import the WallLoaderRunnable (at the top of the script):
from WallLoader import WallLoaderRunnable

[...]

#add WallLoaderRunnables to shear the bottom driving plate:
wall_loader1 = WallLoaderRunnable(
   LsmMpi = sim,
   wallName = "bottom_wall",
   vPlate = Vec3 (0.125, 0.0, 0.0),
   startTime = 30000,
   rampTime = 10000
)
sim.addPreTimeStepRunnable (wall_loader1)
\end{verbatim}

\noindent
Notice that the wall is moved in the X-direction this time and that shear does not commence until $30000$ timesteps have elapsed. The delay commencing shear is to provide sufficient time to apply a constant vertical stress to the model, via the \texttt{top\_wall}. 

Now that we have implemented constant shear boundary conditions for the \texttt{bottom\_\+wall}, we must also implement a constant vertical stress loading condition on the \texttt{top\_wall}. In DEM simulations, constant boundary forces (or stresses) are implemented via so-called \emph{servo walls}. A servo wall is a wall that is incrementally moved a small distance in order to maintain a constant prescribed net force acting on the wall. The net force on a wall is a combination of the forces due to all particles interacting with the wall and any external forces (or applied motions). Consequently, in order to compute the wall displacement required to maintain a prescribed net force, one must have access to all the instantaneous particle-wall forces. Of course, once the wall is moved, these particle-wall forces change, so one must recompute the net force on the wall. Typically a simple iterative procedure can be employed in which a wall is moved a small distance, forces are recomputed, then the wall is moved 
again. In most circumstances, this iterative procedure converges rapidly and the loop is terminated when the incremental wall displacement is smaller than a prescribed tolerance. 

Although it is technically possible to implement a servo wall algorithm from within a \texttt{Runnable}, this would be computationally inefficient, requiring numerous communications between the Python API, the master process and the workers. Consequently, ESyS-Particle provides a built-in subroutine, \texttt{LsmMpi.applyForceToWall (..)}, that implements a simple servo wall algorithm. The user need only supply the \emph{name of the Interaction Group} specifying the type of particle-wall interactions, as well as the net force to apply to the wall. The following code-fragment illustrates how one might apply a $1N$ force in the Y-direction to the \texttt{top\_wall} in our shear cell simulation:

\begin{verbatim}
sim.applyForceToWall (
   interactionName = "twall_bonds", 
   force = Vec3 (0,1,0)
)
\end{verbatim}

\noindent 
Note that we do not supply the name of the wall but rather the particle-wall interaction group, \texttt{twall\_bonds}.

In order to maintain quasi-static equilibrium in the shear cell simulations, it is advantageous to initially increase the applied force linearly until the desired force is achieved. This is similar to the initial acceleration implemented in the \texttt{WallLoaderRunnable}. A \texttt{Runnable} that achieves this, called \texttt{ServoWallLoader.py}, is provided in Appendix~\ref{code:ServoWallLoader}. Once implemented, all that remains is to import and initialise this \texttt{Runnable} in \texttt{shearcell\+.py}:

\begin{verbatim}
#import the ServoWallLoaderRunnable (at the top of the script):
from ServoWallLoader import ServoWallLoaderRunnable

[...]

#add ServoWallLoaderRunnables to apply constant normal stress:
servo_loader1 = ServoWallLoaderRunnable(
   LsmMpi = sim,
   interactionName = "twall_bonds",	
   force = Vec3 (0.0, -1000.0, 0.0),
   startTime = 0,
   rampTime = 5000
)
sim.addPreTimeStepRunnable (servo_loader1)
\end{verbatim}

\noindent
The arguments to \texttt{ServoWallLoaderRunnable} instruct ESyS-Particle to linearly increase the applied force on the \texttt{top\_wall} for $5000$ timesteps, then maintain a constant vertical force of $1000N$ thereafter. Recall that shear commences after $30000$ timesteps, so there is plenty of time for the model to equilibriate at the desired confining pressure prior to shear commencing.

Now that the two boundary conditions are implemented, the physics of the shear cell model are complete. Of course, it would be advantageous to include some \texttt{FieldSavers} or a \texttt{CheckPointer} prior to commencing a simulation. The next section describes how to use \texttt{WallVectorFieldSavers} to measure the bulk friction coefficient of the sheared unbonded particles, as well as how to observe whether dilation occurs during shear.

\subsection{Computation of bulk frictional properties of granular media}

The primary purpose of DEM shear cell simulations is to provide a means to calibrate the microphysical model parameters so that the bulk frictional properties of the DEM model match those measured in laboratory experiments. In the laboratory, the bulk friction coefficient of a sheared granular material is estimated by measuring the amount of force (or torque) required to maintain a constant rate of shear at the boundaries. The bulk friction coefficient ($\mu_{\mathrm{bulk}}$) is defined as the measured shear force ($F_s$) divided by the applied normal force ($F_n$) i.e.

\[ \mu_{\mathrm{bulk}} \approx \frac{F_s}{F_n} \]

\noindent
Typically a time-series of the bulk friction coefficient is averaged in order to obtain a measure of the mean bulk friction coefficient. The standard deviation provides a measure of the degree of variability of the bulk friction. 

It should be obvious that in order to estimate the bulk friction coefficient in the DEM shear cell simulations, we need to extract time-series of the forces acting on the walls throughout the simulations. The following \texttt{WallVectorFieldSaver} will provide the necessary raw data:

\begin{verbatim}
force_saver = WallVectorFieldSaverPrms(
   wallName=["bottom_wall", "top_wall"],
   fieldName="Force",
   fileName="out_Force.dat",
   fileFormat="RAW_SERIES",
   YbeginTimeStep=0,
   endTimeStep=100000,
   timeStepIncr=1
)
sim.createFieldSaver(force_saver)
\end{verbatim}

\noindent
This code-fragment should be quite familiar as it is almost identical to that used in the \texttt{rot\_compress.py} tutorial. 

\begin{figure}
\begin{center}
\resizebox{10cm}{!}{
\includegraphics{SC_bulk_friction.png}}
\end{center}
\caption{Time-series of the effective bulk friction coefficient from a two-dimensional annular shear cell simulation (from \texttt{shearcell.py}).} \label{fig:bulkFriction}
\end{figure}

A time-series of the bulk friction coefficient from a shear cell simulation is provided in Figure~\ref{fig:bulkFriction}. In this figure we average the normal and shear forces acting on the two walls as measures for $F_n$ and $F_s$ in the formula above. Note that during the initial stage of the simulation, the bulk friction coefficient is near-zero. During this interval only a normal force is applied to the top wall (with no shear applied to the bottom wall). Once shear commences (after $30000$ timesteps) the bulk friction coefficient rapidly rises to approximately $\mu_{\mathrm{bulk}} \approx 0.65$. Thereafter, rough stable sliding of the granular material ensues, with the bulk friction coefficient remaining near-constant.

This result is typical when shearing a granular material comprised of irrotational spheres with a constant internal friction coefficient (here we set the internal friction coefficient as $0.6$). Past research has demonstrated that inhibiting rolling of DEM particles (through the use of \texttt{NRotSpheres}) results in a bulk friction coefficient that increases as the internal friction coefficient increases. However, when rotational, unbonded particles are used, the bulk friction coefficient is significantly smaller, and almost independent of the choice of internal friction coefficient. 

The reason for this is that spheres simply roll over one another without any geometrical interlocking and, more importantly, without \emph{dilation} of the granular material as it shears. A time-series of the top wall position (as shown in Figure~\ref{fig:dilation}) confirms that the top wall monotonically reduces in height throughout the simulation. If geometrical interlocking had occurred, one would expect to see evidence for the top wall ``riding up'' as the granular material becomes locally interlocked.  Thankfully there is a relatively simple way to achieve realistic frictional response in DEM shear cell simulations: utilise clusters of particles bonded together that interlock by virtue of the surface roughness of the clusters. In the next chapter we will discuss how one might construct a shear cell model incorporating clusters of bonded particles representing the granular material. 

\begin{figure}
\begin{center}
\resizebox{10cm}{!}{
\includegraphics{SC_dilatancy.png}}
\end{center}
\caption{Time-series of the top wall position from a two-dimensional annular shear cell simulation (from \texttt{shearcell.py}).} \label{fig:dilation}
\end{figure}

\subsection*{What's Next?}

In this chapter, a number of useful DEM modelling techniques were introduced using annular shear cell experiments as a motivation. We discussed how to conduct two-dimensional simulations with ESyS-Particle, including periodic boundaries in one coordinate direction. The use of large viscosity and high particle densities was demonstrated as an effective method to achieve quasi-static conditions in DEM simulations without altering the time-integration scheme. Servo walls were also discussed as a mechanism to apply constant boundary forces (or stresses) to DEM models. 

In the final section of this chapter, we analysed some results from the simple shear cell model. These results, although promising, demonstrated a lack of geometrical interlocking, an important mechanism governing the frictional response of granular media. Some DEM practitioners have implemented more complicated particle-pair interactions to overcome this limitation. This is often a reasonable compromise when computational resources are limited. Another approach is simply to make the model geometrically more complex without increasing the mathematical complexity of the particle-pair interactions. Past research using ESyS-Particle has demonstrated that the use of bonded clusters of particles to represent individual grains results in bulk frictional response matching that observed in laboratory experiments. 

There are numerous other instances where increasing the geometrical realism of a DEM model can yield more realistic results without resorting to more complicated interaction laws. This observation is one of the reasons ESyS-Particle is designed for use on parallel supercomputers. Increased geometrical realism typically requires the use of significantly larger numbers of particles. ESyS-Particle permits these significantly larger models to be executed on parallel supercomputers in almost the same amount of time as a smaller model on a desktop PC. 
As simulation models become increasingly complex, one may need to carefully design models to achieve optimal performance or make use of third party tools to generate and analyse these models. The following chapter describes some of the other resources available to assist advanced users of ESyS-Particle.

Of course, in order to construct these geometrically complex models, one requires a flexible tool for model construction. The next chapter introduces \texttt{GenGeo}, a companion library to ESyS-Particle, designed as a tool for constructing DEM models that are much more geometrically complex than we have encountered thus far.


\newpage