|
|
@@ -8,7 +8,7 @@ from pypdevs.simulator import Simulator
|
|
|
|
|
|
|
|
|
class DelayedAtomic(AtomicDEVS):
|
|
|
- def __init__(self, name: str, int_delay: float, ext_delay: float, add_out_port: bool = False, prep_time=0):
|
|
|
+ def __init__(self, name: str, int_delay: float, ext_delay: float, add_out_port: bool = False, prep_time=0, mode:str = ""):
|
|
|
super().__init__(name)
|
|
|
|
|
|
self.int_delay = int_delay
|
|
|
@@ -16,12 +16,12 @@ class DelayedAtomic(AtomicDEVS):
|
|
|
self.prep_time = prep_time
|
|
|
|
|
|
self.i_in = self.addInPort("i_in")
|
|
|
- if add_out_port:
|
|
|
+ if add_out_port: # for HI and HO models
|
|
|
self.o_out = self.addOutPort("o_out")
|
|
|
|
|
|
- # used for dynamic structure models
|
|
|
- self.in_ports = []
|
|
|
- self.out_ports = []
|
|
|
+ # Dynamic Structure extras
|
|
|
+ self.mode = mode
|
|
|
+ self.out_ports = [] # used for dynamic structure model LI2HI
|
|
|
|
|
|
def intTransition(self):
|
|
|
if self.int_delay:
|
|
|
@@ -36,8 +36,14 @@ class DelayedAtomic(AtomicDEVS):
|
|
|
return INFINITY
|
|
|
|
|
|
def outputFnc(self):
|
|
|
- if hasattr(self, "o_out"):
|
|
|
+ if hasattr(self, "o_out") and self.o_out is not None:
|
|
|
return {self.o_out: [0]}
|
|
|
+
|
|
|
+ # for DSDEVS LI2HI
|
|
|
+ if len(self.out_ports) == 1 and self.out_ports[0].outline != []:
|
|
|
+ # print(f"{self.name}: should be last call!!")
|
|
|
+ # print("Send to ", self.out_ports[0].outline)
|
|
|
+ return {self.out_ports[0]: [0]}
|
|
|
return {}
|
|
|
|
|
|
def extTransition(self, inputs):
|
|
|
@@ -47,19 +53,37 @@ class DelayedAtomic(AtomicDEVS):
|
|
|
return "active"
|
|
|
|
|
|
def modelTransition(self, state):
|
|
|
- if state["LI2HI"]:
|
|
|
- if self.name.split("_")[2] != "1":
|
|
|
- self.in_ports.append(self.addInPort("i_HI"))
|
|
|
- if self.name.split("_")[2] != "2":
|
|
|
- self.out_ports.append(self.addOutPort("o_HI"))
|
|
|
+ if self.mode == "LI2HI":
|
|
|
+ return self.LI2HI(state)
|
|
|
+ elif self.mode == "HI2LI":
|
|
|
+ return self.HI2LI(state)
|
|
|
+ return False
|
|
|
+
|
|
|
+ def LI2HI(self, state):
|
|
|
+ if len(self.out_ports) == 0 and not hasattr(self, "o_out"):
|
|
|
+ # every automic dev gets the extra out port
|
|
|
+ # even though the last atomic could miss it see fig
|
|
|
+ # except the atomic model in the coupled model at max depth
|
|
|
+ # which has a output port by default
|
|
|
+ state["LI2HI"] = True
|
|
|
+ state["name"] = self.name
|
|
|
+ self.out_ports.append(self.addOutPort("o_HI")) # standard LI model doesn't have by default
|
|
|
+ # print(f"{self.name}: should be first call!!")
|
|
|
return True # send to the parent to connect ports
|
|
|
- else:
|
|
|
- return False
|
|
|
+ return False
|
|
|
+
|
|
|
+ def HI2LI(self, state):
|
|
|
+ if hasattr(self, "o_out") and self.o_out is not None:
|
|
|
+ state["HI2LI"] = True
|
|
|
+ state["name"] = self.name
|
|
|
+ self.removePort(self.o_out)
|
|
|
+ self.o_out = None
|
|
|
+ return False
|
|
|
|
|
|
|
|
|
class DelayedAtomicStats(DelayedAtomic):
|
|
|
- def __init__(self, name: str, int_delay: float, ext_delay: float, add_out_port: bool = False, prep_time=0):
|
|
|
- super().__init__(name, int_delay, ext_delay, add_out_port, prep_time)
|
|
|
+ def __init__(self, name: str, int_delay: float, ext_delay: float, add_out_port: bool = False, prep_time=0, mode:str = ""):
|
|
|
+ super().__init__(name, int_delay, ext_delay, add_out_port, prep_time, mode)
|
|
|
|
|
|
self.int_count = 0
|
|
|
self.ext_count = 0
|
|
|
@@ -76,7 +100,7 @@ class DelayedAtomicStats(DelayedAtomic):
|
|
|
class DEVStoneWrapper(CoupledDEVS, ABC):
|
|
|
|
|
|
def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float,
|
|
|
- add_atomic_out_ports: bool = False, prep_time=0, stats=False):
|
|
|
+ add_atomic_out_ports: bool = False, prep_time=0, stats=False, mode: str = ""):
|
|
|
super().__init__(name)
|
|
|
|
|
|
self.depth = depth
|
|
|
@@ -101,9 +125,9 @@ class DEVStoneWrapper(CoupledDEVS, ABC):
|
|
|
|
|
|
if depth == 1:
|
|
|
if self.stats:
|
|
|
- atomic = DelayedAtomicStats("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
|
|
|
+ atomic = DelayedAtomicStats("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time, mode=mode)
|
|
|
else:
|
|
|
- atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time)
|
|
|
+ atomic = DelayedAtomic("Atomic_0_0", int_delay, ext_delay, add_out_port=True, prep_time=prep_time, mode=mode)
|
|
|
|
|
|
self.addSubModel(atomic)
|
|
|
|
|
|
@@ -118,10 +142,10 @@ class DEVStoneWrapper(CoupledDEVS, ABC):
|
|
|
for idx in range(width - 1):
|
|
|
if self.stats:
|
|
|
atomic = DelayedAtomicStats("Atomic_%d_%d" % (depth - 1, idx), int_delay, ext_delay,
|
|
|
- add_out_port=add_atomic_out_ports, prep_time=prep_time)
|
|
|
+ add_out_port=add_atomic_out_ports, prep_time=prep_time, mode=mode)
|
|
|
else:
|
|
|
atomic = DelayedAtomic("Atomic_%d_%d" % (depth - 1, idx), int_delay, ext_delay,
|
|
|
- add_out_port=add_atomic_out_ports, prep_time=prep_time)
|
|
|
+ add_out_port=add_atomic_out_ports, prep_time=prep_time, mode=mode)
|
|
|
self.addSubModel(atomic)
|
|
|
|
|
|
@abstractmethod
|
|
|
@@ -270,49 +294,169 @@ class HOmod(CoupledDEVS):
|
|
|
|
|
|
class LI2HI(DEVStoneWrapper):
|
|
|
"""
|
|
|
- Dynamic DEVStone variant that starts as an LI (loosely interconnected)
|
|
|
- and gradually transforms into an HI (highly interconnected) model
|
|
|
- by adding connections during internal transitions.
|
|
|
+ Dynamic DEVStone variant that starts as an LI
|
|
|
+ and (gradually??) transforms into an HI model
|
|
|
"""
|
|
|
|
|
|
- def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float,
|
|
|
- prep_time=0, stats=False):
|
|
|
- super().__init__(name, depth, width, int_delay, ext_delay,
|
|
|
- add_atomic_out_ports=True, prep_time=prep_time, stats=stats)
|
|
|
+ def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
|
|
|
+ super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False, prep_time=prep_time,
|
|
|
+ stats=stats, mode="LI2HI")
|
|
|
+
|
|
|
+ for idx in range(1, len(self.component_set)):
|
|
|
+ assert isinstance(self.component_set[idx], AtomicDEVS)
|
|
|
+ self.connectPorts(self.i_in, self.component_set[idx].i_in)
|
|
|
+
|
|
|
+ def gen_coupled(self):
|
|
|
+ return LI2HI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
|
|
|
+ prep_time=self.prep_time, stats=self.stats)
|
|
|
+
|
|
|
+ def modelTransition(self, state):
|
|
|
+ if state.get("LI2HI", True):
|
|
|
+ for i in range(len(self.component_set) - 1): # -1 because the last model can't be connected
|
|
|
+ # basically checks if it is an atomic model and if it got the extra output port
|
|
|
+ if hasattr(self.component_set[i], "out_ports") and len(self.component_set[i].out_ports) == 1:
|
|
|
+ # print(f"Connect {self.component_set[i].name} to {self.component_set[i+1].name}")
|
|
|
+ self.connectPorts(self.component_set[i].out_ports[0], self.component_set[i + 1].i_in)
|
|
|
+ return False
|
|
|
+ return False
|
|
|
+
|
|
|
+
|
|
|
+class HI2LI(DEVStoneWrapper):
|
|
|
+ """
|
|
|
+ Dynamic DEVStone variant that starts as an HI
|
|
|
+ and (gradually??) transforms into an LI model
|
|
|
+ """
|
|
|
+ def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
|
|
|
+ super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
|
|
|
+ stats=stats, mode="HI2LI")
|
|
|
+
|
|
|
+ if len(self.component_set) > 1:
|
|
|
+ assert isinstance(self.component_set[-1], AtomicDEVS)
|
|
|
+ self.connectPorts(self.i_in, self.component_set[-1].i_in)
|
|
|
+
|
|
|
+ for idx in range(1, len(self.component_set) - 1):
|
|
|
+ assert isinstance(self.component_set[idx], AtomicDEVS)
|
|
|
+ self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
|
|
|
+ self.connectPorts(self.i_in, self.component_set[idx].i_in)
|
|
|
+
|
|
|
+ def gen_coupled(self):
|
|
|
+ return HI2LI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
|
|
|
+ prep_time=self.prep_time, stats=self.stats)
|
|
|
+
|
|
|
+
|
|
|
+class Fertilizer(AtomicDEVS):
|
|
|
+ def __init__(self, name: str, period: float = 1.0, repeat: int = 1):
|
|
|
+ super().__init__(name)
|
|
|
+ self.period = period
|
|
|
+ self.repeat = repeat
|
|
|
+
|
|
|
+ self._counter = 0
|
|
|
+ self._active = True
|
|
|
+
|
|
|
+ def intTransition(self):
|
|
|
+ return "passive" # state is unimportant
|
|
|
+
|
|
|
+ def timeAdvance(self):
|
|
|
+ # stop when counter >= repeat
|
|
|
+ if self._counter >= self.repeat:
|
|
|
+ return INFINITY
|
|
|
+ return self.period
|
|
|
+
|
|
|
+ def outputFnc(self):
|
|
|
+ return {}
|
|
|
+
|
|
|
+ def modelTransition(self, state):
|
|
|
+ # every time TA elapses, this will be called on the atomic:
|
|
|
+ # set a flag the parent coupled can read
|
|
|
+ # increment counter and request modelTransition (return True)
|
|
|
+ if self._counter >= self.repeat:
|
|
|
+ return False
|
|
|
+ self._counter += 1
|
|
|
+ state["generate"] = True
|
|
|
+ return True
|
|
|
|
|
|
- # transformation state tracking
|
|
|
- self.converted_connections = 0
|
|
|
- self.total_connections = max(0, len(self.component_set) - 2)
|
|
|
|
|
|
- # initially LI-like connections
|
|
|
+class dLI(DEVStoneWrapper):
|
|
|
+ """
|
|
|
+ A LI model which grows in width
|
|
|
+ """
|
|
|
+ def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
|
|
|
+ super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=False, prep_time=prep_time,
|
|
|
+ stats=stats, mode="LI-Dynamic")
|
|
|
+
|
|
|
+ self.max_gen = 1
|
|
|
for idx in range(1, len(self.component_set)):
|
|
|
assert isinstance(self.component_set[idx], AtomicDEVS)
|
|
|
self.connectPorts(self.i_in, self.component_set[idx].i_in)
|
|
|
|
|
|
- # output connection
|
|
|
+ self.generator = self.addSubModel(Fertilizer(self.name + "_gen", repeat=self.max_gen))
|
|
|
+
|
|
|
+ def gen_coupled(self):
|
|
|
+ return dLI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
|
|
|
+ prep_time=self.prep_time, stats=self.stats)
|
|
|
+
|
|
|
+ def modelTransition(self, state):
|
|
|
+ if state.get("generate", True):
|
|
|
+ name = "Atomic_%d_%d" % (self.depth - 1, self.width) if self.depth > 1 else "Atomic_0_%d" % self.width
|
|
|
+ if self.stats:
|
|
|
+ new_atom = DelayedAtomicStats(name, self.int_delay, self.ext_delay,
|
|
|
+ add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
|
|
|
+ else:
|
|
|
+ new_atom = DelayedAtomic(name, self.int_delay, self.ext_delay,
|
|
|
+ add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
|
|
|
+ self.addSubModel(new_atom)
|
|
|
+ self.connectPorts(self.i_in, new_atom.i_in)
|
|
|
+
|
|
|
+ self.width += 1
|
|
|
+ # print(f"{self.name}: added {new_atom.name} (width is now {self.width})")
|
|
|
+ return False
|
|
|
+ return False
|
|
|
+
|
|
|
+
|
|
|
+class dHI(DEVStoneWrapper):
|
|
|
+ """
|
|
|
+ A HI model which grows in width
|
|
|
+ """
|
|
|
+ def __init__(self, name: str, depth: int, width: int, int_delay: float, ext_delay: float, prep_time=0, stats=False):
|
|
|
+ super().__init__(name, depth, width, int_delay, ext_delay, add_atomic_out_ports=True, prep_time=prep_time,
|
|
|
+ stats=stats, mode="HI-Dynamic")
|
|
|
+
|
|
|
+ self.max_gen = 10
|
|
|
if len(self.component_set) > 1:
|
|
|
- self.connectPorts(self.component_set[-1].o_out, self.o_out)
|
|
|
+ assert isinstance(self.component_set[-1], AtomicDEVS)
|
|
|
+ self.connectPorts(self.i_in, self.component_set[-1].i_in)
|
|
|
+
|
|
|
+ for idx in range(1, len(self.component_set) - 1):
|
|
|
+ assert isinstance(self.component_set[idx], AtomicDEVS)
|
|
|
+ self.connectPorts(self.component_set[idx].o_out, self.component_set[idx + 1].i_in)
|
|
|
+ self.connectPorts(self.i_in, self.component_set[idx].i_in)
|
|
|
+
|
|
|
+ self.generator = self.addSubModel(Fertilizer(self.name + "_gen", repeat=self.max_gen))
|
|
|
|
|
|
def gen_coupled(self):
|
|
|
- return LI2HI("Coupled_%d" % (self.depth - 1),
|
|
|
- self.depth - 1, self.width, self.int_delay, self.ext_delay,
|
|
|
- prep_time=self.prep_time, stats=self.stats)
|
|
|
+ return dHI("Coupled_%d" % (self.depth - 1), self.depth - 1, self.width, self.int_delay, self.ext_delay,
|
|
|
+ prep_time=self.prep_time, stats=self.stats)
|
|
|
|
|
|
def modelTransition(self, state):
|
|
|
- """
|
|
|
- Gradually adds HI-style interconnections between atomics.
|
|
|
- Called automatically during internal transitions.
|
|
|
- """
|
|
|
- print("CHANGE")
|
|
|
-
|
|
|
- # Each step adds one connection
|
|
|
- if self.converted_connections < self.total_connections:
|
|
|
- src_idx = self.converted_connections + 1
|
|
|
- if src_idx < len(self.component_set) - 1:
|
|
|
- src = self.component_set[src_idx]
|
|
|
- dst = self.component_set[src_idx + 1]
|
|
|
- self.connectPorts(src.o_out, dst.i_in)
|
|
|
- self.converted_connections += 1
|
|
|
+ if state.get("generate", True):
|
|
|
+ name = "Atomic_%d_%d" % (self.depth - 1, self.width) if self.depth > 1 else "Atomic_0_%d" % self.width
|
|
|
+ if self.stats:
|
|
|
+ new_atom = DelayedAtomicStats(name, self.int_delay, self.ext_delay,
|
|
|
+ add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
|
|
|
+ else:
|
|
|
+ new_atom = DelayedAtomic(name, self.int_delay, self.ext_delay,
|
|
|
+ add_out_port=self.add_atomic_out_ports, prep_time=self.prep_time, mode="LI-Dynamic")
|
|
|
+
|
|
|
+ last = self.component_set[-2]
|
|
|
+
|
|
|
+ self.addSubModel(new_atom)
|
|
|
+ if isinstance(last, AtomicDEVS) and hasattr(last, "o_out"):
|
|
|
+ self.connectPorts(last.o_out, new_atom.i_in)
|
|
|
+ self.connectPorts(self.i_in, new_atom.i_in)
|
|
|
+
|
|
|
+ self.width += 1
|
|
|
+ # print(f"{self.name}: added {new_atom.name} (width is now {self.width})")
|
|
|
+ return False
|
|
|
return False
|
|
|
|
|
|
|