How to create the model-agents to manage the simulation
Example with two Netlogo random walk models
The models used are a simple random
walk where, for each simulation step,
agents (so-called walkers) choose a random direction and step forward.
In our example, the walker can go from one model to another. That is,
we define one boundary in each model, when a walker cross that
boundary, it is sent to the other model.
The Netlogo models specifications available
here and
here.
The exchanged data will be here the walkers positions (x,y) and the
walker ID. The output port of one model is linked to the input of
the
other.
Model-agent implementation
Once again, the process to create a the model-agents is similar to the
previous one.
The difference lies in the fact that we have to build two model-agents,
one for each model. The operation we need in order to decide if a
walker as cross the boundary is describe
here.
The role of the model agent is to manage the simulation process (i.e. to
initiate the model, to run the model, to post and to read output and input data). In
order to implement a model-agent, you need to extends the
GenericAgent
abstract class.
Model-agent behaviour
The model-agent behaviour is quite
simple. After initializing the
model, the model-agent need to execute the model. That is, it reads
input data from the coupling-artefact, runs the model and posts the
output data to the coupling-artefact. In the following example, we
decided to process the simulation for only 2000 steps. We also use an
operation in order to filter the positions we post. That is, when a
walker position is out of the boundary of the model, we remove it from the model and send it through the coupling-artefact.
@Override
public void executeModel() {
// check if the operation has been instantiated
if (filterOp == null) {
initOperation_FilterOp();
}
int c = 0;
while (c < 2000) {
c++;
// read input data
this.readInputData();
// run a simulation step
modelArtefact.run();
// post output data
this.postOutputData();
}
}
Read data from the coupling-artefact and set them to the
model-artefact
In order to read external data, the
model-agent has to check the
coupling-artefact output ports it is linked with. In our example, we
check that there exist a coupling-artefact output port that accept
ExchangedTurtles objects and read from this output port the data. In
order to ensure the coordination process, the read() method
automatically provides the data corresponding to our local simulation
time (see LinkCoord).
@Override
public void readInputData() {
for (OutputPort<? extends SimulData> outputPort : couplingArtefactOutputPortList) {
try {
// check if the output port is the one in charge of receiving
// the sheep positions objects
if (outputPort.getClassO().equals(ExchangedTurtles2.class)) {
// ask it to get the turtles positions for the current
// simulation time
ObjectMessage obj = outputPort.read(modelArtefact
.getCurrentTime());
ExchangedTurtles2 inputData = (ExchangedTurtles2) obj
.getObject();
// set the input data to the model : i.e. the external turtles to add
modelArtefact.setInputData(inputData);
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
Get output data from the model-artefact and post them to the
coupling-artefact
Before posting the data, we filter
them. We only send the positions and the IDs of the walkers that cross
the boundary. Then we remove them from the model.
@Override
public void postOutputData() {
ArrayList<SimulData> outputdata = modelArtefact.getOutputData();
for (SimulData data : outputdata) {
if(data.getClass().equals(ExchangedTurtles1.class)){
/*
* Filter the turtles
*/
FilteredTurtles fT = (FilteredTurtles) filterOp.apply(data);
/*
* remove local turtles
*/
((RandomWalkModelArtefact1)modelArtefact).removeWalkers(fT.getFilteredTurtleList());
/*
* post turtles to exchange
*/
post(new ExchangedTurtles1(fT.getFilteredTurtleList(), 1000));
} else {
System.err.println("trying to post data with the wrong type of class");
System.exit(666);
}
}
}