In this example, we present step by step how to explore a NetLogo model.
The Fire model is a
common NetLogo example. It studies the percolation of a fire in a forest depending on the density of the forest.
We would like to study the impact of the
density factor for a fixed population size. To do this, let's
build a design of experiment where the
density factor ranges from 20% to 80% by steps of 10.
Since the Fire model is stochastic, we are interested in doing replications for each instance of the
density factor. Results for each replication will be stored it in a
CSV file.
In this example case, we will perform 10 replications per step (even though it is a way too small sample to draw up
any formal conclusion).
You can get the NetLogo implementation of the model
here.
We first need to define 2 OpenMOLE variables in order to repeat our experience 10 times for every step of the
density exploration. These 2 variables are:
- an integer (Int) representing the seed of the random number generator for exploring the replications,
- and a Double to set the value of density,
val density = Val[Double]
val seed = Val[Int]
val burned = Val[Double]
Given these variables, the definition of the exploration in OpenMOLE writes as follows:
val exploration =
ExplorationTask(
(density in (20.0 to 80.0 by 10.0)) x
(seed in (UniformDistribution[Int]() take 10))
)
This design of experiment will generate 70 distinct sets of input values for the NetLogo model:
- 10 replications with 10 different seeds for density = 20%
- 10 replications with 10 different seeds for density = 30%
- ...
- 10 replications with 10 different seeds for density = 80%
We now need to compose this design of experiment in a complete workflow in order to run the 70 distinct
experiments.
Let's first construct the main task of the workflow. This task runs the actual NetLogo code. OpenMOLE provides a
NetLogoTask that expects the following parameters definitions:
- the path where to find the NetLogo model, i.e. the nlogo source file,
- the list of NetLogo commands that OpenMOLE needs to run
In this example, the commands list contains:
- random-seed that initialises the random number generator of NetLogo using the seed provided by OpenMOLE,
- setup that calls the setup function of the netlogo file to initialise the model,
- go, a function that runs the model, for this particular model this function is called until no more turttle are active.
The function
go-openMOLE takes three parameters:
val cmds = List(
"random-seed ${seed}",
"setup",
"while [any? turtles] [go]")
val fireTask =
NetLogo6Task("/path/to/the/Fire.nlogo", cmds) set (
inputs += seed,
outputs += (density, seed),
netLogoInputs += (density, "density"),
netLogoOutputs += ("burned-trees", burned)
)
The
replication and the
density OpenMOLE variables are used as parameters of the NetLogo program.
Therefore they appear as inputs of the NetLogoTask.
Similarly, 1 output of the model is considered and collected by OpenMOLE at the end of each model execution.
It is noted as
netLogoOutputs in the task definition.
OpenMOLE usually delegates the tasks execution to many different computers. To gather the results of these remote
executions, we use a mechanism called
hooks. Hooks can be assimilated to a listener that saves or display
results. They are more thoroughly described in a
specific section of the documentation.
Here we will create a hook to listen to the model executions and save the results in a CSV file at the end of
each of the 70 runs.
val csvHook = AppendToCSVFileHook("result.csv", density, burned, seed)
Now that we have defined each component, we can compose the workflow that brings all the pieces of the simulation
together:
val density = Val[Double]
val seed = Val[Int]
val burned = Val[Double]
val exploration =
ExplorationTask(
(density in (20.0 to 80.0 by 10.0)) x
(seed in (UniformDistribution[Int]() take 10))
)
val cmds = List(
"random-seed ${seed}",
"setup",
"while [any? turtles] [go]")
val fireTask =
NetLogo6Task("/path/to/the/Fire.nlogo", cmds) set (
inputs += seed,
outputs += (density, seed),
netLogoInputs += (density, "density"),
netLogoOutputs += ("burned-trees", burned)
)
val csvHook = AppendToCSVFileHook("result.csv", density, burned, seed)
exploration -< (fireTask hook csvHook)
The progress of the simulation can be monitored by printing the state of the execution variable:
print(ex)
.
At the end of the execution, you will find the output values in a file called
result.csv.
The Netlogo code mixes graphical and pure core instructions.
Sometimes, you have to modify slightly your code to render it purely headless so that it can be run everywhere.
The way to proceed in explained in details in the
Headless Netlogo section.