Since I began doing research on modeling, I’ve been thinking about learning to do some modeling of my own as an ethnographic exercise. I couldn’t justify writing about the practice of modeling without some experience of my own so that I could understand first-hand what it’s like to build a model from start to finish. As a result, this weekend I spent some time learning to construct a simple model using NetLogo – an open source agent based modeling software. Here are my results.
First, I had to decide what kind of model to build. I wanted it to be something simple so that I wouldn’t get in over my head, but also complex enough that I would actually be challenged by it. A lot of ideas came to me, but finally I settled on building a simple model of erosion. NetLogo comes with a variety of sample models that you can play with, and there is an erosion model among them, but I don’t like the way it works. The only agents are patches – stationary agents that can have different characteristics and affect other agents – and the flow of water is simulated by making the patches themselves simulate water flow. What I wanted was more like the Grand Canyon flow model that comes with the package created by Uri Wilensky. In this model, little drops of water (representing some unknown quantity of water) flow across a landscape of patches with elevations drawn from a GIS data set from a region of the Grand Canyon. It’s a cool little model, but there’s no erosion built in. As a result, the water simply flows down, pools, and then flows out when it reaches the edge of the map – the landscape doesn’t change in response to the water. So what I wanted was a hybrid of these two approaches.
I began constructing from scratch. I don’t remember the exact order in which I constructed the model, but I had to do a number of things: 1) I had to generate a random background of patches with varying elevations whose color matches the elevation, 2) I had to create water and randomly distribute it around the landscape, 3) I had to make the water flow from higher elevation to lower elevation, and 4) I had to make the water erode the landscape by reducing the elevation of each patch whenever water flows through it. It all seemed simple, and it was, but it took a lot of time to figure out exactly how to get all of the agents to do what I wanted them to do. I won’t go into detail on the entire process of building the model, but here are some of the issues that I ran into:
- Making the background and assigning each patch an elevation was easy enough, but getting the patches to adjust their color to reflect their elevation took some time to figure out. It turns out that there is a function that does this, but I had to wade through a lot of documentation and the code of a few other models to figure out how it works.
- Getting the water to flow was also pretty easy – there’s a built in function (downhill) that makes this possible. However, when I did this, the water tended to simply pool infinitely in single patches. I wanted the water to flow more, so I consulted the Grand Canyon model to see how they made water flow. By assigning a height to the water and using that in conjunction with the elevation, it was possible to limit the amount of pooling in each individual cell.
- Getting the landscape to erode was a real problem at first. I had the water flowing, but it wasn’t altering the landscape at all. I realized that I couldn’t have the water “pick up” a portion of the land and carry it away. I could do that, but it would have been very complicated, so instead I wanted to have the patches evaluate how much water was on it at any given time, and whether or not the water would be flowing in the next turn. With both of those evaluations, the patches would then respond by reducing its elevation proportionately. I played around with the if/else statements, but getting the procedure to work properly took a lot of time and required several phases of trial and error. The problem I had then was that certain portions of the landscape were eroding infinitely, so I had to again limit the amount of erosion possible on any given patch, and also make it so that the water that wasn’t flowing, but simply pooling, wouldn’t affect the elevation.
I did get all of these things to work, though not perfectly, and the water wasn’t behaving in exactly the way that I wanted it to, so I decided to take another approach. The whole time I was trying to build a model entirely from scratch, but after I had done all of that work, I realized that I could just use the code built for the Grand Canyon model and add in an erosion function like the one I had built in my own. I copied and pasted the erosion function into the other model and modified it to fit the parameters defined in that model – this also took some trial and error, but ended up being pretty straight forward. Here are some examples of the model runs:
This is a screenshot of my original model. You can see the landscape with water, mostly pooling in different places. The top three sliders on the side adjust the rate of erosion, the rate of evaporation, and the amount of rain. The fourth slider is for setting up the terrain at the beginning – it determines how much of a gradient there is in the landscape. If it’s set low on setup, then there will be large differences in the elevations between patches, so you could have a patch with 1000 elevation next to a patch with 0 elevation. If it’s set high, then the landscape is more evenly distributed. The draw function simply has the water draw a path as it moves – this is useful for seeing how water flows, but I don’t think it does very much in this model.
These two are before and after images of the terrain. The first is how the terrain looks with ruggedness set to a mid-range immediately after setup. The second is what it looks like after about 1000 ticks. You can see that there is more dark area on the second, and the water is beginning to pool in certain places. Since the landscape starts off fairly flat, there aren’t any major streams where water tends to collect.
This is what the Grand Canyon elevation model looks like at the start. As I said, it draws elevation data from a file in which elevation from a portion of the Grand Canyon is stored. That’s why it looks smoother and more dramatic than mine above.
This is what the Grand Canyon model looks like after I’ve run it with my erosion function included. Obviously some parts get more eroded than others, and this becomes a self-reinforcing process forming these streams and tributaries.
This is what the model looks like when it’s running. You can see the water running over the landscape, pooling in certain places, and flowing from one pool to the next. Over time, depending on the flow rate and the evaporation rate, these pools of water will grow and become streams. One of the ways my model differs from the original in its outcome is that the water in the original tends to collect more in larger pools. Mine tends to stay in narrow paths because the earth on those paths is eroding faster than elsewhere.
There are some obvious limitations to this model. First, it’s not very empirically based aside from the elevation data. I don’t know how much water one drop represents except that it’s “height” is 10 (I also don’t know what units the elevation is measured in). As a result, I have no idea if the erosion rate corresponds to what one would expect for this kind of landscape with the given water flow. I also have it set to prevent it from eroding too far into the negative values – mostly because I want to be able to see the cumulative effects of erosion across the landscape rather than concentrating it in potentially infinitely deep sections of the water bed.
The only thing I still want to do with it is to make it so that I can randomly generate a landscape again instead of using the Grand Canyon data. That won’t be hard, I just haven’t had time to create the code. Here is the code for my model. If you want, you can download it and play around with it – see the effects of erosion on this section of the Grand Canyon (you’ll need the elevation data file, here – just put it in the same folder as the model code – and you might also have to play with the formatting a bit).
breed [waters water]
breed [raindrops raindrop]
water-height ;; how many feet tall one unit of water is
border ;; keep the patches around the edge in a global
;; so we don’t ever have to ask patches in go
;; Setup Procedures
;; reading the external file is startup rather
;; than setup so we only do it once in the model
;; running the model does not change the elevations
;; read the elevations from an external file
;; note that the file is formatted as a list
;; so we only have to read once into a local variable.
file-open “Grand Canyon data.txt”
let patch-elevations file-read
set color-max max patch-elevations + 200 ;; put a little padding on the upper bound so we don’t get too much
;; white and higher elevations have a little more variation.
let min-elevation min patch-elevations
;; adjust the color-min a little so patches don’t end up black
set color-min min-elevation – ((color-max – min-elevation) / 10)
;; transfer the date from the file into the sorted patches
( foreach sort patches patch-elevations
[ ask ?1 [ set elevation ?2 ] ] )
set-default-shape turtles “circle”
;; just clean up the marks that the raindrops have made
;; and set some global variable to defaults
[ set pcolor scale-color brown elevation color-min color-max ]
set water-height 10
set border patches with [ count neighbors != 8 ]
;; Runtime Procedures
;; check for mouse clicks on empty patches.
;; if we’ve got a winner make a manual raindrop that
;; is red.
if mouse-down? and not any? turtles-on patch mouse-xcor mouse-ycor
;; even when raindrops are hidden
;; newly created manual drops will
;; be visible
[ setxy mouse-xcor mouse-ycor
set size 2
set color red
;; make rain-rate drops randomly
[ move-to one-of patches
set size 2
set color blue ]
[ ask turtles [ pd ] ]
[ ask turtles [ pu ] ]
ask raindrops [ flow ]
ask waters [grow-old]
;; when raindrops reach the edge of the world
;; kill them so they exit the system and we
;; don’t get pooling at the edges
ask turtles-here [ die ]
to flow ;; turtle procedure
;; get the lowest neighboring patch taking into account
;; how much water is on each patch.
let target min-one-of neighbors [ elevation + (count turtles-here * water-height) ]
;; if the elevation + water on the neighboring patch is
;; lower than here move to that patch.
ifelse [elevation + (count turtles-here * water-height)] of target
< (elevation + (count turtles-here * water-height))
[ move-to target ]
[ set breed waters ]
ask patches [
if count raindrops-on self >= 1 ;if there are raindrops on this patch
and [elevation] of min-one-of neighbors [elevation] < [elevation] of self ;and the raindrops are flowing
[set elevation (elevation – count raindrops-on self)] ;reduce elevation of this patch by one
;for each raindrop present
[ set pcolor scale-color brown elevation color-min color-max ] ;change patch color to indicate erosion
set age age + 1 ;water ages 1 each tick
if evaporation-rate >= 1 and age > (11 – evaporation-rate) [die] ;waters die after a certain
;number of ticks depending on the
;evaporation rate selected
; Copyright 2006 Uri Wilensky.
; See Info tab for full copyright and license.