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.

random walk example 2


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 = (ExchangedTurtles2obj
          .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 = (FilteredTurtlesfilterOp.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);
      }
    }
  }