-
Notifications
You must be signed in to change notification settings - Fork 13
Models
Pedro R. Andrade, Raian V. Maretto
This tutorial works with TerraME version 2.0.0-RC1 or greater.
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:
- Simulate the same model with different parameters, for example to create scenarios or to calibrate the model.
- 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.
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.
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 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"}
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, with1
as default. -
par2
has three strings as options, with"medium"
as default. -
par3
requires a number greater than or equal to0
, with0
as default. -
par4
requires a number lower than10
, with7
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}
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 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"
}
}
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.
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.
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
}
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)
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.
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.
If you have comments, doubts or suggestions related to this document, please write a feedback to pedro.andrade <at> inpe.br.
Back to wiki or terrame.org.