package AntPheromones1; // A simple model of bugs in a 2D world. // See the Readme.txt file for details. import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Formatter; import java.awt.Point; import java.io.*; import uchicago.src.sim.engine.Schedule; import uchicago.src.sim.engine.SimModelImpl; import uchicago.src.sim.util.*; import uchicago.src.sim.space.Object2DTorus; import uchicago.src.sim.space.Diffuse2D; public class AntPheromones1 extends ModelParameters { // instance variables public int numBugs = 10; public ArrayList bugList = new ArrayList (); public Schedule schedule; public int sizeX = 20, sizeY = 20; // integer size of the world public Object2DTorus world; // 2D class from Repast public Diffuse2D pSpace; // a 2d space from RePast public double diffusionK, evapRate; // for the Diffuse2D public static int maxPher; // max value, so we can map to colors public int pSourceX, pSourceY; // exogenous source of pheromone public double exogRate; // exog source rate, frac of maxPher // aggregate measure public double avgDistanceFromSource; ///////////////////////////////////////////////////////////////////////////// // addModelSpecificParameters // add alias and long name for Model parameters you want to set at run time // the long name should be same as instance variable // // Note: the generic parameters from ModelParameters are already available. public void addModelSpecificParameters () { parametersMap.put( "X", "sizeX" ); parametersMap.put( "Y", "sizeY" ); parametersMap.put( "nB", "numBugs" ); parametersMap.put( "eR", "evapRate" ); parametersMap.put( "dK", "diffusionK" ); parametersMap.put( "exogR", "exogRate" ); } // control what appears in the repast parameter panel public String[] getInitParam () { String[] params = { "numBugs", "sizeX", "sizeY", // these are from the super class: "rDebug", "evapRate", "diffusionK", "exogRate" }; return params; } //////////////////////////////////////////////////////////////////////////// // constructor, if needed. public AntPheromones1 () { //System.out.printf( "==> AntPheromones1 constructor...\n" ); } /////////////////////////////////////////////////////////////////////////// // setup // set defaults after a run start or restart public void setup () { if ( rDebug > 0 ) System.out.printf( "==> setup...\n" ); schedule = null; System.gc (); numBugs = 10; bugList = new ArrayList (); sizeX = 21; sizeY = 21; diffusionK = 0.90; evapRate = 1.00; maxPher = 32000; exogRate = 0.30; super.setup(); // THIS SHOULD BE CALLED after setting defaults in setup(). schedule = new Schedule (1); // create AFTER calling super.setup() if ( rDebug > 0 ) System.out.printf( "\n<=== AntPheromones1-setup() done.\n" ); } /////////////////////////////////////////////////////////////////////////// // buildModel // We build the "conceptual" parts of the model. // (vs the display parts, and the schedule) // // Create a 2D world, tell the Bugs about it. // Create bugs, put them in the world, one per cell, // and also add them to the bugList. public void buildModel () { if ( rDebug > 0 ) System.out.printf( "==> buildModel...\n" ); // CALL FIRST -- defined in super class -- it starts RNG, etc buildModelStart(); // set up the location of exogenous source of pheromone pSourceX = sizeX/2; pSourceY = sizeY/2; // tell the Bugs about "this" Bug.setModel( this ); // create the 2D world, tell the Bug class about it. world = new Object2DTorus( sizeX, sizeY ); Bug.setBugsWorld( world ); // create the 2D diffusion space for pheromones, tell bugs about it pSpace = new Diffuse2D( diffusionK, evapRate, sizeX, sizeY ); Bug.setPSpace( pSpace ); // create bugs, place them at random in the 2D world. // note only one bug per cell! for ( int i = 0; i < numBugs; i++ ) { Bug aBug = new Bug(); bugList.add( aBug ); // lets find a random place that is un-occupied Point location = placeObjectRandomlyInWorld ( (Object) aBug ); aBug.setX( (int) location.getX() ); aBug.setY( (int) location.getY() ); if ( rDebug > 0 ) System.out.printf( " - created bug ID=%d placed at x,y=%d,%d.\n", aBug.getID(), aBug.getX(), aBug.getY() ); } // lets start the world with some pheromone... // more than gets added each step...but not more than maxPher! double exogPheromone = 2.0 * maxPher * exogRate; double initPher = Math.min( exogPheromone, (double) maxPher ); pSpace.putValueAt( pSourceX, pSourceY, initPher ); pSpace.update(); // move from write copy to read copy if ( rDebug > 0 ) System.out.printf( "- buildModel: put initPher=%.3f at %d,%d.\n", pSpace.getValueAt(pSourceX,pSourceY), pSourceX, pSourceY ); // some post-load finishing touches startReportFile(); // you probably don't want to remove any of the following // calls to process parameter changes and write the // initial state to the report file. // NB -> you might remove/add more agentChange processing applyAnyStoredChanges(); stepReport(); getReportFile().flush(); getPlaintextReportFile().flush(); if ( rDebug > 0 ) System.out.printf( "<== buildModel done.\n" ); } // placeObjectRandomlyInWorld // pick a random, unoccupied place and put the object there. // returns a Point with the x,y values used. // NOTE: this assumes it will find a place! public Point placeObjectRandomlyInWorld ( Object obj ) { int randomX, randomY; // lets find a random place that is un-occupied do { randomX = getUniformIntFromTo( 0, world.getSizeX () - 1 ); randomY = getUniformIntFromTo( 0, world.getSizeY () - 1 ); } while ( world.getObjectAt( randomX, randomY ) != null ); world.putObjectAt( randomX, randomY, obj ); // return a Point that tells where we were placed. Point pt = new Point ( randomX, randomY ); return pt; } /////////////////////////////////////////////////////////////////////////// // step // The top of the "conceptual" model's main dynamics // - we diffuse the pheronome // - bugs step (move a bit) // - we pump in some exogenously supplied pheromone // - we update the pSpace (put the written values into the read lattice) // - stepReport to write stats to the report file. public void step () { if ( rDebug > 0 ) System.out.printf( "==> AntPheromones1 step %.0f:\n", getTickCount() ); // diffuse() diffuses from the read matrix (T) and into write (T') // *and* it then does an update(), i.e., writes T' into new read T+1 pSpace.diffuse(); // now the bugs get a chance to move around for ( int i = 0; i < bugList.size(); i++ ) { Bug aBug = bugList.get (i); aBug.step (); } // add the exogenous supply. be sure not to go over maxPher // otherwise the color doesn't work right. // *** Note: This assumes the bugs have not altered the PSpace // without updating the pspace read copy!! // (this is adding to the amount it reads from the Read matrix) double v = (maxPher * exogRate) + pSpace.getValueAt( pSourceX, pSourceY ); v = Math.min( v, maxPher ); pSpace.putValueAt( pSourceX, pSourceY, v ); // now update the pSpace -- move values from the write to read copy pSpace.update(); // print things to the report file. stepReport(); if ( rDebug > 0 ) { System.out.printf( " -> Measured pheromone at %d,%d is %.3f.\n", pSourceX, pSourceY, pSpace.getValueAt( pSourceX, pSourceY ) ); System.out.printf( "<== AntPheromones1 step done.\n" ); } } ///////////////////////////////////////////////////////////////////////////////// // stepReport // each step write out: // time avgD // where avgD = average bug distance from exogenous pheromone source // // Note: update the writeHeaderCommentsToReportFile() to print // lines of text describing the data written to the report file. public void stepReport () { if ( rDebug > 0 ) System.out.printf( " => call stepReport() t=%.0f.\n", getTickCount() ); // calculate these numbers, store in instance variables calculateAvgDistanceFromSource(); // set up a string with the values to write String s = String.format( "%5.0f", getTickCount() ); s += String.format(" %6.3f", avgDistanceFromSource ); // write it to the xml and plain text report files writeLineToReportFile ( "" + s + "" ); writeLineToPlaintextReportFile( s ); // flush the buffers so the data is not lost in a "crash" getReportFile().flush(); getPlaintextReportFile().flush(); } ///////////////////////////////////////////////////////////////////////////////// // calculateAvgDistanceFromSource // calc average of bug distances to the exogenous source of pherome public void calculateAvgDistanceFromSource () { avgDistanceFromSource = 0.0; double bugX, bugY, deltaX, deltaY, distance; for ( Bug bug : bugList ) { bugX = (double) bug.getX(); bugY = (double) bug.getY(); deltaX = bugX - pSourceX; deltaY = bugY - pSourceY; distance = Math.sqrt( (deltaX*deltaX) + (deltaY*deltaY) ); avgDistanceFromSource += distance; } int bugListSize = bugList.size(); if ( bugListSize > 1 ) { avgDistanceFromSource /= bugListSize; } } // printPSpaceValues // for debugging public void printPSpaceValues () { for ( int x = 0; x < sizeX; ++x ) { for ( int y = 0; y < sizeY; ++y ) { double v = pSpace.getValueAt( x, y ); if ( v > 0 ) System.out.println( "x,y=v " + x + "," + y + "=" + v ); } } } // writeHeaderCommentsToReportFile // customize to match what you are writing to the report files in stepReport. public void writeHeaderCommentsToReportFile () { writeLineToReportFile( "" ); writeLineToReportFile( " Avg " ); writeLineToReportFile( " time D " ); writeLineToReportFile( "" ); writeLineToPlaintextReportFile( "# Avg " ); writeLineToPlaintextReportFile( "# time D " ); } ////////////////////////////////////////////////////////////////////////////////// // printProjectHelp // this could be filled in with some help to get from running with -help parameter public void printProjectHelp() { // print project help System.out.printf( "\n%s -- \n", getName() ); System.out.printf( "\n **** Add more info here!! **** \n" ); System.out.printf( "\n" ); printParametersMap(); System.exit( 0 ); } public Schedule getSchedule () { return schedule; } public String getName () { return "AntPheromones1"; } // setters and getters // notes: // - we use the schedule != null to indicated model has been initialized // - some things can't be changed after model initialization // (which things just depends on how the model is implemented) // - if we set something after model initialization, // we need to write an change entry to the report file. // - some things need to send messages to update class variables. // // NOTE: if you want changes a user makes to parameter like numBugs // to be used after a restart (vs going back to defaults), // you probably have to change setup() to not reinitialize IVs. public int getNumBugs () { return numBugs; } public void setNumBugs (int numBugs) { this.numBugs = numBugs; if ( schedule != null ) { System.err.printf("\nCan't change numBugs mid-run.\n"); System.err.printf("\nChange will not take effect until re-init.\n"); } } public int getSizeX () { return sizeX; } public void setSizeX (int sizeX) { this.sizeX = sizeX; if ( schedule != null ) { System.err.printf("\nCan't change sizeX mid-run.\n"); System.err.printf( "\nChange will not take effect until re-init.\n" ); } } public int getSizeY () { return sizeY; } public void setSizeY (int sizeY) { this.sizeY = sizeY; if ( schedule != null ) { System.err.printf("\nCan't change sizeY mid-run.\n"); System.err.printf( "\nChange will not take effect until re-init.\n" ); } } // The following can be changed in mid-run: public double getDiffusionK () { return diffusionK; } public void setDiffusionK ( double diffusionK ) { this.diffusionK = diffusionK; if ( pSpace != null ) pSpace.setDiffusionConstant( diffusionK ); } public double getEvapRate () { return evapRate; } public void setEvapRate ( double evapRate ) { this.evapRate = evapRate; if ( pSpace != null ) pSpace.setEvaporationRate( evapRate ); } public double getExogRate () { return exogRate; } public void setExogRate ( double exogRate ) { this.exogRate = exogRate; } ///////////////////////////////////////////////////////////////////////////// // processEndOfRun // called once, at end of run. // writes some final info, closes report files, etc. public void processEndOfRun ( ) { if ( rDebug > 0 ) System.out.printf("\n\n===== processEndOfRun =====\n\n" ); applyAnyStoredChanges(); endReportFile(); this.fireStopSim(); } }