model.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. import sys
  2. sys.path.append('../../src/')
  3. from infinity import *
  4. from DEVS import AtomicDEVS, CoupledDEVS
  5. from math import exp
  6. T_AMBIENT = 27.0
  7. T_IGNITE = 300.0
  8. T_GENERATE = 500.0
  9. T_BURNED = 60.0
  10. TIMESTEP = 0.01
  11. PH_INACTIVE = 'inactive'
  12. PH_UNBURNED = 'unburned'
  13. PH_BURNING = 'burning'
  14. PH_BURNED = 'burned'
  15. RADIUS = 3
  16. TMP_DIFF = 1.0
  17. #########################################
  18. ## Map layout
  19. #########################################
  20. ## x_max = 6, y_max = 6
  21. #########################################
  22. ## 0 1 2 3 4 5 6
  23. ## 0
  24. ## 1
  25. ## 2
  26. ## 3
  27. ## 4
  28. ## 5
  29. ## 6
  30. #########################################
  31. #########################################
  32. def getPhaseFor(temp, phase):
  33. if temp > T_IGNITE or (temp > T_BURNED and phase == PH_BURNING):
  34. return PH_BURNING
  35. elif temp < T_BURNED and phase == PH_BURNING:
  36. return PH_BURNED
  37. else:
  38. return PH_UNBURNED
  39. class CellState(object):
  40. # Simply here for future necessity
  41. def __init__(self, temp):
  42. self.temperature = temp
  43. self.igniteTime = float('inf')
  44. self.currentTime = 0.0
  45. self.phase = PH_INACTIVE
  46. self.surroundingTemps = [T_AMBIENT] * 4
  47. self.oldTemperature = temp
  48. def __str__(self):
  49. return "%s (T: %f)" % (self.phase, self.temperature)
  50. def copy(self):
  51. a = CellState(self.temperature)
  52. a.igniteTime = self.igniteTime
  53. a.currentTime = self.currentTime
  54. a.phase = self.phase
  55. a.surroundingTemps = list(self.surroundingTemps)
  56. a.oldTemperature = self.oldTemperature
  57. return a
  58. def __eq__(self, other):
  59. 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
  60. def toCellState(self):
  61. return self.temperature
  62. class Cell(AtomicDEVS):
  63. def __init__(self, x, y, x_max, y_max):
  64. AtomicDEVS.__init__(self, "Cell(%d, %d)" % (x, y))
  65. self.state = CellState(T_AMBIENT)
  66. # For Cell DEVS tracing
  67. self.x = x
  68. self.y = y
  69. self.inports = [self.addInPort("in_N"), self.addInPort("in_E"), self.addInPort("in_S"), self.addInPort("in_W"), self.addInPort("in_G")]
  70. self.outport = self.addOutPort("out_T")
  71. self.taMap = {PH_INACTIVE: INFINITY, PH_UNBURNED: 1.0, PH_BURNING: 1.0, PH_BURNED: INFINITY}
  72. def preActivityCalculation(self):
  73. return None
  74. def postActivityCalculation(self, _):
  75. return self.state.temperature - T_AMBIENT
  76. def intTransition(self):
  77. #for _ in range(4100):
  78. # pass
  79. # First check for the surrounding cells and whether we are on a border or not
  80. self.state.currentTime += self.timeAdvance()
  81. # OK, now we have a complete list
  82. if abs(self.state.temperature - self.state.oldTemperature) > TMP_DIFF:
  83. self.state.oldTemperature = self.state.temperature
  84. if self.state.phase == PH_BURNED:
  85. # Don't do anything as we are already finished
  86. return self.state
  87. elif self.state.phase == PH_BURNING:
  88. 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
  89. elif self.state.phase == PH_UNBURNED:
  90. newTemp = 0.98689 * self.state.temperature + 0.0031 * (sum(self.state.surroundingTemps)) + 0.213
  91. newPhase = getPhaseFor(newTemp, self.state.phase)
  92. if newPhase == PH_BURNED:
  93. newTemp = T_AMBIENT
  94. if self.state.phase == PH_UNBURNED and newPhase == PH_BURNING:
  95. self.state.igniteTime = self.state.currentTime * TIMESTEP
  96. self.state.phase = newPhase
  97. self.state.temperature = newTemp
  98. return self.state
  99. def extTransition(self, inputs):
  100. # NOTE we can make the assumption that ALL temperatures are received simultaneously, due to Parallel DEVS being used
  101. self.state.currentTime += self.elapsed
  102. if self.inports[-1] in inputs:
  103. # A temperature from the generator, so simply set our own temperature
  104. self.state.temperature = inputs[self.inports[-1]][0]
  105. self.state.phase = getPhaseFor(self.state.temperature, self.state.phase)
  106. if self.state.phase == PH_BURNING:
  107. self.state.igniteTime = self.state.currentTime * TIMESTEP
  108. else:
  109. for num, inport in enumerate(self.inports[:4]):
  110. self.state.surroundingTemps[num] = inputs.get(inport, [self.state.surroundingTemps[num]])[0]
  111. if self.state.phase == PH_INACTIVE:
  112. self.state.phase = PH_UNBURNED
  113. return self.state
  114. def outputFnc(self):
  115. if abs(self.state.temperature - self.state.oldTemperature) > TMP_DIFF:
  116. return {self.outport: [self.state.temperature]}
  117. else:
  118. return {}
  119. def timeAdvance(self):
  120. return self.taMap[self.state.phase]
  121. class Junk(object):
  122. def __init__(self):
  123. self.status = True
  124. def __str__(self):
  125. return "Generator"
  126. def copy(self):
  127. a = Junk()
  128. a.status = self.status
  129. return a
  130. class Generator(AtomicDEVS):
  131. def __init__(self, levels):
  132. AtomicDEVS.__init__(self, "Generator")
  133. self.outports = []
  134. for i in range(levels):
  135. self.outports.append(self.addOutPort("out_" + str(i)))
  136. self.state = Junk()
  137. def intTransition(self):
  138. self.state.status = False
  139. return self.state
  140. def outputFnc(self):
  141. output = {}
  142. for i in range(len(self.outports)):
  143. output[self.outports[i]] = [T_AMBIENT + T_GENERATE/(2**i)]
  144. return output
  145. def timeAdvance(self):
  146. if self.state.status:
  147. return 1.0
  148. else:
  149. return INFINITY
  150. def preActivityCalculation(self):
  151. return None
  152. def postActivityCalculation(self, _):
  153. return 0.0
  154. class FireSpread(CoupledDEVS):
  155. def putGenerator(self, x, y):
  156. CENTER = (x, y)
  157. for level in range(RADIUS):
  158. # Left side
  159. y = level
  160. for x in range(-level, level + 1, 1):
  161. self.connectPorts(self.generator.outports[level], self.cells[CENTER[0] + x][CENTER[1] + y].inports[-1])
  162. self.connectPorts(self.generator.outports[level], self.cells[CENTER[0] + x][CENTER[1] - y].inports[-1])
  163. x = level
  164. for y in range(-level + 1, level, 1):
  165. self.connectPorts(self.generator.outports[level], self.cells[CENTER[0] + x][CENTER[1] + y].inports[-1])
  166. self.connectPorts(self.generator.outports[level], self.cells[CENTER[0] - x][CENTER[1] + y].inports[-1])
  167. def __init__(self, x_max, y_max):
  168. CoupledDEVS.__init__(self, "FireSpread")
  169. self.cells = []
  170. try:
  171. from mpi4py import MPI
  172. nodes = MPI.COMM_WORLD.Get_size()
  173. except ImportError:
  174. nodes = 1
  175. node = 0
  176. totalCount = x_max * y_max
  177. counter = 0
  178. for x in range(x_max):
  179. row = []
  180. for y in range(y_max):
  181. if nodes == 1:
  182. node = 0
  183. elif x <= x_max/2 and y < y_max/2:
  184. node = 0
  185. elif x <= x_max/2 and y > y_max/2:
  186. node = 1
  187. elif x > x_max/2 and y < y_max/2:
  188. node = 2
  189. elif x > x_max/2 and y > y_max/2:
  190. node = 3
  191. row.append(self.addSubModel(Cell(x, y, x_max, y_max), node))
  192. self.cells.append(row)
  193. counter += 1
  194. # Everything is now constructed, so connect the ports now
  195. self.generator = self.addSubModel(Generator(RADIUS))
  196. #self.putGenerator(2, 2)
  197. #self.putGenerator(2, y_max-3)
  198. #self.putGenerator(x_max-3, 2)
  199. #self.putGenerator(x_max-3, y_max-3)
  200. self.putGenerator(5, 5)
  201. for x in range(x_max):
  202. for y in range(y_max):
  203. if x != 0:
  204. self.connectPorts(self.cells[x][y].outport, self.cells[x-1][y].inports[2])
  205. if y != y_max - 1:
  206. self.connectPorts(self.cells[x][y].outport, self.cells[x][y+1].inports[1])
  207. if x != x_max - 1:
  208. self.connectPorts(self.cells[x][y].outport, self.cells[x+1][y].inports[3])
  209. if y != 0:
  210. self.connectPorts(self.cells[x][y].outport, self.cells[x][y-1].inports[0])