Browse Source

Changed clock to fix DT at it 0 issue

rparedis 2 years ago
parent
commit
e6138e32a3

+ 1 - 1
examples/scripts/SinGen/SinGen_tkinter_experiment.py

@@ -10,7 +10,7 @@ import tkinter as tk
 root = tk.Tk()
 sinGen = SinGen("SinGen")
 sim = Simulator(sinGen)
-sim.setProgressBar()
+# sim.setProgressBar()
 sim.setRealTime()
 sim.setRealTimePlatformTk(root)
 sim.setDeltaT(0.3)

+ 4 - 6
src/CBD/Core.py

@@ -259,7 +259,6 @@ class BaseBlock:
         return list(self.__inputs.values())
 
     def getInputPortNames(self):
-        # return self.__nameLinks
         return list(self.__inputs.keys())
 
     def getInputPortByName(self, name):
@@ -354,8 +353,7 @@ class BaseBlock:
         """
         name_output = "OUT1" if name_output is None else name_output
         port = self.getOutputPortByName(name_output)
-        curIt = port.count()
-        port.set(Signal(self.getClock().getTime(curIt), value))
+        port.set(Signal(self.getClock().getTime(), value))
 
     def getSignalHistory(self, name_output=None):
         """
@@ -724,7 +722,7 @@ class CBD(BaseBlock):
             formalisms/simulators, the Clock's outputs should be replaced with the
             corresponding simulator's clock without loss of generality.
         """
-        self.addBlock(Clock("%s-clock" % prefix, start_time))
+        self.addBlock(Clock("%s-clock" % prefix, delta_t, start_time))
         self.addBlock(ConstantBlock("%s-delta" % prefix, delta_t))
         self.addConnection("%s-delta" % prefix, "%s-clock" % prefix, input_port_name='h')
 
@@ -745,7 +743,7 @@ class CBD(BaseBlock):
                 block.getBlockName() not in self.getOutputPortNames() + self.getInputPortNames():
             self.__blocks.append(block)
             self.__blocksDict[block.getBlockName()] = block
-            if isinstance(block, (Clock, DummyClock)):
+            if isinstance(block, Clock):
                 self.__clock = block
         else:
             logger = logging.getLogger("CBD")
@@ -956,4 +954,4 @@ class CBD(BaseBlock):
         return name
 
 
-from CBD.lib.std import Clock, ConstantBlock, DummyClock
+from CBD.lib.std import Clock, ConstantBlock

+ 1 - 0
src/CBD/converters/latexify/CBD2Latex.py

@@ -259,6 +259,7 @@ class CBD2Latex:
 						deqs.append(eq)
 			for deq in deqs:
 				# prevent cyclic dependency
+				# TODO: maybe refactor part of the equation?
 				if deq.rhs.contains(deq.lhs):
 					continue
 				if deq.lhs not in outputs:

+ 1 - 1
src/CBD/lib/endpoints.py

@@ -56,7 +56,7 @@ class SignalCollectorBlock(CollectorBlock):
 		self.buffer_size = buffer_size
 
 	def compute(self, curIteration):
-		time = self.getClock().getTime(curIteration)
+		time = self.getClock().getTime()
 		value = self.getInputSignal(curIteration, "IN1").value
 		self._data.append((time, value))
 		if self.buffer_size > 0:

+ 2 - 2
src/CBD/lib/io.py

@@ -154,7 +154,7 @@ class ReadCSV(BaseBlock):
 			raise ValueError("A repeating CSV series must not start at time 0")
 
 	def compute(self, curIteration):
-		time = self.getClock().getTime(curIteration)
+		time = self.getClock().getTime()
 		T = self.data[self.time_col][-1]
 		L = len(self.data[self.time_col])
 		data = {k: self.data[k] for k in self.data if k != self.time_col}
@@ -240,6 +240,6 @@ class WriteCSV(BaseBlock):
 
 	def compute(self, curIteration):
 		inputs = { col: self.getInputSignal(curIteration, col).value for col in self.columns if col != self.time_col }
-		inputs[self.time_col] = self.getClock().getTime(curIteration)
+		inputs[self.time_col] = self.getClock().getTime()
 
 		self.writer.writerow(inputs)

+ 38 - 127
src/CBD/lib/std.py

@@ -15,7 +15,7 @@ __all__ = ['ConstantBlock', 'NegatorBlock', 'InverterBlock',
            'MultiplexerBlock', 'SplitBlock',
            'DelayBlock', 'DeltaTBlock', 'TimeBlock', 'LoggingBlock',
            'AddOneBlock', 'DerivatorBlock', 'IntegratorBlock',
-           'Clock', 'DummyClock', 'SequenceBlock']
+           'Clock', 'SequenceBlock']
 
 class ConstantBlock(BaseBlock):
 	"""
@@ -876,8 +876,8 @@ class TimeBlock(BaseBlock):
 		BaseBlock.__init__(self, block_name, [], ["OUT1", "relative"])
 
 	def compute(self, curIteration):
-		time = self.getClock().getTime(curIteration)
-		rel_time = self.getClock().getRelativeTime(curIteration)
+		time = self.getClock().getTime()
+		rel_time = self.getClock().getRelativeTime()
 		self.appendToSignal(time)
 		self.appendToSignal(rel_time, "relative")
 
@@ -907,7 +907,7 @@ class LoggingBlock(BaseBlock):
 
 	def compute(self, curIteration):
 		if self.getInputSignal(curIteration, "IN1").value:
-			simtime = str(self.getClock().getTime(-1))
+			simtime = str(self.getClock().getTime())
 			if self.__lev == logging.WARNING:
 				self.__logger.warning("[" + simtime + "]  " + self.__string, extra={"block": self})
 			elif self.__lev == logging.ERROR:
@@ -1025,21 +1025,23 @@ class IntegratorBlock(CBD):
 		self.addConnection("sumState", "OUT1")
 
 
-class Clock(CBD):
+class Clock(BaseBlock):
 	"""
 	System clock. **Must be present in a simulation model.**
 
 	Args:
-		block_name (str):   The name of the block.
-		start_time (float): Time at which the simulation starts. Defaults to 0.
+		block_name (str):       The name of the block.
+		start_delta (float):    Time delta at the start of the simulation. Defaults to 0.1.
+		start_time (float):     Time at which the simulation starts. Defaults to 0.
 
 	:Input Ports:
-		**h** -- The delta in-between timesteps. For fixed-rate simulations,
-		this must be linked up to a constant value (e.g. a :class:`ConstantBlock`).
+		**h** -- The delta for the next iteration. I.e., the time to wait until the next
+				 iteration can be computed.
 
 	:Output Ports:
 		- **time** -- The current simulation time.
 		- **rel_time** -- The relative simulation time, ignoring the start time.
+		- **delta_t** -- The current delta.
 
 	Warning:
 		**Clock Usage Assumption:** When adding a (custom) clock to your model(s),
@@ -1047,8 +1049,8 @@ class Clock(CBD):
 		independent of the simulation algorithm used. I.e., changing the delay of a
 		fixed-rate clock should only influence the accuracy of the signals, **not**
 		the correctness of the signals. It is forbidden to misuse these outputs for
-		specific simulations (e.g., using the :code:`time` as a counter, using
-		:code:`delta` as a constant value...).
+		specific simulations (e.g., using the :code:`time` as a counter, assuming
+		:code:`delta_t` is a constant value...).
 
 		In other words, the clock is guaranteed to output a correct value and should
 		only be used in the context of "time". When exporting the CBD model to other
@@ -1058,147 +1060,56 @@ class Clock(CBD):
 	See Also:
 		- :class:`TimeBlock`
 		- :class:`DeltaTBlock`
-		- :class:`DummyClock`
 	"""
-	def __init__(self, block_name, start_time=0.0):
-		# TODO: simplify if start_time is 0
-		CBD.__init__(self, block_name, ["h"], ["time", "rel_time", "delta"])
+	def __init__(self, block_name, start_delta=0.1, start_time=0.0):
+		BaseBlock.__init__(self, block_name, ["h"], ["time", "rel_time", "delta_t"])
+		self.__start_delta = start_delta
 		self.__start_time = start_time
+		self.__delta = self.__start_delta
+		self.__time = self.__start_time
 
-		self.addBlock(ConstantBlock("IC", start_time))
-		self.addBlock(DelayBlock("delay"))
-		self.addBlock(AdderBlock("TSum"))
-		self.addBlock(AdderBlock("STSum"))
-		self.addBlock(NegatorBlock("STNeg"))
-		self.addBlock(ConstantBlock("Past", 0.0))
-		self.addBlock(AdderBlock("PastSum"))
-
-		self.addConnection("h", "TSum")
-		self.addConnection("delay", "TSum")
-		self.addConnection("TSum", "delay", input_port_name='IN1')
-		self.addConnection("delay", "PastSum")
-		self.addConnection("Past", "PastSum")
-		self.addConnection("PastSum", "time")
-
-		self.addConnection("IC", "delay", input_port_name='IC')
+	def getDependencies(self, curIteration):
+		return []
 
-		self.addConnection("IC", "STNeg")
-		self.addConnection("PastSum", "STSum")
-		self.addConnection("STNeg", "STSum")
-		self.addConnection("STSum", "rel_time")
+	def compute(self, curIteration):
+		if curIteration > 0:
+			self.__delta = self.getInputSignal(curIteration - 1, "h").value
+		self.__time += self.__delta
 
-		self.addConnection("h", "delta")
+		self.appendToSignal(self.__time, "time")
+		self.appendToSignal(self.getRelativeTime(), "rel_time")
+		self.appendToSignal(self.__delta, "delta_t")
 
-	def getTime(self, curIt):
+	def getTime(self):
 		"""
 		Gets the current time of the clock.
 		"""
-		# FIXME: this will produce wrong signals for all clock inputs and
-		#        all blocks in the clock itself
-		sig = self.getBlockByName("TSum").getSignalHistory("OUT1")
-		if curIt == 0 or len(sig) == 0:
-			return self.__start_time
-		return sig[curIt - 1].value
-
-	def getRelativeTime(self, curIt):
+		return self.__time
+
+	def getRelativeTime(self):
 		"""
 		Gets the relative simulation time (ignoring the start time).
 		"""
-		return self.getTime(curIt) - self.__start_time
+		return self.getTime() - self.__start_time
 
 	def setStartTime(self, start_time=0.0):
 		self.__start_time = start_time
-		self.getBlockByName("IC").setValue(start_time)
 
 	def getDeltaT(self):
-		dSig = self.getSignalHistory("delta")
-		if len(dSig) == 0:
-			return 0.0
-		return dSig[-1].value
+		return self.__delta
 
 	def reset(self):
 		"""
 		Resets the clock. Required for restarting a simulation.
 		"""
-		self.clearSignals()
-
-	def _rewind(self):
-		CBD._rewind(self)
-		time = self.getInputSignal(-1, "h").value
-		c = self.getBlockByName("Past")
-		c.setValue(c.getValue() - time)
-
-
-class DummyClock(BaseBlock):
-	"""
-	Dummy clock to be used in the tests. It contains the exact same functionality
-	as the actual fixed rate :class:`Clock`, but allows for testing without the
-	need for other correctly implemented blocks.
-
-	Args:
-		block_name (str):   The name of the block.
-		start_time (float): The tima at which the simulation starts. Defaults to 0.
-		delta (float):      The (fixed) time-delta between two iterations.
-
-	:Output Ports:
-		- **time** -- The current simulation time.
-		- **rel_time** -- The relative simulation time, ignoring the start time.
-		- **delta** -- The delay between two clock "ticks".
-
-	Warning:
-		Only use this block in the tests!
-
-	See Also:
-		- :class:`TimeBlock`
-		- :class:`DeltaTBlock`
-		- :class:`Clock`
-    """
-	def __init__(self, block_name, start_time=0.0, delta=0.1):
-		BaseBlock.__init__(self, block_name, [], ["time", "rel_time", "delta"])
-		self.__start_time = start_time
+		self.clearPorts()
 		self.__time = self.__start_time
-		self.__delta_t = delta
-
-	def compute(self, curIteration):
-		self.appendToSignal(self.__time, "time")
-		self.appendToSignal(self.__time - self.__start_time, "rel_time")
-		self.appendToSignal(self.__delta_t, "delta")
-		self.__time += self.__delta_t
-
-	def getTime(self, curIt):
-		"""
-		Obtains the current time of the clock.
-		"""
-		sig = self.getSignalHistory("time")
-		if curIt == 0 or len(sig) == 0:
-			return self.__start_time
-		return sig[curIt - 1].value + self.__delta_t
-
-	def getRelativeTime(self, curIt):
-		"""
-		Obtains the relative simulation time (ignoring the start time).
-		"""
-		return self.getTime(curIt) - self.__start_time
-
-	def getDeltaT(self):
-		"""
-		Obtains the time-delta.
-		"""
-		return self.__delta_t
-
-	def setDeltaT(self, dt):
-		"""
-		Sets the time-delta.
-		Args:
-			dt (float): New time delta.
-		"""
-		self.__delta_t = dt
-
-	def setStartTime(self, st):
-		self.__start_time = st
+		self.__delta = self.__start_delta
 
 	def _rewind(self):
-		self.__start_time -= self.__delta_t
+		self.__time -= self.__delta
+		BaseBlock._rewind(self)
+		self.__delta = self.getInputSignal(-1, "h").value
 
 
 class SequenceBlock(BaseBlock):

+ 1 - 1
src/CBD/naivelog.py

@@ -44,7 +44,7 @@ class ColoredFormatter(logging.Formatter):
 			levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ
 			record.levelname = levelname_color
 		if hasattr(record, "block"):
-			record.simtime = record.block.getClock().getTime(-1)
+			record.simtime = record.block.getClock().getTime()
 			record.blockname = record.block.getPath()
 		return logging.Formatter.format(self, record)
 

+ 1 - 1
src/CBD/preprocessing/rungekutta.py

@@ -81,7 +81,7 @@ class RKPreprocessor:
 			if RK.hasBlock(inp):
 				new_model.addConnection(inp, RK, input_port_name=inp)
 		old_clock = original.getClock()
-		new_model.addBlock(Clock("clock", old_clock.getTime(0) - old_clock.getRelativeTime(0)))
+		new_model.addBlock(Clock("clock", old_clock.getDeltaT()))
 		new_model.addConnection("clock", RK, input_port_name='time', output_port_name='time')
 		new_model.addConnection("clock", RK, input_port_name='rel_time', output_port_name='rel_time')
 		if RK.hasBlock("h_new"):

+ 2 - 2
src/CBD/simulator.py

@@ -235,7 +235,7 @@ class Simulator:
 			- :func:`setDeltaT`
 			- :class:`CBD.lib.std.Clock`
 		"""
-		return self.getClock().getTime(self.__sim_data[2])
+		return self.getClock().getTime()
 
 	def getRelativeTime(self):
 		"""
@@ -248,7 +248,7 @@ class Simulator:
 			- :func:`setDeltaT`
 			- :class:`CBD.lib.std.Clock`
 		"""
-		return self.getClock().getRelativeTime(self.__sim_data[2])
+		return self.getClock().getRelativeTime()
 
 	def getDeltaT(self):
 		"""

+ 1 - 1
src/test/basicCBDTest.py

@@ -19,7 +19,7 @@ class BasicCBDTestCase(unittest.TestCase):
 	def _run(self, num_steps=1, delta_t = 1.0):
 		self.sim.setDeltaT(delta_t)
 		self.sim.setTerminationTime(num_steps * delta_t)
-		self.CBD.addBlock(DummyClock("clock", delta=delta_t))
+		self.CBD.addFixedRateClock("clock", delta_t, )
 		self.sim.run()
 
 	def _getSignal(self, blockname, output_port = None):

+ 1 - 1
src/test/stdCBDTest.py

@@ -19,7 +19,7 @@ class StdCBDTestCase(unittest.TestCase):
 	def _run(self, num_steps=1, delta_t = 1.0):
 		self.sim.setDeltaT(delta_t)
 		self.sim.setTerminationTime(num_steps * delta_t)
-		self.CBD.addBlock(DummyClock("clock", delta=delta_t))
+		self.CBD.addFixedRateClock("clock", delta_t)
 		self.sim.run()
 
 	def _getSignal(self, blockname, output_port = None):