123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- import sys
- sys.path.append('../../src/')
- from infinity import *
- from DEVS import AtomicDEVS, CoupledDEVS
- from math import exp
- T_AMBIENT = 27.0
- T_IGNITE = 300.0
- T_GENERATE = 500.0
- T_BURNED = 60.0
- TIMESTEP = 0.01
- PH_INACTIVE = 'inactive'
- PH_UNBURNED = 'unburned'
- PH_BURNING = 'burning'
- PH_BURNED = 'burned'
- RADIUS = 3
- TMP_DIFF = 1.0
- #########################################
- ## Map layout
- #########################################
- ## x_max = 6, y_max = 6
- #########################################
- ## 0 1 2 3 4 5 6
- ## 0
- ## 1
- ## 2
- ## 3
- ## 4
- ## 5
- ## 6
- #########################################
- #########################################
- def getPhaseFor(temp, phase):
- if temp > T_IGNITE or (temp > T_BURNED and phase == PH_BURNING):
- return PH_BURNING
- elif temp < T_BURNED and phase == PH_BURNING:
- return PH_BURNED
- else:
- return PH_UNBURNED
- class CellState(object):
- # Simply here for future necessity
- def __init__(self, temp):
- self.temperature = temp
- self.igniteTime = float('inf')
- self.currentTime = 0.0
- self.phase = PH_INACTIVE
- self.surroundingTemps = [T_AMBIENT] * 4
- self.oldTemperature = temp
- def __str__(self):
- return "%s (T: %f)" % (self.phase, self.temperature)
- def copy(self):
- a = CellState(self.temperature)
- a.igniteTime = self.igniteTime
- a.currentTime = self.currentTime
- a.phase = self.phase
- a.surroundingTemps = list(self.surroundingTemps)
- a.oldTemperature = self.oldTemperature
- return a
- def __eq__(self, other):
- return self.temperature == other.temperature and self.igniteTime == other.igniteTime and self.currentTime == other.currentTime and self.phase == other.phase and self.surroundingTemps == other.surroundingTemps and self.oldTemperature == other.oldTemperature
- def toCellState(self):
- return self.temperature
- class Cell(AtomicDEVS):
- def __init__(self, x, y, x_max, y_max):
- AtomicDEVS.__init__(self, "Cell(%d, %d)" % (x, y))
-
- self.state = CellState(T_AMBIENT)
- # For Cell DEVS tracing
- self.x = x
- self.y = y
- self.inports = [self.addInPort("in_N"), self.addInPort("in_E"), self.addInPort("in_S"), self.addInPort("in_W"), self.addInPort("in_G")]
- self.outport = self.addOutPort("out_T")
- self.taMap = {PH_INACTIVE: INFINITY, PH_UNBURNED: 1.0, PH_BURNING: 1.0, PH_BURNED: INFINITY}
- def preActivityCalculation(self):
- return None
- def postActivityCalculation(self, _):
- return self.state.temperature - T_AMBIENT
- def intTransition(self):
- #for _ in range(4100):
- # pass
- # First check for the surrounding cells and whether we are on a border or not
- self.state.currentTime += self.timeAdvance()
- # OK, now we have a complete list
- if abs(self.state.temperature - self.state.oldTemperature) > TMP_DIFF:
- self.state.oldTemperature = self.state.temperature
- if self.state.phase == PH_BURNED:
- # Don't do anything as we are already finished
- return self.state
- elif self.state.phase == PH_BURNING:
- newTemp = 0.98689 * self.state.temperature + 0.0031 * (sum(self.state.surroundingTemps)) + 2.74 * exp(-0.19 * (self.state.currentTime * TIMESTEP - self.state.igniteTime)) + 0.213
- elif self.state.phase == PH_UNBURNED:
- newTemp = 0.98689 * self.state.temperature + 0.0031 * (sum(self.state.surroundingTemps)) + 0.213
- newPhase = getPhaseFor(newTemp, self.state.phase)
- if newPhase == PH_BURNED:
- newTemp = T_AMBIENT
- if self.state.phase == PH_UNBURNED and newPhase == PH_BURNING:
- self.state.igniteTime = self.state.currentTime * TIMESTEP
- self.state.phase = newPhase
- self.state.temperature = newTemp
- return self.state
- def extTransition(self, inputs):
- # NOTE we can make the assumption that ALL temperatures are received simultaneously, due to Parallel DEVS being used
- self.state.currentTime += self.elapsed
- if self.inports[-1] in inputs:
- # A temperature from the generator, so simply set our own temperature
- self.state.temperature = inputs[self.inports[-1]][0]
- self.state.phase = getPhaseFor(self.state.temperature, self.state.phase)
- if self.state.phase == PH_BURNING:
- self.state.igniteTime = self.state.currentTime * TIMESTEP
- else:
- for num, inport in enumerate(self.inports[:4]):
- self.state.surroundingTemps[num] = inputs.get(inport, [self.state.surroundingTemps[num]])[0]
- if self.state.phase == PH_INACTIVE:
- self.state.phase = PH_UNBURNED
- return self.state
- def outputFnc(self):
- if abs(self.state.temperature - self.state.oldTemperature) > TMP_DIFF:
- return {self.outport: [self.state.temperature]}
- else:
- return {}
-
- def timeAdvance(self):
- return self.taMap[self.state.phase]
- class Junk(object):
- def __init__(self):
- self.status = True
- def __str__(self):
- return "Generator"
- def copy(self):
- a = Junk()
- a.status = self.status
- return a
- class Generator(AtomicDEVS):
- def __init__(self, levels):
- AtomicDEVS.__init__(self, "Generator")
- self.outports = []
- for i in range(levels):
- self.outports.append(self.addOutPort("out_" + str(i)))
- self.state = Junk()
- def intTransition(self):
- self.state.status = False
- return self.state
- def outputFnc(self):
- output = {}
- for i in range(len(self.outports)):
- output[self.outports[i]] = [T_AMBIENT + T_GENERATE/(2**i)]
- return output
- def timeAdvance(self):
- if self.state.status:
- return 1.0
- else:
- return INFINITY
- def preActivityCalculation(self):
- return None
- def postActivityCalculation(self, _):
- return 0.0
- class FireSpread(CoupledDEVS):
- def putGenerator(self, x, y):
- CENTER = (x, y)
- for level in range(RADIUS):
- # Left side
- y = level
- for x in range(-level, level + 1, 1):
- self.connectPorts(self.generator.outports[level], self.cells[CENTER[0] + x][CENTER[1] + y].inports[-1])
- self.connectPorts(self.generator.outports[level], self.cells[CENTER[0] + x][CENTER[1] - y].inports[-1])
- x = level
- for y in range(-level + 1, level, 1):
- self.connectPorts(self.generator.outports[level], self.cells[CENTER[0] + x][CENTER[1] + y].inports[-1])
- self.connectPorts(self.generator.outports[level], self.cells[CENTER[0] - x][CENTER[1] + y].inports[-1])
-
- def __init__(self, x_max, y_max):
- CoupledDEVS.__init__(self, "FireSpread")
- self.cells = []
- try:
- from mpi4py import MPI
- nodes = MPI.COMM_WORLD.Get_size()
- except ImportError:
- nodes = 1
- node = 0
- totalCount = x_max * y_max
- counter = 0
- for x in range(x_max):
- row = []
- for y in range(y_max):
- if nodes == 1:
- node = 0
- elif x <= x_max/2 and y < y_max/2:
- node = 0
- elif x <= x_max/2 and y > y_max/2:
- node = 1
- elif x > x_max/2 and y < y_max/2:
- node = 2
- elif x > x_max/2 and y > y_max/2:
- node = 3
- row.append(self.addSubModel(Cell(x, y, x_max, y_max), node))
- self.cells.append(row)
- counter += 1
- # Everything is now constructed, so connect the ports now
- self.generator = self.addSubModel(Generator(RADIUS))
- #self.putGenerator(2, 2)
- #self.putGenerator(2, y_max-3)
- #self.putGenerator(x_max-3, 2)
- #self.putGenerator(x_max-3, y_max-3)
- self.putGenerator(5, 5)
- for x in range(x_max):
- for y in range(y_max):
- if x != 0:
- self.connectPorts(self.cells[x][y].outport, self.cells[x-1][y].inports[2])
- if y != y_max - 1:
- self.connectPorts(self.cells[x][y].outport, self.cells[x][y+1].inports[1])
- if x != x_max - 1:
- self.connectPorts(self.cells[x][y].outport, self.cells[x+1][y].inports[3])
- if y != 0:
- self.connectPorts(self.cells[x][y].outport, self.cells[x][y-1].inports[0])
|