Skip to content
Pedro R. Andrade edited this page Jul 18, 2017 · 57 revisions

Models and Instances of Models in TerraME

Pedro R. Andrade, Raian V. Maretto

This tutorial works with TerraME version 2.0.0-RC1 or greater.

Summary

Introduction

In TerraME, Model is a top-level type useful to encapsulate the parameters of a model and how it can be created and executed. There are two main usages for this type:

  1. Simulate the same model with different parameters, for example to create scenarios or to calibrate the model.
  2. Build an automatic graphical interface to configure arguments visually, without having to write any source code.

An example of Model is shown below:

Tube = Model{
    initialWater = 100,
    flow = 10,
    checkZero = false,
    finalTime = 10,
    init = function(model) ... end
}

type(Tube) -- "Model"

When we execute the command above, we create a Model called Tube. It has four arguments (initialWater, flow, and finalTime, numbers, and checkZero, boolean), and describes how an instance of model will be checked and initialized. Using Tube, we can create model instances using Tube and simulate the model instance using execute:

instance1 = Tube{} -- do not change any attribute value

type(instance1) -- "Tube"

instance1:execute()

The object instance1 contains all the attribute values defined in the definition of Tube. It is also posible to select values for its parameters, such as:

instance2 = Tube{
    initialWater = 200,
    checkZero = true
}

This tutorial describes how to create models and how they can be used.

Arguments

Each model has a set of arguments. This section describe how to describe the arguments of a model in order to allow the user to create instances of the model.

Summary

number and boolean

Number and boolean are the most basic arguments for a model. Whenever a model contains a variable number or boolean it means that the model has such argument with the respective default values. Additionally, any instance of the model that does not use the default values will need to have the same type of the default value. For example, the model below (MyModel) has two arguments, par1 and par2. The fist one is a number and has 3 as default value, while the second is a boolean, with true as default value (It is a convention that a Model should start with a capital letter to differentiate it from common functions).

MyModel = Model{
    par1 = 3,
    par2 = true,
    -- ...
}

Some ways to build an instance of MyModel are shown below. The first three instances are valid, but the last one has an error that will stop the simulation because par2 should be boolean. (When developing packages, usually models and models instances are in separated files).

m1 = MyModel{}
m2 = MyModel{par1 = 5}
m3 = MyModel{par1 = 0, par2 = false}
m4 = MyModel{par1 = 2, par2 = 7}

string

String works just like number and boolean. They define the type and the default value. Additionally, if the string is in the format "*.a;*.b;...", it describes a set of file extensions. The modeler then has to use a filename as argument with one of the extensions defined by this string. In this case, the argument becomes mandatory. For example, using the model below:

MyModel = Model{
    par1 = "data",
    par2 = "*.csv",
    par3 = "*.txt;*.dat",
    -- ...
}

We can create instances like:

m1 = MyModel{par2 = "data.csv", par3 = "file.txt"} 
m2 = MyModel{par1 = "mydata", par2 = "data.csv", par3 = "file.txt"}

However, the instances below are not valid. The first one does not use mandatory arguments par2 and par3, while the second one uses a wrong file extension for par3.

m1 = MyModel{}
m2 = MyModel{par2 = "data.csv", par3 = "file.csv"}

Choice

The type Choice allows the modeler to define a set of possible values for a given argument. It can be described as a vector of elements with the same type (number or string), or as a named table defining min and/or max (both are optional, but at least one must be used), step (optional, but requires min and max), and default (optional). The default default value is min if it exists, otherwise max if it exists, otherwise the first element of the vector. The value of this argument in the model instance should belong to the same type of the elements of the Choice. For instance, in the code below:

MyModel = Model{
    par1 = Choice{1, 2, 3, 4, 5},
    par2 = Choice{"low", "medium", "high", default = "medium"},
    par3 = Choice{min = 0},
    par4 = Choice{max = 10, default = 7},
    par5 = Choice{min = 1, max = 9, step = 2}
}

we have the following possibilities for the parameters:

  • par1 has five numbers as options, with 1 as default.
  • par2 has three strings as options, with "medium" as default.
  • par3 requires a number greater than or equal to 0, with 0 as default.
  • par4 requires a number lower than 10, with 7 as default.
  • par5 has five numbers as options (1, 3, 5, 7, 9).

Some examples of creating instances of MyModel are shown below. The first three instances are valid. However, m4 and m5 have errors. The first one has an invalid value for par1 (6), while the second one has a wrong type for par2 (number).

m1 = MyModel{}
m2 = MyModel{par1 = 2, par4 = 5}
m3 = MyModel{par1 = 3, par2 = "high", par3 = 10}
m4 = MyModel{par1 = 6, par2 = "high"}
m5 = MyModel{par1 = 2, par2 = 7}

Mandatory

Type Mandatory defines a parameter that belongs to a given type but does not have a default value, and must be described by the modeler in the model instance. For example:

MyModel = Model{
    par1 = Mandatory("number"),
    par2 = Mandatory("function"),
    par3 = Mandatory("CellularSpace")
}

Named tables

Named tables group a set of arguments that have a similar context in the model. They are useful when the model has many arguments. As an example, one can put arguments relative to properties of a given Society inside of a named table, and properties relative to a given CellularSpace in another. The elements inside of a named table follow the same rules defined above (but they cannot be a named table). For instance:

MyModel = Model{
    par1 = {
        para = 3,
        parb = Choice{"low", "medium", "high"},
        parc = true
    }
    -- ...
}

In this case, if the modeler creates an instance of MyModel without any elements, para will be 3, parb will be "low", and parc will be true. One can then define instances such as:

m1 = MyModel{
    par1 = {
        para = 5,
        parb = "medium"
    }
}

finalTime

Every model instance needs to have a numeric attribute finalTime. This attribute must be created inside init() or be a parameter of the Model. When the Model has an argument finalTime, it means that it has a default final time for the simulation.

random

In the same way of finalTime, random has a semantic when it is a parameter of a Model. This parameter is useful when the model works with random numbers, but it is optional. When the Model has an argument random = true, it means to TerraME that two simulations of this model with the same parameters can produce different outcomes. This argument is not available to be configured after the Model is implemented. It is useful when one wants to configure the Model through the graphical interface (see below). Whenever this argument is true, the code created by the automatic graphical interface sets a seed in order to guarantee that, if the user simulates the Model again using the saved script, it will produce the same results again.

init()

Models have only one compulsory function: init(). It takes as single argument the model instance itself, with all arguments set by the user. It might verifiy whether the model instance has the correct arguments, checking if the values follow some rules that cannot be determined by the rules of the arguments as stated above. For example, it can check whether a given numeric argument is greater than another argument. Then it can create the model instance given its parameters. All the created objects need to belong to the model instance. In the end, the model instance should have a Timer or an Environment with at least one Timer to be able to run the simulation. The returning value of this function (if any) will be ignored. An example is shown below:

Tube = Model{
    initialWater = 200,
    flow = 20,
    checkZero = false,
    init = function(model)
        verify(model.flow < model.initialWater, "Flow should be less than initial water.")
        model.cell = TubeCell(model)
        model.timer = TubeTimer(model)
        model.chart = TubeChart(model)
    end
}

getParameters()

It is possible to access the arguments of a Model directly in Lua by using getParameters(). For example:

Tube = Model{
    initialWater = 200,
    flow = 20,
    checkZero = false,
    init = function(model)
        -- ...
    end
}

forEachElement(Tube:getParameters(), function(idx, _, mtype)
    print(idx, mtype)
end)

Multiple simulations

It is possible to run multiple simulations of a given Model using type MultipleRuns from package calibration. An example is shown below:

import("calibration")
import("MyModel")

r = MultipleRuns{
    model = MyModel,
    parameters = {water = 10, rain = 20},
    quantity = 10,
    finalTime = 10,
    population = function(model)
      return #model.society
    end,
    output = function(model)
        model.obscs:save(“file.png”)
    end
}

In this case, TerraME will simulate the same model 10 times. See more options in the documentation of the package.

Graphical Interface

It is possible to create a graphical interface where the modeler can fill the arguments of models visually. Each argument of the model will have an associated graphical component, according to its definition. See the table below:

Argument Graphical component
boolean Check box
number Edit box
string Edit box
file string Read only edit box and a button to select the file
Choice with vector List box
Choice with step Slider
other Choice Edit box
Mandatory number Edit box
Mandatory Model Graphical component of its element
named table Box with its elements as above

Attributes with values math.huge or -math.huge will be shown as inf and -inf in the respective box.

If Model finds an argument that cannot be added to the graphical interface, such as a CellularSpace, then it will stop TerraME with an error. If the user wants to build a graphical interface in this case, it is necessary to add the parameters of the argument that does not have a graphical interface directly to the model. For example, in the case of the CellularSpace, one can add the parameters xdim and ydim to the Model and then create the CellularSpace internally.

The names of the arguments will be used in the interface. Capital letters will be replaced by a space in the label referencing to the given argument in the graphical interface. Therefore, it is very important to define good argument names for the model. For example, given the script below to create and configure a model called Tube:

Tube = Model{
    initialWater    = Choice{min = 10, default = 200},
    finalTime       = Choice{min = 1, default = 10},
    flow            = Choice{min = 1, default = 20},
    observingStep   = Choice{min = 0.1, max = 1, step = 0.1, default = 1},
    checkZero       = false,
	init = function() end
}

Tube:configure()

We will produce the graphical interface below. The Choice values will be shown together, with the one having step in the end. After them we have the boolean value.

If a given model is published in a package, the graphical interface can displayed in TerraME graphical interface as well as in the command line using the option -configure:

terrame -package ca -configure DaisyWorld

This command will create the following graphical interface:

Each time you click on the Run button, TerraME will automatically save the configuration you choose for such simulation in a file called Daisyworld-instance.lua. Only the parameters that are different from the default values are going to be stored in this file. For example, if you select value 0.3 to empty, it will save the following content:

import("ca")

instance = DaisyWorld{
    proportions = {
        empty = 0.3
    }
}

instance:run()

It is possible to describe the order of each argument of the model using function interface(), within the description of a Model. If this function is not implemented in the Model, the components will be distributed automatically. This function should return a table with tables composed by strings. Each position of the table describes a column for the interface. The columns contain the name of the arguments of the model. When using a named table, all its elements will be placed together within a box. For example, in the code below, the graphical interface will have two columns. The first will have attributes mapFile, distFile, and a box will the attributes of block. The second one will have a box with the attributes of agents.

Two examples of models and their graphical interfaces are shown below:

Sugarscape = Model{
    mapFile        = "sugar-map.csv",
    agents = {
        quantity   = 10,
        wealth     = Choice{min = 5, max = 25},
        metabolism = Choice{min = 1, max = 4}
    },
    block = {
        xmin       = 0,
        xmax       = math.huge,
        ymin       = 0,
        ymax       = math.huge
    },
    interface = function()
        return {
            {"string", "agents"},
            {"block"}
        }
    end,
	init = function()
    	-- ...
	end
}

Sugarscape:configure()

The string value ending with ".csv" will have a button to select a csv file stored in the computer. The two tables will be shown in separated boxes, each one with its own parameters.

Note that the elements that do not belong to the table of interface() will not be shown in the graphical interface.