Преглед на файлове

Loads of bugfixes wrt the simulator

rparedis преди 3 години
родител
ревизия
db78b3c4bd
променени са 4 файла, в които са добавени 96 реда и са изтрити 38 реда
  1. 11 12
      experiments/HarmonicOscilator/CBDA.py
  2. 0 0
      experiments/HarmonicOscilator/backup_code.py
  3. 66 3
      src/CBD/Core.py
  4. 19 23
      src/CBD/depGraph.py

+ 11 - 12
experiments/HarmonicOscilator/CBDA.py

@@ -58,7 +58,7 @@ class HarmonicB(CBD):
 
 class HarmonicA(CBD):
 	def __init__(self, block_name):
-		CBD.__init__(self, block_name, input_ports=['dt'], output_ports=['x'])
+		CBD.__init__(self, block_name, output_ports=['x'])
 		
 		# Create the blocks
 		self.addBlock(IntegratorBlock(block_name='int2'))
@@ -68,8 +68,6 @@ class HarmonicA(CBD):
 		self.addBlock(NegatorBlock(block_name='neg'))
 		
 		# Connect the blocks
-		self.addConnection('dt', 'int2', input_port_name='delta_t')
-		self.addConnection('dt', 'int1', input_port_name='delta_t')
 		self.addConnection('zero', 'int2', input_port_name='IC')
 		self.addConnection('one', 'int1', input_port_name='IC')
 		self.addConnection('int2', 'x')
@@ -79,7 +77,7 @@ class HarmonicA(CBD):
 
 
 class ErrorA(CBD):
-	def __init__(self, block_name, dt=(DELTA_T)):
+	def __init__(self, block_name):
 		CBD.__init__(self, block_name, input_ports=[], output_ports=['e', 'real', 'A'])
 		
 		# Create the blocks
@@ -88,7 +86,6 @@ class ErrorA(CBD):
 		self.addBlock(AdderBlock(block_name='sum'))
 		self.addBlock(AbsBlock(block_name='abs'))
 		self.addBlock(IntegratorBlock(block_name='int'))
-		self.addBlock(ConstantBlock(block_name='dt', value=(dt)))
 		self.addBlock(ConstantBlock(block_name='zero', value=(0)))
 		self.addBlock(HarmonicA(block_name='harmonic'))
 		
@@ -97,11 +94,9 @@ class ErrorA(CBD):
 		self.addConnection('neg', 'sum')
 		self.addConnection('sum', 'abs')
 		self.addConnection('zero', 'int', input_port_name='IC')
-		self.addConnection('dt', 'int', input_port_name='delta_t')
 		self.addConnection('abs', 'int')
 		self.addConnection('int', 'e')
 		self.addConnection('harmonic', 'neg', output_port_name='x')
-		self.addConnection('dt', 'harmonic', input_port_name='dt')
 		self.addConnection('sin', 'real')
 		self.addConnection('harmonic', 'A', output_port_name='x')
 
@@ -147,20 +142,24 @@ class ErrorB(CBD):
 		self.addConnection('sin', 'real')
 
 
+from CBD.depGraph import gvDepGraph
 if __name__ == '__main__':
 	outputs = []
 	signals = []
 	for dt in [0.1, 0.01]:
 		print("DT:", dt)
-		cbd = ErrorA("ErrorA", dt=dt)
+		cbd = ErrorA("ErrorA")
+		with open("test.gv", "w") as file:
+			file.write(gvDepGraph(cbd, 0))
 		# Run the simulation
 		sim = Simulator(cbd)
 		sim.setDeltaT(dt)
 		sim.run(int(50/dt))
-		# tvpl = cbd.getSignalHistory("e")
-		# outputs.append(tvpl)
-		# signals.append(str(dt))
-		# plot_signals(cbd, ['real', 'A'], f'Value A ({dt})')
+		tvpl = cbd.getSignalHistory("e")
+		outputs.append(tvpl)
+		signals.append(str(dt))
+		print(" starting plotter")
+		plot_signals(cbd, ['real', 'A'], f'Value A ({dt})')
 
 	# plt.figure()
 	# plt.title("Error A")

experiments/HarmonicOscilator/code.py → experiments/HarmonicOscilator/backup_code.py


+ 66 - 3
src/CBD/Core.py

@@ -58,6 +58,12 @@ class Port:
         """
         self.__history.clear()
 
+    def reset(self):
+        """Resets the port's connections."""
+        self.clear()
+        self.__outgoing = []
+        self.__incoming = None
+
     def getOutgoing(self):
         """
         Obtains all outgoing connections from this port.
@@ -79,15 +85,41 @@ class Port:
     def getPreviousPortClosure(self):
         """
         Find the previous port to which this port is connected that has no incoming connections.
-        Hierarchical blocks and useless connections will be solved using this method.
+        Hierarchical blocks and useless connections can/will be solved using this method.
 
-        I.e., it obtains the port whos signal changes will eventually transfer to this port.
+        I.e., it obtains the port whose signal changes will eventually transfer to this port.
         """
         inc = self.getIncoming().source
         while inc.getIncoming() is not None:
             inc = inc.getIncoming().source
+            if inc == self:
+                raise ValueError("Loop Detected!")
         return inc
 
+    def getNextPortClosure(self):
+        """
+        Find the next ports to which this port is connected that has no outgoing connections.
+        Hierarchical blocks and useless connections can/will be solved using this method.
+
+        I.e., it obtains the ports to whom this port transfers signals.
+        """
+        res = []
+        loop_for = [self]
+        visited = []
+        while len(loop_for) > 0:
+            elm = loop_for.pop(0)
+            if elm in visited:
+                raise ValueError("Loop Detected!")
+            visited.append(elm)
+            for out in elm.getOutgoing():
+                port = out.target
+                # End only in input ports
+                if len(port.getOutgoing()) == 0:
+                    res.append(port)
+                else:
+                    loop_for.append(port)
+        return res
+
     def count(self):
         """
         Counts how often a signal was changed on this port.
@@ -256,6 +288,7 @@ class BaseBlock:
         """
         other = deepcopy(self)
         other.reparentPorts()
+        other.resetPorts()
         other._parent = None
         return other
 
@@ -331,9 +364,19 @@ class BaseBlock:
         return self.getOutputPortByName(name_output).getHistory()
 
     def clearPorts(self):
+        """
+        Clears all signal data from the ports.
+        """
         for port in self.getInputPorts() + self.getOutputPorts():
             port.clear()
 
+    def resetPorts(self):
+        """
+        Clears all signal data from the ports and resets all connections.
+        """
+        for port in self.getInputPorts() + self.getOutputPorts():
+            port.reset()
+
     def getDependencies(self, curIteration):
         """
         Helper function to help the creation of the dependency graph.
@@ -506,14 +549,28 @@ class CBD(BaseBlock):
         self.__clock = None
 
     def clone(self):
+        # Clone all fields
         other = BaseBlock.clone(self)
         # other.setClock(deepcopy(self.getClock()))
+
+        # Re-add all blocks to ensure deep cloning
         other.clearBlocks()
         for block in self.getBlocks():
             other.addBlock(block.clone())
+
+        # Reconnect all blocks in the clone
         for block in self.getBlocks():
             for inp in block.getInputPorts():
-                other.addConnection(inp.block, block, inp.name, inp.getIncoming().source.name)
+                prev = inp.getIncoming().source
+                if prev.block == self: continue
+                other.addConnection(prev.block.getBlockName(), inp.block.getBlockName(), output_port_name=prev.name, input_port_name=inp.name)
+        for inp in self.getInputPorts():
+            for target in inp.getOutgoing():
+                nxt = target.target
+                other.addConnection(other, nxt.block.getBlockName(), output_port_name=inp.name, input_port_name=nxt.name)
+        for out in self.getOutputPorts():
+            prev = out.getIncoming().source
+            other.addConnection(prev.block.getBlockName(), other, output_port_name=prev.name, input_port_name=out.name)
         return other
 
     def getTopCBD(self):
@@ -698,6 +755,12 @@ class CBD(BaseBlock):
                 to_block = self.getBlockByName(to_block)
         to_block.linkToInput(from_block, input_port_name, output_port_name)
 
+    def getDependencies(self, curIteration):
+        deps = []
+        for block in self.getBlocks():
+            deps += [x for x in block.getDependencies(curIteration) if x.block == self]
+        return deps
+
     def removeConnection(self, block, input_port_name):
         """
         Removes an input connection of block :code:`block` and port :code:`input_port_name`.

+ 19 - 23
src/CBD/depGraph.py

@@ -1,11 +1,9 @@
-""" This module implements a dependency graph
+"""
+This module implements a dependency graph
 
-:Author:        Marc Provost
-:Organization:  McGill University
-:License:       GNU General Public License
-:Contact:       marc.provost@mail.mcgill.ca
+:Original Author:        Marc Provost
 """
-from .Core import CBD, BaseBlock
+from .Core import CBD, BaseBlock, Port
 import copy
 
 
@@ -138,6 +136,7 @@ class DepGraph:
 			ValueError: if the dependency already exists
 		"""
 		# TODO: fix infinite cycle when passing straight through
+		# TODO: fix errors when multi-level connection
 
 		# Link CBD outputs
 		if isinstance(influencer, CBD) and not self.ignore_hierarchy:
@@ -147,10 +146,7 @@ class DepGraph:
 			for inp in dependent.getInputPorts():
 				inf_out = inp.getIncoming().source
 				if inf_out.block == influencer:
-					inf = inf_out.getIncoming().source
-					if inf.block == influencer:
-						# Look beyond, because normal connection is straight-through
-						inf = inf.getIncoming().source
+					inf = inp.getPreviousPortClosure()
 					self.setDependency(dependent, inf.block, curIt)
 			return
 
@@ -160,17 +156,11 @@ class DepGraph:
 			# more than one dependency should be set, as there is more than one underlying
 			# connection
 			for inp in dependent.getInputPorts():
-				inf_out = inp.getIncoming().source
-				while isinstance(inf_out.block, CBD):
-					inf_out = inf_out.getIncoming().source
+				inf_out = inp.getPreviousPortClosure()
 				if inf_out.block == influencer:
-					for conn in inp.getOutgoing():
-						dep = conn.target
-						if dep.block == dependent:
-							# Look beyond, because normal connection is straight-through
-							for c2 in dep.getOutgoing():
-								self.setDependency(c2.target.block, influencer, curIt)
-						else:
+					for dep in inp.getNextPortClosure():
+						# When direction == OUT; the port is simply unused!
+						if dep.direction == Port.Direction.IN:
 							self.setDependency(dep.block, influencer, curIt)
 			return
 
@@ -308,6 +298,8 @@ def createDepGraph(model, curIteration, ignore_hierarchy=False):
 
 	for block in blocks:
 		depGraph.addMember(block)
+	# for port in model.getInputPorts() + model.getOutputPorts():
+	# 	depGraph.addMember(port)
 
 	def recSetInternalDependencies(blocks, curIteration):
 		"""
@@ -319,7 +311,7 @@ def createDepGraph(model, curIteration, ignore_hierarchy=False):
 		"""
 		for block in blocks:
 			for dep in block.getDependencies(curIteration):
-				# if dep.block == block._parent:
+				# if dep.block == model:
 				# 	depGraph.setDependency(block, dep, curIteration)
 				# else:
 				depGraph.setDependency(block, dep.block, curIteration)
@@ -348,7 +340,11 @@ def gvDepGraph(model, curIt, ignore_hierarchy=False):
 	nodes = []
 	edges = []
 	for block in m2.getBlocks():
-		nodes.append('{name} [label="{type}({name})"];'.format(name=block.getBlockName(), type=block.getBlockType()))
+		bname = block.getBlockName()
+		nodes.append('{namee} [label="{type}({name})"];'.format(name=bname, namee=bname.replace(".", "_"), type=block.getBlockType()))
 		for inf in depGraph.getInfluencers(block):
-			edges.append("{} -> {};".format(block.getBlockName(), inf.getBlockName()))
+			if isinstance(inf, BaseBlock):
+				edges.append("{} -> {};".format(block.getBlockName().replace(".", "_"), inf.getBlockName().replace(".", "_")))
+			else:
+				edges.append("{} -> {};".format(block.getBlockName().replace(".", "_"), inf.name.replace(".", "_")))
 	return "digraph {  // time = " + str(curIt) + "\n\t" + "\n\t".join(nodes + edges) + "\n}"