Disturbance Cellular Automata – Example 12.2
Cellular Automata are used in many applications to understand and simplify complex natural phenomena. Sand dunes, braided river networks, and ecosystems are just a few of the things that authors have attempted to translate into simple rules and which in the end generate complex results.
This script is based on a well-known cellular automata known as the “Forest-Fire Model” and can be used to model patterns of disturbance in ecosystems. While this could be used to model a fire, the results seemed to slow moving to be a true fire…that is, new growth sprouted up too quickly in the wake of the fire. So to me it seemed more like a slowly, but relentlessly spreading disease or parasite, which can devastate natural systems sometimes much worse than a fire. By adjusting parameters such as growth-rate and chance of spontaneous outbreak, lessons can be learned about how real ecosystems might function.
Step One – Initial Setup
The setup here will be similar to the previous script, except this time instead of using a regular grid of cells, we will use a random population of points, scaled based on an average “Area per tree.” I did a quick mesurement of the spacing of trees in a mature beech forest, and determined 300 square meters per tree is a reasonable figure. This is used to determine the geometry in “Cell Centers”
Like the previous example, we also will do a proximity test to determine the vectors along which the disease can spread. In this case, we limit the potential spread to 20 meters. You could use a higher number here later, which will impact how easily and how quickly disease can spread. The “T”apology output of the “Proximity2D” component goes into a data container for later use, but you can see in the image these topological relationships generated in the “L” output.
The last thing we do here is a list of random cell states. For this script, we will have three states. 0 (vacant/dead), 1 (alive), and 2 (infected/dying). We start with only 0’s and 1’s.
To see these results visually, we draw a circle at each center point and use the random number data to color the cell based on its state. Like in the last script, we will move the coloring process after the loop once we create it in the next step.
Step Two – Loop Procedures Three and Four
Like the last script, the loop only recalculates a list of numbers called the “Cell State.” This can be 0, 1, or 2 as previously explained. Every time the loop runs, four basic operations will be performed to determine if the cell state changes, and what it changes to. The operations, in this order, are:
1-Cells that are infected die. That is If the cell-state is equal to 2, it will now be reset to 0
2-Living Cells that are in the “neighborhood” of a cell that was infected in the previous round, become infected. That is, If the cell state is equal to 1 AND at least one of the cells in that cell’s neighborhood (determined by the T output of the Proximity component) was equal to 2 at the start of the round, then the cell becomes infected, going from 1 to 2.
3-A new plant has a chance to sprout in each vacant cell. This is determined by comparing a random list of values to a probability test. If this chance is 5%, then in each round, about 5% of the cells will randomly go from 0 to 1.
4-Test for “spontaneous” outbreak. There is a chance that a living cell will spontaneously become infected, despite not being near a neighbor. In nature, spontaneous outbreaks of disease can be caused by introduction of a foreign pathogen to a new environment, by mutation of a previously benign version of a disease, and other causes, but these are by nature, very rare. For our first example, we will have an infection probability of 0.02% to see what happens. But in the rare cases of spontaneous infection the cell would go from 1 to 2.
How does this translate into a code? Since there are no “2’s” at the start, we will not worry about coding the first two steps quite yet. We will focus on three and four since they are set up in almost the exact same way. The most important thing here is to generate and structure a random list of numbers. All in all, we will need many Many MANY random numbers. The precise number is the number of rounds we will be looping, multiplied by the total number of objects or “Cells”. So if we are doing 200 rounds, and have 2000 trees, that is a whopping 400,000 random values! Don’t worry, the computer can handle it 😉 More importantly, we only want it to have access to 2000 (or whatever the number of “Cells” is) of those random values at a time. To get this, we use the “Partition” list component with the size of the partitions based on the “List Length” or the number of cells (2000 in our example). We then use “Flip Matrix” and “List Item” so that in round 0, we will get access to each item 0, in round 1, each item 1, etc…. This was a lot of number gymnastics!! But if we get it to work with our first list, we simply copy and paste to get a second list that will work for Procedure 4. The results of this are in the image below.
Once we have these, we now script the procedures 3 and 4 themselves. We again use if/then expressions as explained in Example 12.1. In this case the expression is “if (x>y,1,0)” which translates into “if x is greater than y, then the result is equal to 1, or else if not, it is equal to 0.” X will only be greater y than 5% of the time based on how we wrote this (see below). This is then compared to the existing list of values with the “Max” component.
Once this is done, the values go into the final test, to see if a spontaneous outbreak will occur. This is scripted in exactly the same way.
Once we let this run a couple of rounds, the empty cells will slowly start filling up with living trees (zeros becoming 1s). This could go on forever if we didn’t have the disease procedure 4. Unfortunately for our forest, after a few rounds, finally one of the random values didn’t pass the if/then test, and has now become infected!! It is now time to write a procedure for what happens if disease breaks out.
Step Three – Refining the Loop / Procedures 1 and 2
To see the fate and destruction of our once flourishing forest, we will script two procedures. The first is very simple. The second a bit more complex. The first is an if then expression “if (x=2,0,x)” This translates to “if x is equal to 2 (infected), then it now is equal to 0 (dead), and if not, it remains x.” So if it was 0 or 1 before, it will remain 0 or 1, but if it were 2, it is now 0. Got it?!
The second is a bit more complicated. We use our typology relationship determined at the start (finally!) and use list item to list, for each cell, the value of all the cells that are in its neighborhood at the beginning of the round, before we killed them in the last step, otherwise there would be no infected cells left. Some cells have rather small neighborhoods (2 or 3 neighbors), while for others it is a bit larger. We then use the “Bounds” component to get the minimum and maximum value in each set. If there is no infected neighbor, the bounds will be between 0 and 0 or 0 and 1. In both these cases, things are looking good for the cell in question. If One neighbor is infected, the bounds will be between 0 and 2. This means things are not good, and the cell in question would now go from a 1 to a 2.
Scripting this requires a tricky if/then/and equation. The syntax is quite difficult at first. We bring in two variables, X and Y. “X” was our current cell state “Y” is the highest value in the neighborhood. First I will write out the expression precisely, and then will translate it.
“if (x=1, if (y=2,2,x),x)”
>:-( What that means is “if x is equal to one, AND if y is equal to 2, then the result is equal to 2, and if not, it is equal to x…in both cases.” Don’t worry if you don’t understand it exactly at first, but if you do, you are smarter than me! Remember, in an if/then expression, if the condition is true, it does whatever is after the first comma. “if (x=1, if (y=2,2,x),x)” If the condition is not true, it does whatever is after the second comma. So if X is not equal to 1, it immediately skips the second if/then expression, jumping to the second comma, and producing the value X as a result.
Anyways, the results of this altogether can be seen in the images below.
You can see that the cells that are yellow in each round become white in the next round, while all neighbors become yellow in that round.
If the density of living cells is very high, the infection will spread relentlessly in a big wave across the forest. If the density of living cells is low, the infection will tend to peter out, having no living neighbors to jump to.
Playing with Variables
By playing with the variables, growth rate and infection rate, certain patterns tend to emerge, although the landscape will always be in flux. If infections are very rare, with high growth rate, a very dense forest will tend to emerge, and when an infection does break out, the devastation is complete and catastrophic. If infection rates are high, sometimes a low growth rate will actually do better in managing this in the long term. In other words, sometimes it is better to bounce back slowly after disturbance than to bounce back too fast while the infection is still raging. Otherwise, the disease will become endemic.
Below are some images of two scenarios where the script is extended. Note that once the size gets pretty big, the patterns will be much more interesting, but the computer will also slow down quite a bit.
Taking it Further
I played around with this quite a bit to try and improve the results. I won’t show my coding, but a few things you can play around with include introducing the probability that an infected cell can survive and go onto live another day, and also scaling the cells down (through a second data stream) to show growth. In other words, new cells come in at size “1” and increase every round until they reach a maximum size.