package firesimulator.simulator; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.Map; import java.util.HashMap; import firesimulator.util.Configuration; import firesimulator.util.Rnd; import firesimulator.world.Building; import firesimulator.world.FireBrigade; import firesimulator.world.World; import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.Log; import org.uncommons.maths.random.GaussianGenerator; public class Simulator { private static final Log LOG = LogFactory.getLog(Simulator.class); private World world; private WindShift windShift; public static float GAMMA=0.5f; public static float AIR_TO_AIR_COEFFICIENT=0.5f; public static float AIR_TO_BUILDING_COEFFICIENT=45f; public static float WATER_COEFFICIENT=0.5f; public static float ENERGY_LOSS=0.9f; public static float WIND_DIRECTION=0.9f; public static float WIND_RANDOM=0f; public static int WIND_SPEED=0; public static float RADIATION_COEFFICENT=1.0f; public static float TIME_STEP_LENGTH=1f; public static float WEIGHT_GRID = 0.2f; public static float AIR_CELL_HEAT_CAPACITY = 1f; public Set monitors; public static boolean verbose; private static Simulator me; private EnergyHistory energyHistory; public Simulator(World world){ me = this; monitors=new HashSet(); verbose = true; // this.kernel=kernel; this.world=world; // kernel.register(this); windShift=null; } public static Simulator getSimulator(){ return me; } public void addMonitor(Monitor monitor){ monitors.add(monitor); } public void removeMonitor(Monitor monitor){ monitors.remove(monitor); } private void informStep(){ for(Iterator i = monitors.iterator();i.hasNext();){ ((Monitor)i.next()).step(world); } } private void informDone(){ for(Iterator i = monitors.iterator();i.hasNext();){ ((Monitor)i.next()).done(world); } } private void informReset(){ for(Iterator i = monitors.iterator();i.hasNext();){ ((Monitor)i.next()).reset(world); } } public void step(int timestep){ energyHistory = new EnergyHistory(world, timestep); refill(); executeExtinguishRequests(); burn(); cool(); updateGrid(); exchangeBuilding(); //FIXED cool(); energyHistory.registerFinalEnergy(world); energyHistory.logSummary(); } private void cool(){ for(Iterator i = world.getBuildings().iterator();i.hasNext();){ Building b = (Building) i.next(); waterCooling(b); } } private void refill(){ for(Iterator i = world.getFirebrigades().iterator();i.hasNext();){ FireBrigade fb = ((FireBrigade)i.next()); if(fb.refill()){ LOG.debug("refilling fire brigade "+fb.getID()); } } } private void executeExtinguishRequests() { for(Iterator i=world.getExtinguishIterator();i.hasNext();){ ExtinguishRequest er=(ExtinguishRequest)i.next(); er.execute(); } world.clearExtinguishRequests(); } private void burn() { for (Building b : world.getBuildings()) { if (b.getTemperature() >= b.getIgnitionPoint() && b.fuel > 0 && b.isInflameable()) { float consumed = b.getConsum(); if(consumed > b.fuel) { consumed = b.fuel; } double oldFuel = b.fuel; double oldEnergy = b.getEnergy(); double oldTemp = b.getTemperature(); b.setEnergy(b.getEnergy() + consumed); energyHistory.registerBurn(b, consumed); b.fuel -= consumed; b.setPrevBurned(consumed); /* LOG.debug("Building " + b.getID() + " burned " + consumed + " fuel."); LOG.debug("Old fuel: " + oldFuel + ", old energy: " + oldEnergy + ", old temperature: " + oldTemp); LOG.debug("New fuel: " + b.fuel + ", new energy: " + b.getEnergy() + ", new temperature: " + b.getTemperature()); */ } else { b.setPrevBurned(0f); } } } private void waterCooling(Building b) { double lWATER_COEFFICIENT=(b.getFieryness()>0&&b.getFieryness()<4?WATER_COEFFICIENT:WATER_COEFFICIENT*GAMMA); boolean cond = false; if(b.getWaterQuantity()>0){ double oldEnergy = b.getEnergy(); double oldTemp = b.getTemperature(); double oldWater = b.getWaterQuantity(); double dE=b.getTemperature() * b.getCapacity(); if (dE <= 0) { // LOG.debug("Building already at or below ambient temperature"); return; } double effect=b.getWaterQuantity()*lWATER_COEFFICIENT; int consumed=b.getWaterQuantity(); if(effect>dE){ cond = true; double pc=1-((effect-dE)/effect); effect*=pc; consumed*=pc; } b.setWaterQuantity(b.getWaterQuantity()-consumed); b.setEnergy(b.getEnergy()-effect); energyHistory.registerCool(b, effect); LOG.debug("Building " + b.getID() + " water cooling"); LOG.debug("Old energy: " + oldEnergy + ", old temperature: " + oldTemp + ", old water: " + oldWater); LOG.debug("Consumed " + consumed + " water: effect = " + effect); LOG.debug("New energy: " + b.getEnergy() + ", new temperature: " + b.getTemperature() + ", new water: " + b.getWaterQuantity()); } } private void exchangeBuilding() { for(Iterator i=world.getBuildings().iterator();i.hasNext();){ Building b=(Building)i.next(); exchangeWithAir(b); } double sumdt=0; Map<Building, Double> radiation = new HashMap<Building, Double>(); for(Iterator i=world.getBuildings().iterator();i.hasNext();){ Building b=(Building)i.next(); double radEn=b.getRadiationEnergy(); radiation.put(b, radEn); } for(Iterator i=world.getBuildings().iterator();i.hasNext();){ Building b=(Building)i.next(); double radEn=radiation.get(b); Building[] bs=b.connectedBuilding; float[] vs=b.connectedValues; /* LOG.debug("Building " + b.getID() + " radiating energy"); LOG.debug("Total energy: " + b.getEnergy()); LOG.debug("Radiated energy: " + radEn); LOG.debug("Old temperature: " + b.getTemperature()); */ for(int c=0;c<vs.length;c++){ double oldEnergy=bs[c].getEnergy(); double connectionValue=vs[c]; double a=radEn*connectionValue; double sum=oldEnergy+a; bs[c].setEnergy(sum); energyHistory.registerRadiationGain(bs[c], a); /* LOG.debug("Building " + bs[c].getID() + " connection value: " + connectionValue); LOG.debug("Building " + bs[c].getID() + " received " + a); */ } b.setEnergy(b.getEnergy()-radEn); energyHistory.registerRadiationLoss(b, -radEn); /* LOG.debug("New temperature: " + b.getTemperature()); */ } } private void exchangeWithAir(Building b) { // Give/take heat to/from air cells double oldTemperature = b.getTemperature(); double oldEnergy = b.getEnergy(); double energyDelta = 0; /* if (b.getID() == 16204 || b.getID() == 23545) { LOG.debug("Building " + b.getID() + " heat exchange"); LOG.debug("Building energy: " + oldEnergy); LOG.debug("Building temperature: " + oldTemperature); LOG.debug("AIR_TO_BUILDING_COEFFICIENT: " + AIR_TO_BUILDING_COEFFICIENT); LOG.debug("AIR_CELL_HEAT_CAPACITY: " + AIR_CELL_HEAT_CAPACITY); LOG.debug("TIME_STEP_LENGTH: " + TIME_STEP_LENGTH); LOG.debug("CELL_SIZE: " + world.SAMPLE_SIZE); } */ for (int[] nextCell : b.cells) { int cellX = nextCell[0]; int cellY = nextCell[1]; double cellCover = nextCell[2] / 100.0; double cellTemp = world.getAirCellTemp(cellX, cellY); double dT = cellTemp - b.getTemperature(); double energyTransferToBuilding = dT * AIR_TO_BUILDING_COEFFICIENT * TIME_STEP_LENGTH * cellCover * world.SAMPLE_SIZE; energyDelta += energyTransferToBuilding; double newCellTemp = cellTemp - energyTransferToBuilding / (AIR_CELL_HEAT_CAPACITY * world.SAMPLE_SIZE); world.setAirCellTemp(cellX, cellY, newCellTemp); /* if (b.getID() == 16204 || b.getID() == 23545) { LOG.debug("Cell " + cellX + ", " + cellY); LOG.debug("Area covered: " + cellCover); LOG.debug("Cell temperature: " + cellTemp); LOG.debug("dT: " + dT); LOG.debug("Energy transfer to building: " + energyTransferToBuilding); LOG.debug("New cell temperature: " + newCellTemp); } */ } b.setEnergy(oldEnergy + energyDelta); energyHistory.registerAir(b, energyDelta); /* if (b.getID() == 16204 || b.getID() == 23545) { LOG.debug("New energy: " + b.getEnergy()); LOG.debug("New temperature: " + b.getTemperature()); } */ } /* private double averageTemp(Building b) { double total=0; double tempSum=0; // LOG.debug("Finding average cell temperature for building " + b.getID()); for(int i=0;i<b.cells.length;i++){ double pc=((double)b.cells[i][2])/100d; total+=pc; tempSum+=getTempAt(b.cells[i][0],b.cells[i][1])*pc; } return tempSum/total; } */ private void updateGrid() { LOG.debug("Updating air grid"); double[][] airtemp=world.getAirTemp(); double[][] newairtemp = new double[airtemp.length][airtemp[0].length]; for(int x=0;x<airtemp.length;x++){ for(int y=0;y<airtemp[0].length;y++){ double dt = (averageTemp(x,y)-airtemp[x][y]); double change = (dt * AIR_TO_AIR_COEFFICIENT * TIME_STEP_LENGTH); newairtemp[x][y] = relTemp(airtemp[x][y] + change); // if (newairtemp[x][y] > 0.000001 || airtemp[x][y] > 0.000001) { // LOG.debug("Cell " + x + ", " + y + " old temperature: " + airtemp[x][y] + ", dt: " + dt + ", change: " + change + ", new temp: " + newairtemp[x][y]); // } if(!(newairtemp[x][y]>-Double.MAX_VALUE&&newairtemp[x][y]<Double.MAX_VALUE)){ LOG.warn("Value is not sensible: " + newairtemp[x][y]); newairtemp[x][y]=Double.MAX_VALUE*0.75; } if(newairtemp[x][y] == Double.NEGATIVE_INFINITY || newairtemp[x][y] == Double.POSITIVE_INFINITY) { LOG.warn("aha"); } } } world.setAirTemp(newairtemp); world.setAirTemp(getWindShift().shift(world.getAirTemp(),this)); } private double relTemp(double deltaT){ return Math.max(0, deltaT*ENERGY_LOSS*TIME_STEP_LENGTH); } private double averageTemp(int x, int y) { // double rv = (neighbourCellAverage(x,y)+buildingAverage(x,y))/(weightSummBuilding(x,y)+weightSummCells(x,y)); double rv = neighbourCellAverage(x, y) / weightSummCells(x, y); return rv; } /* private float buildingAverage(int x, int y) { float total=0; for(Iterator i=world.gridToBuilding[x][y].iterator();i.hasNext();){ Object o[]=(Object[])i.next(); total+=((Building)o[0]).getTemperature()*((Float)o[1]).floatValue(); } if (Double.isNaN(total)) { LOG.warn("buildingAverage(" + x + ", " + y + ") returned NaN"); total = 0; for(Iterator i=world.gridToBuilding[x][y].iterator();i.hasNext();){ Object o[]=(Object[])i.next(); Building b = (Building)o[0]; float f = ((Float)o[1]).floatValue(); LOG.debug("Building " + b.getID() + " temperature: " + b.getTemperature()); LOG.debug("Building " + b.getID() + " coverage : " + f); total+=((Building)o[0]).getTemperature()*((Float)o[1]).floatValue(); LOG.debug("New total: " + total); } LOG.debug("Done"); } return total; } private float weightSummBuilding(int x,int y){ float total=0; for(Iterator i=world.gridToBuilding[x][y].iterator();i.hasNext();){ Object o[]=(Object[])i.next(); total+=((Float)o[1]).floatValue(); } return total; } */ private double neighbourCellAverage(int x, int y) { double total=getTempAt(x+1,y-1); total+=getTempAt(x+1,y); total+=getTempAt(x+1,y+1); total+=getTempAt(x,y-1); total+=getTempAt(x,y+1); total+=getTempAt(x-1,y-1); total+=getTempAt(x-1,y); total+=getTempAt(x-1,y+1); return total*WEIGHT_GRID; } private float weightSummCells(int x,int y){ return 8 * WEIGHT_GRID; } protected double getTempAt(int x,int y){ if(x<0||y<0||x>=world.getAirTemp().length||y>=world.getAirTemp()[0].length) return 0; return world.getAirTemp()[x][y]; } public void setWind(float direction,float speed){ windShift=new WindShift(direction,(float)speed,world.SAMPLE_SIZE); } public void setWindSpeed(float speed){ windShift=getWindShift(); setWind(windShift.getDirection(),speed); } public void setWindDirection(float direction){ windShift=getWindShift(); setWind(direction,windShift.speed); } public WindShift getWindShift(){ if(WIND_RANDOM>0&&windShift!=null){ float nd=(float) (windShift.direction+windShift.direction*WIND_RANDOM*Rnd.get01()); float ns=(float) (windShift.speed+windShift.speed*WIND_RANDOM*Rnd.get01()); setWind(nd,ns); } if (windShift==null||windShift.direction!=WIND_DIRECTION||windShift.speed!=WIND_SPEED) setWind(WIND_DIRECTION,WIND_SPEED); return windShift; } private void loadVars(){ AIR_TO_BUILDING_COEFFICIENT = new Float(Configuration.getValue("resq-fire.air_to_building_flow")).floatValue(); AIR_TO_AIR_COEFFICIENT =new Float(Configuration.getValue("resq-fire.air_to_air_flow")).floatValue(); ENERGY_LOSS = new Float(Configuration.getValue("resq-fire.energy_loss")).floatValue(); WATER_COEFFICIENT= new Float(Configuration.getValue("resq-fire.water_thermal_capacity")).floatValue(); WIND_SPEED = new Integer(Configuration.getValue("resq-fire.wind_speed")).intValue(); WIND_DIRECTION = new Float(Configuration.getValue("resq-fire.wind_direction")).floatValue(); WIND_RANDOM = new Float(Configuration.getValue("resq-fire.wind_random")).floatValue(); RADIATION_COEFFICENT=new Float(Configuration.getValue("resq-fire.radiation_coefficient")).floatValue(); AIR_CELL_HEAT_CAPACITY=new Float(Configuration.getValue("resq-fire.air_cell_heat_capacity")).floatValue(); ExtinguishRequest.MAX_WATER_PER_CYCLE=new Integer(Configuration.getValue("resq-fire.max_extinguish_power_sum")).intValue(); ExtinguishRequest.MAX_DISTANCE=new Integer(Configuration.getValue("resq-fire.water_distance")).intValue(); GAMMA=new Float(Configuration.getValue("resq-fire.gamma")).floatValue(); Rnd.setSeed(new Long(Configuration.getValue("resq-fire.randomseed")).longValue()); java.util.Random random = new java.util.Random(new Long(Configuration.getValue("resq-fire.randomseed")).longValue()); Building.burnRate = new GaussianGenerator(new Double(Configuration.getValue("resq-fire.burn-rate-average")).doubleValue(), new Double(Configuration.getValue("resq-fire.burn-rate-variance")).doubleValue(), random); } public void initialize(){ try{ loadVars(); }catch (Exception e) { LOG.fatal("invalid configuration, aborting", e); System.exit(-1); } world.initialize(); } public void reset(){ loadVars(); world.reset(); informReset(); } }