How to create an interface (a model-artefact) for an existing
model implemented in its own simulator
Example with a single Netlogo random walk model
The model used is a simple random
walk where, for each simulation step,
agents (so-called walkers) choose a random direction and step forward.
The Netlogo model specification is available
here.
The exchanged data will be here the walkers positions (x,y) the same
for input and output. In this simplistic case, the model exchange
theses
data with itself (i.e. its input port correspond to its output port).
Since not very useful for simulation results, this
example allows to introduce the very first concepts and their
implementation.
In this tutorial, we present the implementation of a model-artefact
(which role is to allow the model-agent to interact with the simulator).
Implementation : model-artefact
In order to create a model-artefact,
you need to extend the
Interface
GenericModelArtefact.
Then, you need to fill in the methods (be sure that your simulator
implements these functions).
init() |
Initialize the model |
run() |
Execute the model (one simulation event, one simulation
step, for a given simulation time interval...) |
getOutputData() |
Return output data from the model output ports |
setInputData() |
Set input data to the model input ports |
getCurrentTime()
|
Return the current simulation time |
getNextTime() |
Return the next simulation time |
stop() |
Manage the end of the simulation process (optional) |
Functions
init()
The init() function is used to create
a simulator instance, to load the model and to set it up with initial
parameters. In this examples, this function creates a Netlogo
application instance, loads the randomWalk models and call the "setup"
command. Once done, we update the simulation time values. In our case
the current simulation time ct=0 and, as we execute the model
step-by-step, nt=1.
@Override
public void init() {
// create a simulator instance
createSimulatorInstance();
try {
// set up the model
// the case of the RandomWalk model, create the agents with an
// initial position (0,0)
App.app.command("setup");
// set patches color
String cmd = "ask patches [ if (pxcor > " + (int) environmentWidth / 2
+ ") or (pxcor < " + (int) -environmentWidth / 2
+ ") or (pycor > " + (int) environmentHeight / 2
+ ") or (pycor < " + (int) -environmentHeight / 2
+ ") [ set pcolor green ] ]";
App.app.command(cmd);
cmd = "ask patches [ if (pxcor - pycor = 0) or (pxcor + pycor = 0) [set pcolor white]]";
App.app.command(cmd);
// set up current and next simulation time
currentTime = new NetlogoTime();
nextTime = new NetlogoTime();
currentTime.setTimeFromModel(App.app.report("ticks"));
nextTime.setTimeFromModel(currentTime.getTimeValue() + 1);
// note : we can also call the updateSimulationTime() function.
} catch (CompilerException e) {
e.printStackTrace();
}
}
@Override
protected void createSimulatorInstance() {
// create a new Netlogo instance
String[] arg = new String[0];
App.main(arg);
// open the model file
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
try {
App.app
.open("Netlogo_models/My Models/Random Walk.nlogo");
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
} catch (InterruptedException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
run()
The run() function is used to execute
the model. In our example, the execution policy is step-by-step, so
this function only calls the Netlogo command "go".
@Override
public void run() {
try {
// run the go procedure (here our policy is to run only one-by-one
App.app.command("go");
} catch (CompilerException e) {
e.printStackTrace();
}
this.updateSimulationTime();
}
getCurrentTime(),
getNextTime() = updateSimulationTime()
We get the current simulation time
from the Netlogo model by calling
the report("tick") method, as our simulation is processed step-by-step,
the getNextTime() function equals to getCurrentTime() + 1. As we need
both values after each run() function call, we created the
updateSimulationTime() function.
@Override
protected void updateSimulationTime() {
try {
currentTime.setTimeFromModel(App.app.report("ticks"));
//getCurrentTime() function
} catch (CompilerException e) {
e.printStackTrace();
}
nextTime.setTimeFromModel(currentTime.getTimeValue() + 1);
//getNextTime() function
}
getOutputData()
This function returns the output data (here, the set of walker
positions). Here we report all the walkers, get their IDs, positions
and return everything in a list of SimulData objects.
@Override
public ArrayList<SimulData> getOutputData() {
ArrayList<SimulData> outputData = new ArrayList<SimulData>();
outputData.add(getWalkersPositionsFromModel());
return outputData;
}
/**
* This function returns the output data from the model
* @return the list of walkers positions from Netlogo
*/
private SimulData getWalkersPositionsFromModel() {
// the walkers positions
HashMap<Double, Point2D.Double> myPositionsMap = new HashMap<Double, Point2D.Double>();
// the list of agents (the walkers)
AgentSet listOfWalkers;
try {
// get the list of walkers from Netlogo
listOfWalkers = (AgentSet) App.app.report("turtles");
// check if the agent set is not empty
if (!listOfWalkers.isEmpty()) {
// for each walkers get the position and set the
// WalkersPosition object
for (int i = 0; i < listOfWalkers.count(); i++) {
try {
// get the agent
Agent agt = listOfWalkers.agent(i);
// get the agent id, the position values
Double agtID = (Double) agt.getVariable(0);
Double xPos = (Double) agt.getVariable(3);
Double yPos = (Double) agt.getVariable(4);
// create a Point2D object
Point2D.Double agtPos = new Point2D.Double(xPos, yPos);
// put them in to the map
myPositionsMap.put(agtID, agtPos);
} catch (NullPointerException e) {
}
} // end for
} else {
System.err.println("all walkers are dead");
// System.exit(1300);
}
} catch (CompilerException e) {
e.printStackTrace();
}
return new TurtlePositions(myPositionsMap);
}
setInputData()
This function sets the input data to
the model (here, the positions).
@Override
public void setInputData(SimulData data) {
// check if the input data sent by the agent is of type TurtlePosition
if ((TurtlePositions.class).equals(data.getClass())) {
TurtlePositions pos = (TurtlePositions) data;
setTurtlePositions(pos);
} else {
// if not (since there is no other input data the model can read)
// exit.
System.err
.println("trying to send a non turtle position data to the random walk model model");
System.exit(666);
}
}
/**
* Set the turtle positions to Netlogo
*
* @param pos
* A TurtlePositions to set into the model
*/
private void setTurtlePositions(TurtlePositions pos) {
HashMap<Double, Point2D.Double> positionList = pos.getTurtlePositions();
// set the sheep positions
for (Double turtleID : positionList.keySet()) {
// get the sheep position
Point2D.Double aPosition = positionList.get(turtleID);
// Netlogo command
String cmd = "ask turtle " + turtleID + " [ setxy "
+ aPosition.getX() + " " + aPosition.getY() + " ]";
try {
App.app.command(cmd);
} catch (CompilerException e) {
e.printStackTrace();
}
}
}