Skip to content

New Distributed Simulation

miccar edited this page Jul 1, 2016 · 23 revisions

#New Distributed Simulation In the following guide we provide a step-by-step guide to the process of simulation parallelization of a MASON simulation.

Usually a MASON simulation package is composed by three classes:

  • A class that implements the agent simulated by the application
  • A class that represents the simulation environment, it
  • A class that allows to run the simulations using a GUI

Similarly in D-MASON, the simulation package of a distributed simulation contains the following classes:

  • A class that represents the distributed agent
  • A class that represents the distributed environment
  • A class to represents the field with GUI in distributed manner

#Particle model In this simulation model is an example of 2D particles bouncing around on a grid. When particles intersect, they each choose a random direction to go (including staying put). Particles bounce off the wall and leave trails.

The package Particles is composed by three classes:

  • Particle: it implements the agent that will be simulated by the application.
  • Particles: it represents the simulation environment: it allows to run the simulation from the command line without using a GUI.
  • ParticlesWithUI: it allows to run simulations with a GUI.

##From Particle to DParticle

The original Particle has to implement the Steppable interface and, in particular, the method step(), containing the agent logic. In the same way RemoteParticle is an abstract class that has to implement RemotePositionedAgent, that is the D-MASON interface containing the necessary logic for the distributed agent. Finally DParticle extends RemoteParticle and implements the logic of the agent.

RemotePositionedAgent is parameterized with an Int2D object-type because, in this simulation, the field has this specific type to indicate locations, and allows programmers to set, for each agent, an unique identifier and a field position. A Particle simply contains two integer parameters, xdir and ydir, for setting the initial direction that the particle will move along.

...
public Particle(int xdir, int ydir) {
    public boolean randomize = false;
    this.xdir = xdir;
    this.ydir = ydir;
}
...

DParticle has two constructors: the first is empty and it is useful for object serialization, the second one has as parameter a subclass of the abstract class DistributedState.

...
public class DParticle extends RemoteParticle<Int2D> {
    public int xdir; // -1, 0, or 1
    public int ydir; // -1, 0, or 1
    public DParticle(){ }
    public DParticle(DistributedState state) {
        super(state);
    }
...

In order to distribute a MASON simulation it is necessary to change some parts of the agent logic. In the original MASON version each particle, on each step, performs a collision avoidance routine by checking whether the location it is moving to is already occupied by another particle or not.

...
public void step(SimState state) {
    ...
    if (randomize) {
        xdir = tut.random.nextInt(3) - 1;
        ydir = tut.random.nextInt(3) - 1;
        randomize = false;
    }
    ...
    // set my new location
    Int2D newloc = new Int2D(newx, newy);
    tut.particles.setObjectLocation(this, newloc);

    // randomize everyone at that location if need be
    Bag p = tut.particles.getObjectsAtLocation(newloc);
    if (p.numObjs > 1) {
        for (int x = 0; x < p.numObjs; x++)
            ((Particle)(p.objs[x])).randomize = true;
    }
}
...

The distributed version is slightly different because it first checks if the new location is occupied and, in this case, it randomizes its direction and move to the new location by using the method setDistributedObjectLocation.

...
public void step(SimState state) {
    DParticles tut = (DParticles)state;
    Int2D location = tut.particles.getObjectLocation(this);
    Bag p = tut.particles.getObjectsAtLocation(location);
    tut.trails.setDistributedObjectLocation(1.0, location,state);
    if (p.numObjs > 1) {
        xdir = tut.random.nextInt(3) - 1;
        ydir = tut.random.nextInt(3) - 1;
    }
    int newx = location.x + xdir;
    int newy = location.y + ydir;

    if (newx < 0) { newx++; xdir = -xdir; }
    else if (newx >= tut.trails.getWidth()) {newx--; xdir = -xdir; }

    if (newy < 0) { newy++ ; ydir = -ydir; }
    else if (newy >= tut.trails.getHeight()) {newy--; ydir = -ydir; }

    Int2D newloc = new Int2D(newx,newy);
    tut.particles.setDistributedObjectLocation(newloc, this, state);
...
}
...

##From Particles to DParticles

The class Particles extends the SimState class while the class DParticles extends DistributedState, parameterized with Int2D object-type.

The class DParticles contains three other variables indicating, respectively, width and height of the field and the way of partitioning the field (that can be one or two dimensional). Particles has just one constructor that has as parameter the random generator seed while DParticles constructor has as input a GeneralParams object, which wraps several parameters specific for the distributed simulation (e.g. network address, port, etc...) and a String object, which is an unique identifier for simulation messages in Apache ActiveMQ communication system.

In order to divide the agents(the particles) in the distributed field, this example splits equally the agents in in the cells of the distributed field with a division. If the division have a remainder, this "remainder agents" will assign to a cell (in this example at the cell with ID 0-0).

In Particles there are two fields, the first containing the agents, the second one containing the trails. The creation of the fields and the placement of the agents in them are carried out by a simple loop that instantiates new particles with a random position and direction and place them in the proper field.

In order to add particles to the schedule, it is possible to use scheduleRepeating(), that allows to schedule agents repeatedly, and to add particles to the field there is setObjectLocation(). In DParticles there is the method createDSparseGrid2D of the class DSparseGrid2DFactory for creating a new distributed field. Note that it is necessary to use a factory to choose the kind of field partition. The agent initial position is computed by the method setAvailableRandomLocation() and to add particles in the schedule it is necessary to use the method scheduleOnce(), because in the next step a certain agent could not stay in the same part of the field, so using scheduleRepeating() will not delete the particle from the schedule. Finally there are other three new methods: a getter method for returning the subclass of the DistributedState, a method for adding an agent with a given position in the field, a method for attaching a portrayal to an agent.

Class Particles

public class Particles extends SimState {
    public DoubleGrid2D trails;
    public SparseGrid2D particles;
    ...
    public Particles(long seed) {
        super(seed);
    }
    public void start() {
        ...
        for(int i=0 ; i<numParticles ; i++) {
            p = new Particle(random.nextInt(3) - 1, random.nextInt(3) - 1); // random direction
            schedule.scheduleRepeating(p);
            ...
            particles.setObjectLocation(p,new Int2D(x,y)); // random location
        }
    }
    public static void main(String[] args) {
        doLoop(Particles.class, args);
        System.exit(0);
    }
}

Class DParticles

public class DParticles extends DistributedState<Int2D> {
     protected DSparseGrid2D particles;
    protected DDoubleGrid2D trails;
    protected SparseGridPortrayal2D p;

    public int gridWidth ;
    public int gridHeight ;   
    public int MODE;
  
    private String topicPrefix = "";

    public DParticles(){ super();}
    
    public DParticles(GeneralParam params, String prefix)
    {    	
    	super(params,new DistributedMultiSchedule<Int2D>(),prefix,params.getConnectionType());
    	this.MODE=params.getMode();
    	this.topicPrefix=prefix;
    	gridWidth=params.getWidth();
    	gridHeight=params.getHeight();
    } 
    
    
    @Override
    public void start(){
     super.start();

     try{
        	
        particles = DSparseGrid2DFactory.createDSparseGrid2D(gridWidth, gridHeight,this,
                    super.AOI,TYPE.pos_i,TYPE.pos_j,super.rows,super.columns,MODE,
                    "particles", topicPrefix,false);
        trails = DDoubleGrid2DFactory.createDDoubleGrid2D(gridWidth,gridHeight,this,
                    super.AOI,TYPE.pos_i,TYPE.pos_j,super.rows,super.columns,MODE,
                    0,false,"trails",topicPrefix,false);
			
		    init_connection();
		  
     }catch (DMasonException e) { e.printStackTrace();}
      
     DParticle p=new DParticle(this);
     
     //split the particles in distributed fields
     //the number of agents for each field is totalAgents/(rows*columns)
     //the remainder of division will assign to cell 0-0  
    
     int agentsToCreate=0;
     int remainder=super.NUMAGENTS%super.NUMPEERS; 

     if(remainder==0){  
	agentsToCreate= super.NUMAGENTS / super.NUMPEERS;
     }

     else if(remainder!=0 && TYPE.pos_i==0 && TYPE.pos_j==0){ 
       agentsToCreate= (super.NUMAGENTS / super.NUMPEERS)+remainder;
     }
	
     else{
       agentsToCreate= super.NUMAGENTS / super.NUMPEERS;
     }

		
     while(particles.size() != agentsToCreate)		
       
        p.setPos(particles.getAvailableRandomLocation());
        p.xdir = random.nextInt(3)-1;
        p.ydir = random.nextInt(3)-1;

     if(particles.setObjectLocation(p, new Int2D(p.pos.getX(),p.pos.getY())))
     {		
        schedule.scheduleOnce(schedule.getTime()+1.0,p);
           		
          if(particles.size() != super.NUMAGENTS) p=new DParticle(this);
        }
        }
             
        // Schedule the decreaser
        Steppable decreaser = new Steppable()
        {
            @Override
	    public void step(SimState state) { trails.multiply(0.9); }
            static final long serialVersionUID = 6330208160095250478L;
        };
        schedule.scheduleRepeating(Schedule.EPOCH,2,decreaser,1);
    }

    public static void main(String[] args)
    {
        doLoop(DParticles.class, args);
        System.exit(0);
    }    

    static final long serialVersionUID = 9115981605874680023L;
	
	@Override
	public DistributedField2D getField(){ return particles; }

	@Override
	public SimState getState(){ return this; }

	@Override
	public void addToField(RemotePositionedAgent<Int2D> rm,Int2D loc){		
		particles.setObjectLocation(rm, loc);
	}
	public boolean setPortrayalForObject(Object o){
		if(p!=null){
	            p.setPortrayalForObject(o, new sim.portrayal.simple.OvalPortrayal2D(Color.YELLOW) );
		    return true;
		  }
		return false;
	}    
}

##From ParticlesWithUI to DParticlesWithUI

There are few differences between original ParticlesWithUI and the its distributed version, DParticlesWithUI. They both extend the class GUIState, responsible of instantiating all graph- ics elements; DParticlesWithUI has a constructor for passing to DParticles the objects array and it has to store in a String the region identifier, in order to show which region it is simulating (e.g. 0-0 means the upper-left part of the grid partitioned field). Class ParticlesWithUI

public class ParticlesWithUI extends GUIState {
    ...
    public static void main(String[] args) {
    ParticlesWithUI t = new ParticlesWithUI();
        t.createController();
    }
    public ParticlesWithUI() {
        super(new Tutorial3(System.currentTimeMillis()));
    }
    public ParticlesWithUI(SimState state) {
        super(state);
    }
    ...
}

Class DParticlesWithUI

public class DParticlesWithUI extends GUIState {
    ...
    public static String name;
    ...
    public DParticlesWithUI(Object[] args) {
        super(new DParticles(args));
        name = String.valueOf(args[7]) + "" + (String.valueOf(args[8]));
    }
    public static String getName() {
        return "Peer: <"+name+">";
    }
    ...
}