devstone.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. from abc import ABC, abstractmethod
  2. from collections import defaultdict
  3. from pystone import pystones
  4. from pypdevs.DEVS import AtomicDEVS, CoupledDEVS
  5. from pypdevs.infinity import INFINITY
  6. from pypdevs.simulator import Simulator
  7. class DelayedAtomic(AtomicDEVS):
  8. def __init__(self, name: str, int_delay: float, ext_delay: float, add_out_port: bool = False, prep_time=0):
  9. super().__init__(name)
  10. self.int_delay = int_delay
  11. self.ext_delay = ext_delay
  12. self.prep_time = prep_time
  13. self.i_in = self.addInPort("i_in")
  14. if add_out_port:
  15. self.o_out = self.addOutPort("o_out")
  16. def intTransition(self):
  17. if self.int_delay:
  18. pystones(self.int_delay)
  19. return "passive"
  20. def timeAdvance(self):
  21. if self.state == "active":
  22. return self.prep_time
  23. else:
  24. return INFINITY
  25. def outputFnc(self):
  26. if hasattr(self, "o_out"):
  27. return {self.o_out: [0]}
  28. return {}
  29. def extTransition(self, inputs):
  30. if self.ext_delay:
  31. pystones(self.ext_delay)
  32. return "active"
  33. class DEVStoneWrapper(CoupledDEVS, ABC):
  34. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float,
  35. add_atomic_out_ports: bool = False):
  36. super().__init__(name)
  37. self.depth = depth
  38. self.width = width
  39. self.int_delay = int_delay
  40. self.ext_delay = ext_delay
  41. self.i_in = self.addInPort("i_in")
  42. self.o_out = self.addOutPort("o_out")
  43. if depth < 1:
  44. raise ValueError("Invalid depth")
  45. if width < 1:
  46. raise ValueError("Invalid width")
  47. if int_delay < 0:
  48. raise ValueError("Invalid int_delay")
  49. if ext_delay < 0:
  50. raise ValueError("Invalid ext_delay")
  51. if depth == 1:
  52. atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True)
  53. self.addSubModel(atomic)
  54. self.connectPorts(self.i_in, atomic.i_in)
  55. self.connectPorts(atomic.o_out, self.o_out)
  56. else:
  57. coupled = self.gen_coupled()
  58. self.addSubModel(coupled)
  59. self.connectPorts(self.i_in, coupled.i_in)
  60. self.connectPorts(coupled.o_out, self.o_out)
  61. for idx in range(width - 1):
  62. atomic = DelayedAtomic("Atomic_%d_%d" % (depth - 1, idx), int_delay, ext_delay,
  63. add_out_port=add_atomic_out_ports)
  64. self.addSubModel(atomic)
  65. @abstractmethod
  66. def gen_coupled(self):
  67. """ :return a coupled method with i_in and o_out ports"""
  68. pass
  69. class LI(DEVStoneWrapper):
  70. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float):
  71. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False)
  72. for idx in range(1, len(self.component_set)):
  73. assert isinstance(self.component_set[idx], AtomicDEVS)
  74. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  75. def gen_coupled(self):
  76. return LI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay)
  77. class HI(DEVStoneWrapper):
  78. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float):
  79. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True)
  80. if len(self.component_set) > 1:
  81. assert isinstance(self.component_set[-1], AtomicDEVS)
  82. self.connectPorts(self.i_in, self.component_set[-1].i_in)
  83. for idx in range(1, len(self.component_set) - 1):
  84. assert isinstance(self.component_set[idx], AtomicDEVS)
  85. self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
  86. self.connectPorts(self.i_in, self.component_set[idx].i_in)
  87. def gen_coupled(self):
  88. return HI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay)
  89. class HO(DEVStoneWrapper):
  90. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float):
  91. super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False)
  92. self.i_in2 = self.addInPort("i_in2")
  93. self.o_out2 = self.addOutPort("o_out2")
  94. assert len(self.component_set) > 0
  95. if isinstance(self.component_set[0], CoupledDEVS):
  96. self.connectPorts(self.i_in, self.component_set[0].i_in2)
  97. if len(self.component_set) > 1:
  98. assert isinstance(self.component_set[-1], AtomicDEVS)
  99. self.connectPorts(self.i_in2, self.component_set[-1].i_in)
  100. self.connectPorts(self.component_set[-1].o_out, self.o_out2)
  101. for idx in range(1, len(self.component_set) - 1):
  102. assert isinstance(self.component_set[idx], AtomicDEVS)
  103. self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
  104. self.connectPorts(self.i_in2, self.component_set[idx].i_in)
  105. self.connectPorts(self.component_set[idx].o_out, self.o_out2)
  106. def gen_coupled(self):
  107. return HO("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay)
  108. class HOmod(CoupledDEVS):
  109. def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float):
  110. super().__init__(name)
  111. self.depth = depth
  112. self.width = width
  113. self.int_delay = int_delay
  114. self.ext_delay = ext_delay
  115. self.i_in = self.addInPort("i_in")
  116. self.i_in2 = self.addInPort("i_in2")
  117. self.o_out = self.addOutPort("o_out")
  118. if depth < 1:
  119. raise ValueError("Invalid depth")
  120. if width < 1:
  121. raise ValueError("Invalid width")
  122. if int_delay < 0:
  123. raise ValueError("Invalid int_delay")
  124. if ext_delay < 0:
  125. raise ValueError("Invalid ext_delay")
  126. if depth == 1:
  127. atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True)
  128. self.addSubModel(atomic)
  129. self.connectPorts(self.i_in, atomic.i_in)
  130. self.connectPorts(atomic.o_out, self.o_out)
  131. else:
  132. coupled = HOmod("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay)
  133. self.addSubModel(coupled)
  134. self.connectPorts(self.i_in, coupled.i_in)
  135. self.connectPorts(coupled.o_out, self.o_out)
  136. if width >= 2:
  137. atomics = defaultdict(list)
  138. # Generate atomic components
  139. for i in range(width):
  140. min_row_idx = 0 if i < 2 else i - 1
  141. for j in range(min_row_idx, width - 1):
  142. atomic = DelayedAtomic("Atomic_%d_%d_%d" % (depth - 1, i, j), int_delay, ext_delay, add_out_port=True)
  143. self.addSubModel(atomic)
  144. atomics[i].append(atomic)
  145. # Connect EIC
  146. for atomic in atomics[0]:
  147. self.connectPorts(self.i_in2, atomic.i_in)
  148. for i in range(1, width):
  149. atomic_set = atomics[i]
  150. self.connectPorts(self.i_in2, atomic_set[0].i_in)
  151. # Connect IC
  152. for atomic in atomics[0]: # First row to coupled component
  153. self.connectPorts(atomic.o_out, coupled.i_in2)
  154. for i in range(len(atomics[1])): # Second to first rows
  155. self.connectPorts(atomics[1][i].o_out, atomics[0][i].i_in)
  156. for i in range(2, width): # Rest of rows
  157. for j in range(len(atomics[i])):
  158. self.connectPorts(atomics[i][j].o_out, atomics[i-1][j+1].i_in)
  159. if __name__ == '__main__':
  160. import sys
  161. sys.setrecursionlimit(10000)
  162. root = HOmod("Root", 4, 3, 0, 0)
  163. sim = Simulator(root)
  164. sim.setVerbose(None)
  165. # sim.setTerminationTime(10.0)
  166. sim.setStateSaving("custom")
  167. sim.simulate()