-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOpenCAL.tex
More file actions
1727 lines (1496 loc) · 82.8 KB
/
OpenCAL.tex
File metadata and controls
1727 lines (1496 loc) · 82.8 KB
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
\chapter{OpenCAL}\label{ch:opencal}
This Chapter introduces the serial implementation of OpenCAL by
examples. After a brief overview about data types, main structure and
adopted conventions, the implementation of a simple 2D cellular
automaton is described. Subsequently, four different implementations
of a more complex 2D example are illustrated, to show how simulation
efficiency can progressively be improved. The implementation of a
simple 3D model is also presented for the sake of completeness. The
last part of the Chapter deals with OpenCAL-GL and shows how to
integrate a basic OpenGL/GLUT visualization system in both 2D and 3D
OpenCAL-based applications.
\section{Statements conventions}\label{sec:Conventions}
The OpenCAL API adopts the following conventions:
\begin{itemize}
\item Derived data types are characterised by the \verb'CAL' prefix,
followed by a type identifier formed by one or more capitalised
keywords, an optional suffix identifying the model dimension
(\verb'2D' or \verb'3D'), and an eventual optional suffix
specifying the basic scalar type (\verb'b', \verb'i', or \verb'r',
for \verb'CALbyte', \verb'CALint' and \verb'CALreal' derived
types, respectivey);
\item Constants and enumerals are characterised by the \verb'CAL_'
prefix, followed by one or more uppercase keywords separated by
the \verb'_' character (e.g. the \verb'CAL_TRUE' and
\verb'CAL_FALSE' boolean-type values);
\item Functions are characterised by the \verb'cal' prefix, followed
by at least one capitalized keyword, and end with a suffix
specifying the model dimension (\verb'2D' or \verb'3D') and the
basic datatype (\verb'b', \verb'i', or \verb'r', for
\verb'CALbyte', \verb'CALint' and \verb'CALreal' derived types,
respectively).
\end{itemize}
Moreover, the \verb'{arg1|arg2|...|argn}' and
\verb'[arg1|arg2|...|argn]' conventions are adopted in the
following. The first one identifies a list of $n$ mutually exclusive
arguments, where one of the arguments is needed, while the second a
list of $n$ non-mutually exclusive optional arguments.
\section{Basic data types and main loop structure}
The current version of OpenCAL provides support for three different
scalar data types, namely \verb'CALbyte', \verb'CALint', and
\verb'CALreal', which redefine the \verb'char', \verb'int' and
\verb'double' C native scalar types, respectively. For each of them, a
corresponding substate type is provided, namely
\verb'CALSubstate{2D|3D}{b|i|r}', as well as more complex objects for
model and simulation definitions, namely \verb'CALModel{2D|3D}' and
\verb'CALRun2D{2D|3D}', respectively. Supported scalar types and
substates are listed in Table \ref{tab:basic_types}, while Table
\ref{tab:ca_sim_types} lists model and simulation data types.
\begin{table}
\centering
\begin{tabular}{c|c|c|c}
\hline
OpenCAL basic type & Basic type alias & C type & Substate type\\
\hline
\verb'CALbyte' & \verb'CALParameterb' & \verb'char' & \verb'CALSubstate{2D|3D}b'\\
\verb'CALint' & \verb'CALParameteri' & \verb'int' & \verb'CALSubstate{2D|3D}i'\\
\verb'CALreal' & \verb'CALParameterr' & \verb'double' & \verb'CALSubstate{2D|3D}r'\\
\hline
\end{tabular}
\caption{OpenCAL basic scalar and substate types.}
\label{tab:basic_types}
\end{table}
\begin{table}
\centering
\begin{tabular}{c|c}
\hline
OpenCAL substate type & Meaning \\
\hline
\verb'CALModel{2D|3D}' & CA data type\\
\verb'CALRun2D{2D|3D}' & Simulation data type\\
\hline
\end{tabular}
\caption{OpenCAL types for CA and simulation objects.}
\label{tab:ca_sim_types}
\end{table}
The model object essetially allows to define the XCA dimension, the
neighbourhood (which can be predefined or custom - for maximum
flexibility), the cellular space boundary behaviour and if the active
cell optimization has to be employed.
Substates and elementary processes must be registered to the model
object to be transparently processed. For this purpose, the
\verb'calAddSubstate{2D|3D}{b|i|r}()' and
\verb'calAddElementaryProcess{2D|3D}{b|i|r}()' API functions can be
adopted. Specifically, substates are composed by two computational
layes, namely the \emph{current} and \emph{next} ones. Both of them
are implemented by means of linearised arrays, even if they represent
2D and 3D domain data. Nevertheless, a transparent multi indices-based
access is guarantied by specific API functions. If unsafe operations
are not considered (see below in this Chapter), the current layer is
used as a read-only memory, while the next one for updating the velues
for the central cell, by guarantying the implicit
parallelism. Single-layer substates are also implemented, whcih have
the current layer only. This latter can be used for internal
transformation processing and, obviusly, do not need to be
updated. Instead, elementary processes are defined by means of
callback functions. Theese latter must return void and take a pointer
to the model object as first parameter, followed by a list of integer
parameters representing the coordinates of a generic cell of the
cellular space.
The simulation object determines of the system evolution. It
essentially allows to define how many steps have to be computed, and
the update scheme. This latter, in particular, can be implicit or
explicit. In the first case, OpenCAL applies the elementary processes
in the same order in which they have been registered to the model
object and, after the application of each of them, updates all the
registered substates (even if they were left unchanged). In the other
case, the model transition function must be overridden, the elemantary
processes explicitly applied and the substates explicitly updated,
allowing for improving both flexibility and performace. In fact, the
elementary processes application order can be changed with respect to
the one chosen at the registration stage, and unneeded substates
update can be avoided. Figure \ref{fig:opencal_main_loop} shows the
OpenCAL main implicit simulation loop. Before entering the loop, if
defined, the init function is executed once and a substates update
performed. Afterwards, while the current step is lower or equal to the
final step of computation (or this latter is set to
\verb'CAL_RUN_LOOP', which defines an infinite loop), elementary
processes are executed once at a time, in the order they have been
registered to the model object, and substates updated after the
application of each them. Moreover, just before the end of the
computational step, the steering function (if defined) is applied and
substates updated again. At the end of the computational step, a stop
condition is checked, which can stop the simulation even before the
last step is reached.
\begin{figure}[htbp]
\centering
\includegraphics[width=9.5cm]{./images/OpenCAL/opencal_main_loop.pdf}
\caption{OpenCAL main loop chart.}
\label{fig:opencal_main_loop}
\end{figure}
\section{Statements conventions}\label{sec:Conventions}
%% As seen, data types have the same \verb'CAL' prefix, and an
%% optional suffix identifying the CA dimension or the basic scalar type,
%% or both of them. For instance, the 2Di suffix specifies a
%% two-dimensional object in which each element is of type integer
%% (cf. Table \ref{tab:substate_types}).
%% Predefined numerical constants start with the \verb'CAL_' prefix,
%% followed by at least one uppercase keyword. In case of more keywords,
%% they are separated by the \verb'_' character.
%% Eventually, functions start with the \verb'cal' suffix, followed by at
%% least one capitalized keyword, and end with a suffix specifying the CA
%% dimension and the basic datatype.
In OpenCAL, besides the three basic supported scalar data types,
namely \verb'CALbyte', \verb'CALint', and \verb'CALreal', which
redefine the \verb'char', \verb'int' and \verb'double' C native
scalar data types, respectively, the API adopts the following
conventions:
\begin{itemize}
\item Derived data types are characterised by the \verb'CAL' prefix,
followed by a type identifier formed by one or more capitalised
keywords, an optional suffix identifying the model dimension
(\verb'2D' or \verb'3D'), and an eventual optional suffix
specifying the basic scalar type (\verb'b', \verb'i', or \verb'r',
for \verb'CALbyte', \verb'CALint' and \verb'CALreal' derived
types, respectivey);
\item Constants and enumerals are characterised by the \verb'CAL_'
prefix, followed by one or more uppercase keywords separated by
the \verb'_' character (e.g. the \verb'CAL_TRUE' and
\verb'CAL_FALSE' boolean-type values);
\item Functions are characterised by the \verb'cal' prefix, followed
by at least one capitalized keyword, and end with a suffix
specifying the model dimension (\verb'2D' or \verb'3D') and the
basic datatype (\verb'b', \verb'i', or \verb'r', for
\verb'CALbyte', \verb'CALint' and \verb'CALreal' derived types,
respectively).
\end{itemize}
Moreover, the \verb'{arg1|arg2|...|argn}' and
\verb'[arg1|arg2|...|argn]' conventions are adopted in the
following. The first one identifies a list of $n$ mutually exclusive
arguments, where one of the arguments is needed, while the second a
list of $n$ non-mutually exclusive optional arguments.
\section{Conway's Game of Life}\label{sec:cal_life}
In order to introduce OpenCAL, this section starts by implementing the
Conway's Game of Life, one of the most simple, yet powerful examples
of CA, devised by mathematician John Horton Conway in 1970.
The Game of Life can be thought as an infinite two-dimensional
orthogonal grid of square cells, each of which is in one of two
possible states, \emph{dead} or \emph{alive}. Every cell interacts
with the eight adjacent neighbors belonging to the Moore
neighborhood. At each time step, one of the following transitions
occur:
\begin{enumerate}
\item Any live cell with fewer than two alive neighbors dies, as
if by loneliness.
\item Any live cell with more than three alive neighbors dies, as
if by overcrowding.
\item Any live cell with two or three alive neighbors lives,
unchanged, to the next generation.
\item Any dead cell with exactly three live neighbors comes to
life.
\end{enumerate}
The initial configuration of the system specifies the state (dead or
alive) of each cell in the cellular space. The evolution of the
system is thus obtained by applying the above rules (which define the
cell transition function) simultaneously to every cell in the
cellular space, so that each new configuration is function of the one
at the previous step. The rules continue to be applied repeatedly to
create further generations. For more details on the Game of Life,
please check Wikipedia at the URL
\url{http://en.wikipedia.org/wiki/Conway's_Game_of_Life}.
\begin{figure}
\begin{center}
\includegraphics[width=5cm]{./images/OpenCAL/LifeNeighborhood.png}
\caption{OpenCAL 2D cellular space and Moore neighborhood. Cells
are individuated by a couple of matrix-style integer coordinates
$(i, j)$, where $i$ represents the row and $j$ the column. Cell
(0,0) is the one located at the upper-left corner. Moore
neighborhood is represented in gray, with the central cell
highlighted in dark gray. Neighboring cells can also be indexed by
the integer subscripts shown within the cells. Cells indices are
implicitly assigned by OpenCAL, both in the case of predefined
and custom neighborhoods. In the first case, indices can not be
modified, while in the second case, indices are assigned
progressively in an automatic way each time a new neighbor is added
to the CA by means of the \texttt{calAddNeighbor2D()} function.}
\label{fig:LifeNeighborhood}
\end{center}
\end{figure}
The formal definition of the Life CA is reported below.
$$Life = < R, X, Q, \sigma >$$
where:
\begin{itemize}
\item $R$ is the set of points, with integer coordinates, which
defines the 2-dimensional cellular space. The generic cell in $R$ is
individuated by means of a couple of integer coordinates $(i, j)$,
where $0 \leq i < i_{max}$ and $0 \leq j < j_{max}$. The first
coordinate, $i$, represents the row, while the second, $j$, the
column. The cell at coordinates $(0,0)$ is located at the top-left
corner of the computational grid (cf. Figure
\ref{fig:LifeNeighborhood}).
\item $X = \{(0,0), (-1, 0), (0, -1), (0, 1), (1, 0), (-1,-1), (1,-1),
(1,1), (-1,1)\}$ is the Moore neighborhood relation, a geometrical
pattern which identifies the cells influencing the state transition
of the central cell. The neighborhood coordinates of the generic
cell of coordinate $(i, j)$ is given by
$$N(X, (i, j)) = $$
$$= \{(i, j)+(0,0), (i, j)+(-1, 0), \dots, (i, j)+(-1,1)\} =$$
$$= \{(i, j), (i-1, j), \dots, (i-1,j+1)\}$$
Here, a subscript operator can be used to index cells belonging to the
neighborhood. Let $|X|$ be the number of elements in X, and $n \in
\mathbb{N}$, $0 \leq n < |X|$; the notation
$$N(X, (i, j), n)$$
represents the coordinates of the $n^{th}$ neighborhood of the cell
$(i,j)$. Thereby, $N(X, (i, j), 0) = (i, j)$, i.e. the central cell,
$N(X, (i, j), 1) = (i-1, j)$, i.e. the first neighbor, and so on
(cf. Figure \ref{fig:LifeNeighborhood}).
\item $Q = \{0, 1\}$ is the set of cell states.
\item $\sigma : Q^9 \rightarrow Q$ is the deterministic cell
transition function. It is composed by one elementary process, which
implements the aforementioned transition rules.
\end{itemize}
\lstinputlisting[float, label=lst:cal_life, caption=An OpenCAL implementation of the Conway's Game of Life.]{../opencal-examples/OpenCAL/cal_life/source/life.c}
The program in Listing \ref{lst:cal_life} provides a complete
OpenCAL-based implementation of Game of Life in just 60 lines of code,
by defining both the CA model and the simulation object, needed to let
the CA evolve step by step.
Header files at lines 3-5 allow to use the serial implementation of
OpenCAL. Specifically, the \verb'cal2D.h' header allows to define the
model and substates objects, as well as to define the elementary
processes composig the transition function. Instead, the
\verb'cal2DRun.h' header allows to define the simulation
object. Eventually, the \verb'cal2DIO.h' provides some basic
input/output functions for reading/writing substates from/to file. The
model object is then declared at line 9, while lines 10 and 11 declare
a substate and a simulation object, respectively.
Objects declared at lines 9-11 are defined later in the \verb'main'
function. In particular, the $Life$ CA object is defined at line 29 by
the \verb'calCADef2D()' function. The first 2 parameters define the CA
dimensions (the number of rows and columns, respectively), while the
third parameter defines the neighborhood pattern. Here, the predefined
Moore neighborhood is selected (cf. Figure
\ref{fig:LifeNeighborhood}), among those provided by OpenCAL. See
Listings \ref{lst:CALNeighborhood2D} and \ref{lst:CALNeighborhood3D}
for a list of OpenCAL predefined 2D and 3D neighborhoods,
respectively. Custom neighborhoods can also be defined by means of the
\verb'calAddNeighbor2D()' function. In both cases, 0-based indices are
progressively assigned each time a new cell is added to the
neighborhood. The fourth parameter specifies the boundary
conditions. In this case, the CA cellular space is considered as a
torus, with cyclic conditions at boundaries. The last parameter allows
to specify if the model has to use the so called \emph{active cells
optimization}, that permits to restrict the computation to only
\emph{non-stationary cells}. In this case, no optimization is
considered. The complete definition of \verb'calCADef2D()' is provided
in Listing \ref{lst:calCADef2D}. The \verb'calCADef3D()' 3D CA
definition function is also shown in Listing \ref{lst:calCADef2D} for
the sake of completeness. The \verb'CALNeighborhood2D' enum type
(Listing \ref{lst:CALNeighborhood2D}) allows to select a predefined or
a custom neighborhood to be defined in the application. In particular,
\verb'CAL_VON_NEUMANN_NEIGHBORHOOD_2D' corresponds to the von Neumann
pattern, \verb'CAL_MOORE_NEIGHBORHOOD_2D' to the Moore one,
\verb'CAL_HEXAGONAL_NEIGHBORHOOD_2D' and
\verb'CAL_HEXAGONAL_NEIGHBORHOOD_ALT_2D' to the hexagonal and
alternative hexagonal patterns, respectively (cf. Figure
\ref{fig:2Dneighborhood}, Section \ref{sec:CAInformaDef}). As regards
3D neighborhoods patterns, they are defined by means of the
\verb'CALNeighborhood3D' enum type (Listing
\ref{lst:CALNeighborhood3D}). Here, we can find the 3D equivalent
versions of the von Neumann and Moore neighborhoods, while hexagonal
neighborhoods are (obviously) not defined. Custom neighborhoods will
be discussed later in Section
\ref{sec:CustomNeiughbourhoods}. Similarly, the
\verb'CALSpaceBoundaryCondition' enum type (Listing
\ref{lst:CALSpaceBoundaryCondition}) allows to set cyclic condition at
boundaries. Eventually, the \verb'CALOptimization' enum type (Listing
\ref{lst:CALOptimization}) allows to consider the \emph{active cells
optimization}, discussed later in this Chapter.
\begin{lstlisting}[float, floatplacement=H, label=lst:calCADef2D, caption=Definition of the calCADef2D() function., numbers=none]
struct CALModel2D* calCADef2D (
int rows,
int columns,
enum CALNeighborhood2D CAL_NEIGHBORHOOD_2D,
enum CALSpaceBoundaryCondition CAL_TOROIDALITY,
enum CALOptimization CAL_OPTIMIZATION
)
\end{lstlisting}
\begin{lstlisting}[float, label=lst:calCADef3D, caption=Definition of the calCADef3D() function., numbers=none]
struct CALModel3D* calCADef3D(
int rows,
int columns,
int slices,
enum CALNeighborhood3D CAL_NEIGHBORHOOD_3D,
enum CALSpaceBoundaryCondition CAL_TOROIDALITY,
enum CALOptimization CAL_OPTIMIZATION
);
\end{lstlisting}
\begin{lstlisting}[float,label=lst:CALNeighborhood2D, caption=The CALNeighborhood2D enum type., numbers=none]
enum CALNeighborhood2D {
CAL_CUSTOM_NEIGHBORHOOD_2D,
CAL_VON_NEUMANN_NEIGHBORHOOD_2D,
CAL_MOORE_NEIGHBORHOOD_2D,
CAL_HEXAGONAL_NEIGHBORHOOD_2D,
CAL_HEXAGONAL_NEIGHBORHOOD_ALT_2D
};
\end{lstlisting}
\begin{lstlisting}[float, label=lst:CALNeighborhood3D, caption=The CALNeighborhood3D enum type., numbers=none]
enum CALNeighborhood3D {
CAL_CUSTOM_NEIGHBORHOOD_3D,
CAL_VON_NEUMANN_NEIGHBORHOOD_3D,
CAL_MOORE_NEIGHBORHOOD_3D
};
\end{lstlisting}
\begin{lstlisting}[float, label=lst:CALSpaceBoundaryCondition, caption=The CALSpaceBoundaryCondition enum type., numbers=none]
enum CALSpaceBoundaryCondition{
CAL_SPACE_FLAT = 0,
CAL_SPACE_TOROIDAL
};
\end{lstlisting}
\begin{lstlisting}[float, label=lst:CALOptimization, caption=The CALOptimization enum type., numbers=none]
enum CALOptimization{
CAL_NO_OPT = 0,
CAL_OPT_ACTIVE_CELLS
};
\end{lstlisting}
\begin{lstlisting}[float, label=lst:calRunDef2D(), caption=Definition of the calRunDef2D() function., numbers=none]
struct CALRun2D* calRunDef2D (
struct CALModel2D* ca2D,
int initial_step,
int final_step,
enum CALUpdateMode UPDATE_MODE
)
\end{lstlisting}
\begin{lstlisting}[float, label=lst:calRunDef3D(), caption=Definition of the calRunDef3D() function., numbers=none]
struct CALRun3D* calRunDef3D(
struct CALModel3D* ca3D,
int initial_step,
int final_step,
enum CALUpdateMode UPDATE_MODE
);
\end{lstlisting}
\begin{lstlisting}[float, label=lst:CALUpdateMode, caption=The CALUpdateMode enum type., numbers=none]
enum CALUpdateMode {
CAL_UPDATE_EXPLICIT = 0,
CAL_UPDATE_IMPLICIT
};
\end{lstlisting}
The CA simulation object is defined at line 30 by the
\verb'calRunDef2D()' function. The first parameter is a pointer to a
CA object (\verb'life' in our case), while the second and third
parameters specify the initial and last simulation step,
respectively. In this case, we just perform one step of computation,
being both the first and last step set to 1. The last parameter allows
to specify the substate update policy, which can be implicit or
explicit. The \verb'CALUpdateMode' enumeration, shown in Listing
\ref{lst:CALUpdateMode}, defines possible update policies. In the
first case, OpenCAL performs substates update automatically, while in
the second the user must explicitly apply the elementary processes
composig the transition function and update the involved
substates. The complete definition of \verb'calRunDef2D()' is provided
in Listing \ref{lst:calRunDef2D()}, while the 3D version of the same
function can be found in Listing \ref{lst:calRunDef3D()}. The
\verb'CALUpdateMode' type, shown in Listing \ref{lst:CALUpdateMode},
enumerates possible update policies.
Line 33 allocates memory and registers the \verb'Q' substate to the
\verb'life' CA, while line 36 registers an elementary process to the
transition function. The \verb'calAddSubstate2Di()' function is
self-explanatory, while \verb'calAddElementaryProcess2D()' must be
discussed more in detail. In particular, it takes the handle to the CA
model to which the elementary process must be registered and a pointer
to a callback function, that defines the elementary process itself. In
our example, we specified \verb'lifeTransitionFunction' as second
parameter, being it the name of a developer-defined function
implementing the transition function roles (cf. lines 14-24). As can
be seen, the elementary process callback function returns
\verb'void'. Moreover, it takes a pointer to a CA object as first
parameter, followed by a couple of integers, representing the
coordinates of the generic cell in the CA space. This is the function
prototype which is common to each elementary process.
Note that, for each computational step, each elementary process is
applied simultaneously to each cell. This characteristic is known as
\emph{implicit parallelism} and is obtained in OpenCAL by considering
two different working planes, namely \emph{current} and \emph{next},
for each registered substate. The \emph{current} plane is used to read
cells state at the current CA step, while the \emph{next} to store
updated values. In this manner, the \emph{current} plane remains
unchanged for the overall current CA step. As a consequence, even in
the case of serial computation, in which cells are updated once at a
time, the resulting effect is that all the cells are updated
simultaneously on the basis of their sates at the current
computational step. At the contrary, if updated states would be stored
in the \verb'current' computing plane, such updated values would
affect the state change of neighboring cells even in the current
computational step, and the computation would result actually
serial. When all the cells have been processed and their states
updated, computing planes are switched (i.e. \emph{next} becomes the
new \emph{current} and \emph{current} the new \emph{next}), and the
process is reiterated \footnote{The \emph{implicit parallelism} is
also used in the parallel versions of OpenCAL, with the difference
that more than one cell can be processed and updated concurrently by
exploiting more than one processing unit.}. Note that, besides
specific cases, working planes are completely transparent to the user.
When the user implements an elementary process, by defining its
callback function, a set of OpenCAL functions can be used to retrieve
the substates values for both the central and the neighboring cells,
and to update the central cell state. In the specific case of the Game
of Life, the \verb'calGet2Di()' function gets the central cell value
of the substate \verb'Q' (note that the central cell is identified by
the coordinates (i, j), coming from the parameters of the callback
function), the \verb'calGetX2Di()' function gets the value of the
\verb'Q' substate for the n-th neighbor, and the \verb'calSet2Di()'
function updates the value of the \verb'Q' substate for the
central cell. In the Game of Life example, we defined just one
elementary process, that therefore represents the whole cell
transition function. However, as we will see later, many elementary
processes can be defined in OpenCAL by simply calling the
\verb'calAddElementaryProcess{2D|3D}()' function many times. If the user
defines more than one elementary process, these will be applied in
the same order they were registered to the CA.
The \verb'calInitSubstate2Di()' function at line 39 sets the whole
\verb'Q' substate to the value 0 (for both the current and next
working planes), i.e. the value of the \verb'Q' substate is set to 0
in each cell of the cellular space. Lines 42-46, set the value of the
\verb'Q' substate for some cells to 1, in order to define a so called
\emph{glider} pattern. In this case, the \verb'calInit2Di()' function,
used for this purpose, takes the cells coordinates as the third and
fourth parameters, while the value 1 to be set was specified through
the last parameter. In this way, the initial condition of the system
was defined within the \verb'main' function, even if, as we will see
later in this Chapter, OpenCAL allows for registering an
initialization function, to be executed once before the simulation
loop.
The \verb'calSaveSubstate2Di()' function (line 49) saves the \verb'Q'
substate to file, while the \verb'calRun2D()' function (line 52)
enters the simulation loop (actually, only one computational step in
this example), and returns to the \verb'main' function when the
simulation is complete. The \verb'calSaveSubstate2Di()' is called
again at line 55 to save the new (last) configuration of the CA
(represented by the only defined \verb'Q' substate) to file, while the
last two functions at lines 58 and 59 release memory previously
automatically allocated by OpenCAL for CA, substates (actually, only
\verb'Q' in this case) and simulation objects. The \verb'return'
statement at line 61 ends the program.
Figures \ref{fig:life_0000} and \ref{fig:life_LAST} show the initial
and final configuration of Game of Life, respectively, as implemented
in Listing \ref{lst:cal_life}.
\begin{figure}
\begin{center}
\includegraphics[width=7cm]{./images/OpenCAL/life_0000}
\caption{Initial configuration of Game of Life, as implemented in Listing \ref{lst:cal_life}.}
\label{fig:life_0000}
\end{center}
\begin{center}
\includegraphics[width=7cm]{./images/OpenCAL/life_LAST}
\caption{Final configuration of Game of Life (actually, just one step of computation), as implemented in Listing \ref{lst:cal_life}.}
\label{fig:life_LAST}
\end{center}
\end{figure}
%% \begin{figure}
%% \begin{center}
%% \includegraphics[width=7cm]{./images/OpenCAL/life_LAST}
%% \caption{Final configuration of Game of Life (actually, just one step of computation), as implemented in Listing \ref{lst:cal_life}.}
%% \label{fig:life_LAST}
%% \end{center}
%% \end{figure}
\section{Custom Neighborhoods}\label{sec:CustomNeiughbourhoods}
In the Game of Life example, we used the predefined Moore
neighborhood. As already stated, OpenCAL also provides other 2D and 3D
predefined neighborhoods (cf. Listings \ref{lst:CALNeighborhood2D} and
\ref{lst:CALNeighborhood3D}). Furthermore, it allows for the
definition of custom neighborhood patterns.
%% \begin{lstlisting}[float, label=lst:calAddNeighbor2D-3D, caption=The calAddNeighbor2D() and calAddNeighbor3D() functions to define custom neighborhood patterns., numbers=none]
%% struct CALCell2D* calAddNeighbor2D(
%% struct CALModel2D* ca2D, // pointer to a 2D CA object
%% int i, // row coordinate
%% int j // column coordinate
%% );
%% struct CALCell3D* calAddNeighbor3D(
%% struct CALModel3D* ca3D, // pointer to a 3D CA object
%% int i, // row coordinate
%% int j, // column coordinate
%% int k // slice coordinate
%% );
%% \end{lstlisting}
\begin{lstlisting}[float, label=lst:calAddNeighbor2D(), caption=The calAddNeighbor2D() functions to define custom neighborhood patterns in 2D CA., numbers=none]
struct CALCell2D* calAddNeighbor2D(
struct CALModel2D* ca2D, // pointer to a 2D CA object
int i, // row coordinate
int j // column coordinate
);
\end{lstlisting}
\begin{lstlisting}[float, label=lst:calAddNeighbor3D(), caption=The calAddNeighbor3D() function to define custom neighborhood patterns in 3D CA., numbers=none]
struct CALCell3D* calAddNeighbor3D(
struct CALModel3D* ca3D, // pointer to a 3D CA object
int i, // row coordinate
int j, // column coordinate
int k // slice coordinate
);
\end{lstlisting}
In order to define a custom neighborhood,
\verb'CAL_CUSTOM_NEIGHBORHOOD_{2D|3D}' must be used as the third
parameter of the \verb'calCADef{2D|3D}()' function. By doing this, you
will start with an empty neighboring pattern and can subsequently call
the \verb'calAddNeighbor{2D|3D}()' function to add a cell to the
pattern (cf. Listings \ref{lst:calAddNeighbor2D()} and
\ref{lst:calAddNeighbor3D()}).
\begin{lstlisting}[float, label=lst:CustomMooreExample, caption=Example of custom neighbourhood pattern; the sequence of calls to the calAddNeighbor2D() function defines the Moore neighbourhood for the Game of Life CA., numbers=none]
// ...
int main ()
{
// define of the life CA and life_simulation simulation objects
life = calCADef2D (8, 16, CAL_MOORE_NEIGHBORHOOD_2D, CAL_CUSTOM_NEIGHBORHOOD_2D , CAL_NO_OPT );
//...
//add neighbors of the Moore neighborhood
calAddNeighbor2D(life, 0, 0); // neighbor 0 (central cell)
calAddNeighbor2D(life, - 1, 0); // neighbor 1
calAddNeighbor2D(life, 0, - 1); // neighbor 2
calAddNeighbor2D(life, 0, + 1); // neighbor 3
calAddNeighbor2D(life, + 1, 0); // neighbor 4
calAddNeighbor2D(life, - 1, - 1); // neighbor 5
calAddNeighbor2D(life, + 1, - 1); // neighbor 6
calAddNeighbor2D(life, + 1, + 1); // neighbor 7
calAddNeighbor2D(life, - 1, + 1); // neighbor 8
//...
}
\end{lstlisting}
Listing \ref{lst:CustomMooreExample} shows an example of how a custom
neighborhood pattern can be built for the Game of Life CA described
above. In particular, the Moore neighborhood is built by using a
sequence of nine calls to the \verb'calAddNeighbor2D()' function. The
first time the function is called, it adds the relative coordinates
(0,0) to the neighboring pattern. This first set of coordinates
receives the subscript identifier 0 and therefore can be used later to
refer the central cell. For instance, if
\verb'calSet2Di(life,Q,i,j,0)' is called, as at line 23 of Listing
\ref{lst:cal_life}, the relative coordinates of the neighbor 0
(specified as last parameters), i.e. (0,0), are added to the
coordinates of the cells the elementary process is applying to,
i.e. (i,j) (cf. second and third to last parameters), by obtaining the
cell (i,j) itself, which corresponds to the central cell by
definition. The subsequent calls to \verb'calAddNeighbor2D()' add
further couples of coordinates to the neighboring pattern, by
progressively assigning an integer subscript handle to each of
them.
Eventually, note that it is possible to define custom neighborhoods
starting from a predefined one. For instance, by considering the above
Game of Life example, it is possible to specify the value
\verb'CAL_MOORE_NEIGHBORHOOD_2D' as parameter for the
\verb'calCADef2D' function and then add further neighbors by calling
\verb'calAddNeighbor2D()' as many times the as the number of cells
have to be added.
\section{SciddicaT}\label{sec:sciddicaT}
In Section \ref{sec:cal_life} we illustrated an OpenCAL implementation
of a simple cellular automaton, namely the Conway’s Game of
Life. Here, we will deal with a more complex example, concerning the
implementations of one of the simplest versions of the Sciddica
landslide simulation models, namely SciddicaT. Different versions of
SciddicaT will be considered, ranging from a naive to a fully
optimized implementation.
%% Sciddica is a family of two-dimensional XCA debris flow models,
%% successfully applied to the simulation of many real cases, such as the
%% 1988 Mt. Ontake (Japan) landslide and the 1998 Sarno (Italy)
%% disaster. A simplified toy-version of Sciddica (SciddicaT in the
%% following) was here considered to be implemented in \verb"OpenCAL",
%% and its application to the 1992 Tessina (Italy) landslide shown.
\subsection{SciddicaT naive implementation}
The first, naive, version of SciddicaT here considered is formally
defined as:
$$SciddicaT_{naive} = < R, X, Q , P, \sigma >$$
where:
\begin{itemize}
\item $R$ is the set of points, with integer coordinates, which
defines the 2-dimensional cellular space over which the phenomenon
evolves. The generic cell in $R$ is individuated by means of a
couple of integer coordinates $(i, j)$, where $0 \leq i < i_{max}$
and $0 \leq j < j_{max}$. The first coordinate, $i$, represents the
row, while the second, $j$, the column. The cell at coordinates
$(0,0)$ is located at the top-left corner of the computational grid.
\item $X = \{(0,0), (-1, 0), (0, -1), (0, 1), (1, 0)\}$ is the von
Neumann neighborhood relation (cf. Figure \ref{fig:2Dneighborhood}), a
geometrical pattern which identifies the cells influencing the state
transition of the central cell. The neighborhood of the generic cell
of coordinate $(i, j)$ is given by
$$N(X, (i, j)) =$$
$$= \{(i, j)+(0,0), (i, j)+(-1, 0), (i, j)+(0, -1),
(i, j)+(0, 1), (i, j)+(1, 0)\} =$$
$$= \{(i, j), (i-1, j), (i, j-1), (i, j+1), (i+1, j)\}$$
Here, a subscript operator can be used to index cells belonging to the
neighborhood. Let $|X|$ be the number of elements in X, and $n \in
\mathbb{N}$, $0 \leq n < |X|$; the notation
$$N(X, (i, j), n)$$
represents the $n^{th}$ neighborhood of the cell $(i,j)$. Thereby, $N(X, (i, j), 0) = (i, j)$, i.e. the central cell, $N(X, (i, j), 1) = (i-1, j)$, i.e. the first neighbor, and so on.
\item $Q$ is the set of cell states. It is subdivided in the following
substates:
\begin{itemize}
\item $Q_z$ is the set of values representing the topographic altitude (i.e. elevation);
\item $Q_h$ is the set of values representing the debris thickness;
\item $Q_o^4$ are the sets of values representing the debris outflows from the central cell to the neighboring ones.
\end{itemize}
The Cartesian product of the substates defines the overall set of
states $Q$:
$$Q = Q_z \times Q_h \times Q_o^4$$
so that the cell state is specified by the following sextuplet:
$$ q = (q_z, q_h, q_{o_0}, q_{o_1}, q_{o_2}, q_{o_3})$$
In particular, $q_{o_0}$ represents the outflows from the central cell towards the neighbor 1, $q_{o_1}$ the outflow towards the neighbor 2, and so on.
\item $P$ is set of parameters ruling the CA dynamics:
\begin{itemize}
\item $p_\epsilon$ is the parameter which specifies the thickness of the debris that cannot leave the cell due to the effect of adherence;
\item $p_r$ is the relaxation rate parameter, which affects the size of outflows (cf. section above).
\end{itemize}
\item $\sigma : Q^5 \shortrightarrow Q$ is the deterministic cell
transition function. It is composed by two elementary processes,
listed below in the same order they are applied:
\begin{itemize}
\item $\sigma_1 : (Q_z \times Q_h)^5 \times p_\epsilon \times
p_r\shortrightarrow Q_o^4$ determines the outflows from the central
cell to the neighboring ones by applying the \emph{minimization
algorithm of the differences}. In brief, a preliminary control
avoids outflows computation for those cells in which the amount of
debris is smaller or equal to $p_\epsilon$, acting as a
simplification of the adherence effect. Thus, by means of the
minimization algorithm, outflows $q_o(0,m) \; (m=0,\ldots,3)$ from
the central cell towards its four adjacent cells are evaluated, and
the $Q_o^4$ substates accordingly updated. Note that, $q_o(0,0)$
represents the outflow from the central cell towards the neighbor
1, $q_o(0,1)$ the outflow towards the neighbor 2, and so on. In
general, $q_o(0,m)$ represents the outflows from the central cell
towards the $n=(m+1)^{th}$ neighboring cell. Eventually, a
relaxation rate factor, $p_r \in \; ]0,1]$, is considered in order
to obtain the local equilibrium condition in more than one CA
step. This can significantly improve the realism of model as, in
general, more than one step may be needed to displace the proper
amount of debris from a cell towards the adjacent ones. In this
case, if $f(0,m) \; (i=0, \ldots, 3)$ represent the outgoing
flows towards the 4 adjacent cells, as computed by the
minimization algorithm, the resulting outflows are given by
$q_o(0,m)=f(0,m) \cdot p_r \; (i=0, \ldots, 3)$.
%% , while the amount of debris remaining in the central cell is
%% obtained as: $$h_r(0) = q_0(0,0) = h(0) - \sum_{i=1}^4
%% q_4(0,i)$$
\item $\sigma_2: Q_h \times (Q_o^4)^4 \shortrightarrow Q_h$ determines
the value of debris thickness inside the cell by considering mass
exchange in the cell neighborhood: $h'(0) = h(0) + \sum_{m=0}^3
(q_o(0,m) - q_o(m,0))$. Here, $h'(0)$ is the new debris
thickness inside the cell, while $q_o(m,0)$ represents the inflow from
the $n=(m+1)^{th}$ neighboring cell. No parameters are involved in
this elementary process.
\end{itemize}
\end{itemize}
In the following Listing \ref{lst:cal_sciddicaT}, an OpenCAL
implementation of SciddicaT is shown.
\lstinputlisting[label=lst:cal_sciddicaT, caption=An OpenCAL implementation of the SciddicaT debris flows simulation model.]{../opencal-examples/OpenCAL/cal_sciddicaT/source/sciddicaT.c}
As for the case of Game of Life, the XCA model and the simulation
objects are declared at lines 22 and 35, respectively, and defined
later into the main function at lines 147 and 148, respectively. The
2D cellular space results in a structured grid of of \verb'ROWS' rows
times \verb'COLS' columns square cells, corresponding to $i_{max}$ and
$j_{max}$ of the formal definition, respectively (cf. lines 10-11),
and the von Neumann neighborhood is adopted. The cellular space is
still toroidal, as in $Life$, and no optimization is
considered. Regarding the simulation object, a total of \verb'STEPS'
steps (i.e. 4000 steps - cf. line 14) are set, and implicit substates
updating considered.
Substates and parameters are grouped into two different C structures
(lines 24-28 and 30-33, respectively). Substates are therefore bound
to the CA context by means of the \verb'calAddSubstate2Dr()' function
(lines 155-160), as well as elementary processes are defined as
callback functions and registered to the model object by means of the
\verb'calAddElementaryProcess2D()' function (lines 151-152).
The topographic altitude and debris thickness substates are
initialized from files through the \verb'calLoadSubstate2Dr()'
function (lines 163-164), while the remaining initial state of the
model is set by means of the \verb'calRunAddInitFunc2D()' function. It
registers the \verb'sciddicaTSimulationInit()' callback, which is
executed once before the execution of the simulation loop, in which
the elementary processes are applied to the whole set of cells of the
cellular space. Such callback function must return void and take a
pointer to a simulation object as parameter. Differently to an
elementary process, which can only access state values of cells
belonging to the neighborhood, this function can perform global
operations over the whole cellular space. In the specific case of the
$SciddicaT_{naive}$ model, the \verb'sciddicaTSimulationInit()'
function (lines 104-130) sets the values of all the outflows from the
central cell to its neighbors to zero, by means of the function
\verb'calInitSubstate2Dr()' (lines 110-113). Moreover, it sets the
values of the P.r and P.epsilon parameters (lines 116-117) and
initializes the debris flow source by simply subtracting the source
debris thickness to the topographic altitude. For this purpose, a
nested double \verb'for' is executed to check the debris thickness in
each cell of the cellular space. Here, the \verb'sciddicaT->rows' and
\verb'sciddicaT->cols' members of the CA object are used, which
represent the cellular space values of rows and columns,
respectively. Still, the \verb'calGet2Dr()' and \verb'calSet2Dr()'
functions are here employed to read/update substates values inside the
cells. Alternatively to the double \verb'for' loop, it would be
possible to define a fictitious elementary process in which otflows
are locally set to zero, and thus call the
\verb'calApplyElementaryProcess2D()' to apply it to the whole
computational domain.
Line 168 registers a \emph{steering} callback by means of the
\verb'calRunAddSteeringFunc2D()' function. Steering is executed at the
end of each computational step (i.e. after all the elementary
processes have been applied to each cell of the cellular space), and
can perform global operations over the whole cellular space. The
steering callback prototype must return void and take a pointer to a
simulation onject, as for the \verb'sciddicaTSteering()' callback
(lines 132-140). In the specific case of $SciddicaT_{naive}$, the
steering callback simply sets to zero outflows everywhere through the
\verb'calInitSubstate2Dr()' function. Again, as for the case of the
init callback, a fictitious elementary process could be considered for
this purpose.
The function \verb'calRun2D()' (line 171) enters the OpenCAL
simulation loop, which executes a total of 4000 steps (cf. lines 14
and 148). Eventually, the final debris flow path is saved to file by
means of the \verb'calSaveSubstate2Dr()' function (line 176) and
previously allocated memory is released (lines 179-180).
As regards the elementary processes, the first one, $\sigma_1$, is
defined at lines 38-88, while the second, $\sigma_2$, at lines
91-101. In both cases, the \verb'calGet2Dr()' \verb'calGetX2Dr()'
functions are employed to get values for the central cell and its
neighbors, respectively. Moreover, the \verb'calSet2Dr()' function,
updates the central cell state.
Figure \ref{fig:sciddicaT} shows the $SciddicaT_{naive}$ simulation of the 1992
Tessina (Italy) landslide. Both the initial landslide source and the
final flow path configuration are shown.
\begin{figure}[htbp]
\centering
\includegraphics[width=9cm]{./images/OpenCAL/sciddicaT}
\caption{$SciddicaT_{naive}$ simulation of the 1992 Tessina (Italy)
landslide. Topographic altitudes are represented in gray scale
(black for lower altitude, white for the highest
elevation). Debris thickness is represented with colors ranging
from red (lower values) to yellow (higher values). (a) Initial
configuration. (b) Final debris flow path. Note that the graphic
output was generated by using the \texttt{cal\_sciddicaT-glut}
application, that implements the $SciddicaT_{naive}$ model and
provides a minimal visualization system. You can find
\texttt{cal\_sciddicaT-glut} in the examples directory.}
\label{fig:sciddicaT}
\end{figure}
As regards computational performance, the simulation shown in Figure
\ref{fig:sciddicaT} was executed on an Intel Core i7-4702HQ CPU @
2.20GHz by exploiting only a single core. The simulation lasted a
total of 240 seconds and considered a total of 4000 computational
steps.
\subsection{SciddicaT with active cells optimization}\label{sec:sciddicaT_active}
In this section we present a computationally improved version of
SciddicaT, which takes advantage of the built-in OpenCAL active cells
optimization. As stated above, this optimization is able to restrict
computation to a subset of cells which are actually involved in
computation, by neglecting those cells that will not change state to
the next step (stationary cells).
In the case of SciddicaT, only cells containing debris and their
neighbors can change state to the next step, as they can be
interested in mass variation due to outflows and inflows. At the
beginning of the simulation, we can simply initialize the set of
active cells to those cells containing debris (i.e. those cells
forming the initial landslide source). Moreover, we can add to this
set new cells or remove some ones from it. Specifically, if an outflow
is computed from an active cell towards a neighboring non-active
cell, this latter can be added to the set of active cells and
considered for state change by the remaining elementary processes in
the current computation step \footnote{Remember that, by default,
substates are updated after the application of each elementary
process.} (if any), or by the next computational step. Similarly, if
a given active cell looses a sufficient amount of debris, it can be
eliminated from the set of active cells. In the case of SciddicaT,
this happens when its thickness becomes lower than or equal to a given
threshold (i.e. $p_\epsilon$).
In order to account for these processes, we have to slightly revise
the SciddicaT definition. In particular we have to add the set of
active cells, A. The optimized SciddicaT model is now defined as
$$SciddicaT_{ac} = < R, A, X, Q , P, \sigma >$$
where $A \subseteq R$ is the set of active cells, while the other
components are defined as before. The transition function is now defined as:
$$\sigma : A \times Q^5 \shortrightarrow Q \times A$$ denoting that it
is applied to only the cells in $A$ and that it can add/remove active
cells. More in detail, the $\sigma_1$ elementary process has to be
modified, as it can activate new cells. Moreover, a new elementary
process, $\sigma_3$, has to be added in order to remove cells that
cannot produce outflows during the next computational step due to the
fact that their debris thickness is negligible. The new sequence of
elementary processes is listed below, in the same order they are
applied.
\begin{itemize}
\item $\sigma_1 : A \times (Q_z \times Q_h)^5 \times p_\epsilon \times p_r
\shortrightarrow Q_o^4 \times A$ determines the outflows from the
central cell to the neighboring ones, as before. In addition, each
time an outflow is computed, the neighbor receiving the flow is
added to the set of active cells.
\item $\sigma_2: A \times Q_h \times (Q_o^4)^4 \shortrightarrow Q_h$ determines
the value of debris thickness inside the cell by considering mass
exchange in the cell neighborhood. This elementary process does not
change with respect to the $SciddicaT_{naive}$ version.
\item $\sigma_3: A \times Q_h \times p_\epsilon \shortrightarrow A$
removes a cell from $A$ if its debris thickness is lower than or
equal to the $p_\epsilon$ threshold.
\end{itemize}
In order to implement the $SciddicaT_{ac}$ debris flows model in
OpenCAL, we have to change the definition of the CA object, by also
adding the third $\sigma_3$ elementary process. Moreover, the
$\sigma_1$ elementary process has to be changed. A complete
implementation of the active cells optimized version of SciddicaT is
shown in Listing \ref{lst:cal_Sciddicat-activecells} for the sake of
completeness, even if only the differences with respect to the
original implementation are commented.
\lstinputlisting[label=lst:cal_Sciddicat-activecells, caption=An OpenCAL implementation of the $SciddicaT_{ac}$ debris flows simulation model with the active cells optimization.]{../opencal-examples/OpenCAL/cal_sciddicaT-activecells/source/sciddicaT.c}
As noted, few modifications to the original source code are needed to
add the active cells optimization. In particular, the active cells
support is enabled by means of the \verb'CAL_OPT_ACTIVE_CELLS'
parameter at line 159, while the third elementary process added at
line 165. As regards the elementary process $\sigma_1$, it is the same
of that adopted in $SciddicaT_{naive}$, with the exception that when
an outflow is generated, the cell receiving the flow is added to the
set A of the active cells (line 87). Note that the
\verb'calAddActiveCellX2D()' can be considered as an unsafe function,
as it affects the activation state of a neighboring cell. For this
reason, it is important to maintain the activation and deactivation
phases completely disjoint, in order to avoid possible race conditions
and thus logical errors. As a matter of fact, the $\sigma_1$
elementary process only add cells to A, while is the $\sigma_3$ one
that removes cells from A when they become inactive (lines
107-108). According to the formal definition of $SciddicaT_{ac}$, this
occurs in the case the debris thickness becomes lower than or equal to
the $p_\epsilon$ threshold parameter. Since, in general, the cell state
activation/deactivation is threshold-dependent, the active cells
optimization is also known as \emph{quantization}.
Regarding the computational performance, the same simulation shown in
Figure \ref{fig:sciddicaT} was executed using the $SciddicaT_{ac}$
implementation. Still, only a single core of the Intel Core i7-4702HQ
CPU was used. The simulation lasted a total of 23 seconds, versus 240
seconds obtained for the naive version, which is about 10.5 times
faster. Indeed, this can be considered a very good result and can be
easily obtained. In general, simulations which involve only a subset
of the whole computational domain can take advantage by the active
cells optimization.
\subsection{SciddicaT with direct neighbors update}\label{sec:sciddicaT_extended}
OpenCAL allows for a further optimization by means of the so called
\emph{unsafe operations}, i.e. operations that are not strictly
permitted by the formal definition of Cellular Automata. Obviously, in
order to be well defined, a CA exploiting unsafe operations must be