Coding 101 in GAMA & FAQs

This document consists of three parts.

  1. The first part highlights some good practice guidelines for coding in general. This part is primarily dedicated to newbie programmers and can be skipped, if you already have experience in programming. It is not specific to GAMA.
  2. The second part relates to debugging. This section is also dedicated to persons, who have not too much experience with programming. It is a guideline to track down errors in the code with a lot of hands-on exercises for you. Error tracking usually takes much more time than actually writing the code. This is normal, nothing to worry about. However, you will find it very useful to take a systematic approach to find the errors. It will save you a lot of time.
  3. The third part is a growing collection of forum questions. Together, this is a living document and I will be happy to take up on your suggestions what to add!

Part I: Good practice in coding

1 GAML programming language

At the first sight, computer languages seem pretty incomprehensible for a nonprogrammer. However, a computer cannot understand a computer program at all! A computer needs to work with bits and bytes – that is, series of zeros and ones. To translate a computer program into machine readable instructions, we need an “interpreter” or a “compiler”. An interpreter reads a code line by line and can translate and execute it on the fly, whereas a compiler first needs to read the entire code and before it can translate and execute it. Interpreted languages are also known as scripting languages (e.g. python or JavaSript), whereas compiled languages are what we understand as ‘real’ computer programming languages (e.g. java or C#).

The programming language that is used to develop models in GAMA is called GAML, which simply stands for GAma Modelling Language. GAML has its roots in compiled computer languages. The specific feature of GAML is that it is not a general purpose language like Java, but it is a domain-specific language that is designed to specially support the implementation of spatial simulation models. Domain specific languages have two advantages compared to their ‘big brothers’: first, they are easier to understand and to learn, and second, their domain-specific functionalities are more powerful. However, general building blocks and coding principles apply to GAMA as for any other programming language. So, if you have learned to work with GAMA, you will find it relatively easy to also learn other languages.

Programming with GAMA is greatly supported by its integrated development environment (IDE), which is built on the widely used Eclipse environment. The IDE supports a programmer through code coloration, automatic indents, syntax and coherency checking, on-the-fly warning and error messages, concerted variable renaming and much more.

2 Building blocks

Agents and cells

The building blocks of GAML are agents and cellular grids. A specific type (e.g. a lion or a zebra) of an agent is called a species. A specific cellular grid (e.g. a raster map of the savanna soil or the grassland) is called a grid species.

All attributes, behaviour and visualisation specifications are held together in one code block. If you have for example five agents of that species (e.g. 5 zebras) the entire code block will be executed five times, once for each agent.

Variables

Each species has properties (e.g. lion has some age and the grass has some biomass). These properties are represented as variables of that species. To tell the computer that your species has a specific species variable, you need to declare the variable at the beginning of the species code section. The declaration of a variable states the variable type (e.g. int, float, string) and its name (you can choose any name, but follow the convention of using small letters – GAMA is case sensitive!). The assignment of values to the variable is not part of the declaration and can be done at a later step. Considering again our five zebras, we will have one zebra variable (e.g. age), but five values (e.g.1, 5, 2, 3, 7 years). Analogously, we will have one grid variable (e.g. biomass), but as many values as there are cells in the grid.

Species variables are local variables. The computer only ‘understands’ them locally, within the species section. Apart from local species variables, we also have global variables. These are declared in the global section and it can hold only one value. A global variable is understood everywhere in the code.

Behaviour

Active behaviour of individual agents is the very core of agent-based modelling. In GAMA behaviour is represented as a code block that binds together a couple of commands that together make the agent move around, grow, or eat zebras.

Programmers calls such code blocks that ‘do something’ functions or procedures. In GAMA, there are three types of procedures: reflex, action, and init. Reflex procedures are executed automatically at each time step. Action procedures are executed only when they are called from somewhere else (e.g. if a male lion is older than 2 years it chases females). Init procedures are only executed once, to setup the model (e.g. to load the data, or to create the initial set of agents).

Visual display

This is the specification, how species are represented visually. It is an important programming paradigm to clearly separate content from its display. The visualisation specifications of species are collected in a dedicated procedure type at the end of a species section: the aspect. You can specify two parallel views at the model by defining two aspects.

Simulation

A simulation is the iterative execution of model code. In GAMA the simulation output is governed by the experiment section – the fifth top-level element after model, global, species, and grid.

3 Structure and indents

Organisation is half the battle. Computer languages are structured in levels. Each level is made visually separated by an indent. Keeping to that structure makes it much easier to orientate yourself in the code and to spot missing curly brackets. The IDE (integrated development environment) helps you to keep the order in various ways:

Indents

When you open a curly bracket, the GAMA IDE knows that a new, embedded level begins. It therefore will automatically add an indent to the right when you hit enter to start a new line of code. On the outer left level (the top-level) you will only find the five leading structural elements of GAMA: model, global, species, grid, and experiment. For each embedded element, a new level will be added. Here we have a short code example with four embedded levels:

global {
  init {
    create species number: 1 {
      age <- 0;
    }
  }
}

Two shortcuts are helpful to shift an entire code block to the left (Shiftand Tab) or to the right (CapsLockand Tab).

Code folding

to keep the overview over long codes, you can use the (-) signs next to a hierarchical element. The entire section with all sub-levels will collapse.

Code folding collapses all code lines that belong to the selected hierarchical element.

Figure 13.5: Code folding collapses all code lines that belong to the selected hierarchical element.

Visualise pairs of brackets

A very handy feature to search for missing brackets is to visualise bracket pairs by locating your cursor right after a bracket. The corresponding opening or closing bracket will be highlighted.

Bracket pairs that belong together are highlighted, if the cursor is placed right after it. Here, the curser is behind the closing bracket, which highlights the corresponding opening bracket with a grey rectangle.

Figure 13.6: Bracket pairs that belong together are highlighted, if the cursor is placed right after it. Here, the curser is behind the closing bracket, which highlights the corresponding opening bracket with a grey rectangle.

Code outline

In the code outline view you will see the structural overview of your code. You will see all your agents, their attributes and their behaviour. In this overview it is possible to interactively collapse and unfold lower levels like in the code. If this view is not visible on your GAMA interface, you can activate it via the main menu -> Views -> Other -> Show view -> Outline

The code outline (window bottom left) summarises the hierarchy of elements in the code, like species with their attributes, reflexes, actions and aspects.

Figure 13.7: The code outline (window bottom left) summarises the hierarchy of elements in the code, like species with their attributes, reflexes, actions and aspects.

4 Code elements

Curly brackets

One structural element is always held together with curly brackets.

Semicolon

The semicolon is an important code element, which indicates the end of a statement. Commonly this is also the end of a code line. GAMA will correctly interpret several statements in one line, if they are separated by semicolons, but this makes it hard to read the code for human programmers. It is thus common practice to start a new line after a semicolon has ended a statement. If you forget a semicolon, the subsequent code will throw a lot of errors. So, if you suddenly encounter a lot of errors that have not been there before, this is likely to be due to a missing semicolon or a missing bracket.

5 Commenting

Code commenting

is another best practice standard in coding. A comment is the short summary or explanation of a code part or a code line in plain English. This helps a lot to better understand code – including the code of other persons. For example, if you comment the modifications that you made in the assignments, this will help me a lot to understand what you did. However, you will be surprised how helpful it is for yourself to comment your own code. It will help you remember what you were thinking when you revisit your model after some weeks break. In GAMA the double slash (//) comments out a code line. The comment will turn to green, to make it stick out visually

// reproduce 1 zebra every 5th time step
reflex reproduce {
  if cycle mod 5 = 0 {
    create zebras number: 1;
  }
}

Code commenting can also serve another purpose: when you want to track down an error, it may be helpful to “turn off” some recently added code parts. GAMA supports you in commenting out larger chunks of code. Just select the respective code and together click CTRL and 7 (or navigate to the “Toggle Comment” command in the menu that pops up upon right mouse-click on the selection like in Figure 13.8).

The Toggle Comment function in GAMA's context menu.

Figure 13.8: The Toggle Comment function in GAMA’s context menu.

Part II: Debugging

Debugging is the procedure to identify and eliminate errors and flaws in your code. It is an integral part of programming. As you develop a new part of your code, you will immediately test and debug it. A systematically tested and debugged code is verified: a verified code does, what it is expected to do.

  • Testing is an integral part of programming
  • Test, while you write
  • Test, before you use the model

For effective debugging, you systematically look into the five different kinds of errors. GAMA supports this task, as it continuously compiles the code that you enter and immediately reports on errors. However, the compiler only finds syntactic and semantic errors. Runtime errors happen during simulation runs and cause the simulation to stop with an error report.

Syntax (and semantic) errors

Syntactic errors indicate that a piece of code does not conform to the syntax of the programming language. Semantic errors are similar to syntactic errors; they use a correct syntax, but the syntax does not make sense. The most common error sources are:

  • Missing or incorrect brackets,
  • use of statements that do not exist (often just a misspelling), or
  • only one input is given, if GAMA expects two.

Syntactic and semantic errors are shown with red markers at the erroneous code line. If you hover over the red marker, more information about the error will be displayed. This information can be useful, but sometimes it is not pointing directly to the source of the error. Try to implement the two following statements. Can you spot the error? Which error message do you get?

reflexi myProcess { }
if grid_value > 150.0 []

Warnings

If the model can be compiled, although there is a flaw in the code, you will get a warning. Probably, you have encountered the according yellow ‘warning’ markers before. Often these are caused, when parsing an incorrect data type to a variable. For example, if you declare an integer variable and parse a float value:

    int myVariable <- 10.0;

Warnings can be ignored - at least in the draft versions of your code. The code will work despite the warning. It is good practice to avoid them, but this is nothing to spend your nights on.

Runtime errors

Runtime errors cannot be found by the compiler beforehand, they appear during a simulation run. Thus, no red markers appear in the code. A typical example would be a division by a variable that at some point in the simulation takes the value of zero. Runtime errors stop the simulation run immediately and display and error message in the experiment view of GAMA. To learn about where in the code the error originated, you can open the drop down context of the error message.

Run-time error message in GAMA that tells you that you tried to divide by zero.

Figure 13.9: Run-time error message in GAMA that tells you that you tried to divide by zero.

This error is common with random number generators: rnd (10) returns a random value between 0 and 9 (not between 1 and 10 as you may expect). For how long do you expect a model with the following code to run?

int myRandomVariable <- rnd(10);
float myResult <- 100 / myRandomVariable;

Misunderstanding of statements

Domain-specific computer languages like GAMA are written for domain experts. They try to use statements that are as simple and intuitive as possible. However, unlike in human language we still have to deal with a computer language. This means, that no fuzziness or inaccuracy is possible.

For example, you want to find all agents that are at the distance of 20 units from you and type in the following.

myAgents at_distance 20;

What GAMA returns is not the set of agents at the distance = 20, but instead all agents that are exactly at within the distance of 20.

To avoid such misunderstandings, carefully check the statement in the GAMA documentation: http://gama-platform.org/. Use the search field at the top right to find the command you are interested in. This website is always open alongside to GAMA, when I code a model.

The GAMA documentation is indispensable to understand, what a command does.

Figure 13.10: The GAMA documentation is indispensable to understand, what a command does.

Logical errors

Logical errors are the most tricky ones. A logical error is, when the program logic does not match your conceptual model. These are difficult to find, and sometimes you do not even realise that they are there! So, to have a fully verified model, you have to test all model parts and the model as a whole for plausibility for common cases, but also extreme parameter settings.

In case your model produces non-plausible results, this is what you can do:

  1. use common sense

Narrow down the problem: What is strange about your model? What happens? When do the strange things happen? Which code part can be responsible for the behaviour? Think like a compiler, go through the model line by line and try to understand what exactly is computed during one simulation step.

  1. Toggle comments

To effectively narrow down an error, you may want to delete a part of the code that you suspect to cause the problem. However, you would loose a lot of work. So it is better to just tell the computer, that it should ignore it. So, you want to comment it out. Remember: this is done with the double-slash at the beginning of the code line //. To do this for an entire code block, select the code and use the shortcut CTRL and 7 to toggle the code on and off.

Of course you can do this only, if these are not essential parts of a model. However, for example a where clause of an if statement can easily be commented out. The same can be true for a reflex statement block.

Report the state of the model to the console

Reporting the model state is actually good practice in programming. Write the current value of a variable to the console. This slows down the model, but performance is not important in the testing phase. It’s quite common that I have 5 or 10 write statements distributed in my code, while I am testing.

The following statement will write the location and the corresponding value of all cells to the console. Try it out and check the result in the Console!

  write "Grid value of " + grid_x + " " + grid_y + ": " + grid_value;

Hint: use write statements within if blocks to test, whether and when it is executed.

Visualise the state of the model to a map or chart

For an explorative testing phase, visualisation can be more effective than writing values of variables.

  • Make use of the map output to visualise the state of agents and cells. Use different shapes for different types of agents, use size to visualise values, shade the cells, according to the grid_value. Change colours, in case of a certain action or if statement is executed.
  • Make use of the inspect functionality in the map output: right mouse click on the cell or agent you want to explore in more detail -> inspect. The agent will be highlighted and all attribute values displayed.
  • Make use of monitors and the chart output to see the state and trend of variables. All these visualisations are explorative and part of testing. They are not included in the final code, but are indispensable for verification.

Use the interactive console

There are two types of consoles in GAMA: the ‘regular’ console and the ‘interactive console’. The first reports whatever your model writes to the console. The latter allows you to interact with the current state of your model in a simulation.

If you want to experiment with the interactive console, open the toy model “Life.gaml” and check the code: the cellular automaton in this model is called “life_cell” and it has a variable called new_state. Now, start the experiment and type into the interactive console:

  ask life_cell {write new_state;}

Switch to the regular console and check the results. Now go one simulation step forward, switch back to the interactive console (delete everything with the crossed out A) and type in again the above statement. Check the results.

What happens, if you type in the following?

  ask life_cell {color <- #green;}

The interactive console is extremely valuable, when you want to test whether a piece of code acts like you want, or when you want to interactively explore the state of your model.

You are now well-equipped to identify and eliminate errors. Good luck!

Part III: GAMA resource collection

This is a collection of resources to support your model programming tasks throughout this course. Especially the statement documentation will be your permanent companion for coding.

The GAMA website http://gama-platform.org

GitHub Wiki: https://github.com/gama-platform/gama/wiki

  • Errors in your model? Code Verification helps: Debugging

Part IV: Good question.. !

1 GAMA CRS?

Question: It worked for me to get the new shapefiles and clip them to 2km x 2km. But when I try to integrate them into my code, I do not have a square as the shape of my simulation, but rather a rectangle. Now I tried different ways with defining the CRS or the extent as numbers, but that did not work. A little hint would be very helpful!

Answer: The problem is that you use WGS84 geographic coordinates. These are spherical coordinates (unit: angular degree) that are not suited to calculate metric distance functions (clip a 2 by 2km) polygon, neither to be plotted on a flat 2D surface (load and use in GAMA). To represent angular degree of geographic coordinates on a flat screen, you need to project them. If you don’t project the data, a GIS either plots the degree as if they were meters. This results in a rectangular world, in which the north pole point is represented as a line of the length of the equator - and fully distorted shapes in between. See UNIGIS module 1.. ;)

Comment: believe it or not: this GIS-novice error is probably the most common error in this module. Pleeeaaase take care not to do the same!

2 GAMA’s coordinate reference system - revisited

Question: Why is the simulation of my model displayed in a GAMA-internal CRS when all the imported shapefiles are in the DHDN GK3 projected CRS?

Answer: GAMA works indeed with an internal, image reference system that has the coordinates [0,0] as its origin. This is why you have to obligatory assign the bounding box of your spatial data to the built-in global shape geometry variable. This casts the geographic coordinates of your spatial data to GAMA’s reference frame. However, you can nevertheless properly work with spatial data. If you import shapefiles with a respective .prj file, GAMA will be able to extract and interpret the CRS correctly and even transform the data to another CRS. If you write a shapefile, the given CRS will be used, but if you want to query the location, you will get the internal GAMA coordinates. To query the actual GIS coordinates, you need to use the command “to_GAMA_CRS”. Consider this example for an agent location:

write location;
geometry GIS_location <- to_GAMA_CRS({location.x,location.y}, "EPSG:31467");
write GIS_location;

The write location command returns the GAMA coordinates, while the write GIS_location command returns the coordinates in DHDN GK3. Further CRS settings of GAMA can be defined in the main menu under Views - Preferences - External.

3 The use of brackets, curly brackets and parentheses

Question: when do I use braces {}, brackets [], or parentheses ()?

Answer:

  • blocks are held together with braces {}. e.g. the global section, the section of one species or an if block.
  • indices of the position in a list are indicated with brackets [], e.g. cell[1,5]
  • logical entities are held together with parentheses (), for example:
count (each.grid_value = 0.0) 

here GAMA counts “each grid_value that equals 0”. If you state the same like this:

count each.grid_value = 0.0

GAMA would count each grid_value and would not know what to do with “= 0”

  • parameters are held in parantheses (), e.g. draw circle(300)

4 header: true does not work

Question: My expectation was that when the header is set to true, save will write a header to my file in the first cycle, when it is created, and later will just append lines. However, there is no header in the saved .csv file.

Answer: There are variables that have a predefined structure in GAMA. For example, if you save a species, it will always save the cycle number / the name / x / y / z / the attributes of the species. GAMA knows this structure and thus can automatically writes the header, if header is set true. However, for self-defined variables, GAMA is pretty stupid. No matter, whether you set the header true or false, it does not know the structure and thus does not write a header.

5 Performance issues

Question: My model slows down over time and then crashes – how can I boost the performance?

Answer: There can be many reasons, why your model performs badly. On the hardware side, this mainly depends on the RAM of your computer (to work reasonably it should be 8GB RAM or more). With regards to the model it will slow down with the number of agents it has to handle, and even more so with the number of interactions between agents. The more complex the model, the slower it is. However, if you encounter this problem in one of the assignments it is most likely a coding issue, e.g. if you have an operation that only needs to be executed once and implement it in the species or grid section: each agent or cell will repeat the same operation. If you have 100 by 100 cells in your grid, this will slow down the execution by the factor 10,000.

However, there are some tips and tricks to make your model speed up:

  1. To spot performance problems in the code, make use of the statement machine_time and let it write to the console. Declare a global variable t of type float. In the global init procedure assign the machine time to it:
t <- machine_time;

Then throughout the code, in each section write the time difference to the console and update t:

write "species procedure xxx: " + (machine_time - t);
t <- machine_time;
  1. In the main menu select Views -> Preferences -> Display. The item “Keep the data displayed in charts in memory (to save them later as .csv)” is set to true per default. This can be a helpful feature, but it makes the memory fill up quickly. If you need the data you can instead save it to a file (using the save command in the code). The writing process will take some time, but your memory does not get stuffed.

  2. If the two above points do not solve the problem, you change the memory (Java heap space) that is allocated to GAMA by editing the Gama.ini configuration file. However, this is not needed in the context of this module.