std.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. """
  2. This file contains the standard library for CBD building blocks.
  3. """
  4. from CBD.CBD import BaseBlock, CBD, level
  5. from CBD import naivelog
  6. import math
  7. __all__ = ['ConstantBlock', 'NegatorBlock', 'InverterBlock', 'AdderBlock', 'ProductBlock', 'ModuloBlock',
  8. 'RootBlock', 'AbsBlock', 'IntBlock', 'ClampBlock', 'GenericBlock', 'MultiplexerBlock', 'LessThanBlock',
  9. 'EqualsBlock', 'LessThanOrEqualsBlock', 'NotBlock', 'OrBlock', 'AndBlock', 'DelayBlock', 'LoggingBlock',
  10. 'AddOneBlock', 'DerivatorBlock', 'IntegratorBlock', 'SplitBlock', 'Clock', 'TimeBlock', 'PowerBlock',
  11. 'MaxBlock', 'MinBlock']
  12. class ConstantBlock(BaseBlock):
  13. """
  14. The constant block will always output its constant value
  15. """
  16. def __init__(self, block_name, value=0.0):
  17. BaseBlock.__init__(self, block_name, [], ["OUT1"])
  18. self.__value = value
  19. def getValue(self):
  20. """Get the current value."""
  21. return self.__value
  22. def setValue(self, value):
  23. """Change the constant value."""
  24. self.__value = value
  25. def compute(self, curIteration):
  26. # TO IMPLEMENT
  27. self.appendToSignal(self.getValue())
  28. def __repr__(self):
  29. return BaseBlock.__repr__(self) + " Value = " + str(self.getValue()) + "\n"
  30. class NegatorBlock(BaseBlock):
  31. """
  32. The negator block will output the value of the input multiplied with -1
  33. """
  34. def __init__(self, block_name):
  35. BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])
  36. def compute(self, curIteration):
  37. # TO IMPLEMENT
  38. self.appendToSignal(-self.getInputSignal(curIteration, "IN1").value)
  39. class InverterBlock(BaseBlock):
  40. """
  41. The invertblock will output 1/IN
  42. """
  43. def __init__(self, block_name, tolerance=1e-30):
  44. BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])
  45. self._tolerance = tolerance
  46. def compute(self, curIteration):
  47. # TO IMPLEMENT
  48. input = self.getInputSignal(curIteration, "IN1").value
  49. if abs(input) < self._tolerance:
  50. raise ZeroDivisionError("InverterBlock received input less than {}.".format(self._tolerance))
  51. self.appendToSignal(1.0 / input)
  52. class AdderBlock(BaseBlock):
  53. """
  54. The adderblock will add all the inputs.
  55. Args:
  56. block_name (str): The name of the block.
  57. numberOfInputs (int): The amount of input ports to set.
  58. """
  59. def __init__(self, block_name, numberOfInputs=2):
  60. BaseBlock.__init__(self, block_name, ["IN%d" % (x+1) for x in range(numberOfInputs)], ["OUT1"])
  61. self.__numberOfInputs = numberOfInputs
  62. def compute(self, curIteration):
  63. # TO IMPLEMENT
  64. result = 0
  65. for i in range(1, self.__numberOfInputs+1):
  66. result += self.getInputSignal(curIteration, "IN%d"%i).value
  67. self.appendToSignal(result)
  68. def getNumberOfInputs(self):
  69. """
  70. Gets the total number of input ports.
  71. """
  72. return self.__numberOfInputs
  73. class ProductBlock(BaseBlock):
  74. """
  75. The product block will multiply all the inputs
  76. """
  77. def __init__(self, block_name, numberOfInputs=2):
  78. BaseBlock.__init__(self, block_name, ["IN%d" % (x+1) for x in range(numberOfInputs)], ["OUT1"])
  79. self.__numberOfInputs = numberOfInputs
  80. def compute(self, curIteration):
  81. # TO IMPLEMENT
  82. result = 1
  83. for i in range(1, self.__numberOfInputs+1):
  84. result *= self.getInputSignal(curIteration, "IN%d"%i).value
  85. self.appendToSignal(result)
  86. def getNumberOfInputs(self):
  87. """
  88. Gets the total number of input ports.
  89. """
  90. return self.__numberOfInputs
  91. class ModuloBlock(BaseBlock):
  92. """
  93. A basic block that computes the IN1 modulo IN2
  94. """
  95. def __init__(self, block_name):
  96. BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])
  97. def compute(self, curIteration):
  98. # TO IMPLEMENT
  99. # Use 'math.fmod' for validity with C w.r.t. negative values AND floats
  100. self.appendToSignal(math.fmod(self.getInputSignal(curIteration, "IN1").value, self.getInputSignal(curIteration, "IN2").value))
  101. class RootBlock(BaseBlock):
  102. """
  103. A basic block that computes the IN2-th root from IN1
  104. """
  105. def __init__(self, block_name):
  106. BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])
  107. def compute(self, curIteration):
  108. # TO IMPLEMENT
  109. self.appendToSignal(self.getInputSignal(curIteration, "IN1").value ** (1 / self.getInputSignal(curIteration, "IN2").value))
  110. class PowerBlock(BaseBlock):
  111. """
  112. A basic block that computes IN1 to the IN2-th power
  113. """
  114. def __init__(self, block_name):
  115. BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])
  116. def compute(self, curIteration):
  117. # TO IMPLEMENT
  118. self.appendToSignal(self.getInputSignal(curIteration, "IN1").value ** self.getInputSignal(curIteration, "IN2").value)
  119. class AbsBlock(BaseBlock):
  120. """
  121. The abs block will output the absolute value of the input.
  122. """
  123. def __init__(self, block_name):
  124. BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])
  125. def compute(self, curIteration):
  126. # TO IMPLEMENT
  127. self.appendToSignal(abs(self.getInputSignal(curIteration).value))
  128. class IntBlock(BaseBlock):
  129. """
  130. The int block will output the integer value (floored) of the input.
  131. """
  132. def __init__(self, block_name):
  133. BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])
  134. def compute(self, curIteration):
  135. self.appendToSignal(int(self.getInputSignal(curIteration).value))
  136. class ClampBlock(BaseBlock):
  137. """
  138. The clamp block will clamp the input between min and max.
  139. Args:
  140. block_name (str): The name of the block.
  141. min (numeric): The minimal value.
  142. max (numeric): The maximal value.
  143. use_const (bool): When :code:`True`, the :attr:`min` and :attr:`max`
  144. values will be used. Otherwise, the minimal and
  145. maximal values are expected as inputs 2 and 3,
  146. respectively.
  147. """
  148. def __init__(self, block_name, min=-1, max=1, use_const=True):
  149. super().__init__(block_name, ["IN1"] if use_const else ["IN1", "IN2", "IN3"], ["OUT1"])
  150. self._use_const = use_const
  151. self.min = min
  152. self.max = max
  153. def compute(self, curIteration):
  154. if self._use_const:
  155. min_ = self.min
  156. max_ = self.max
  157. else:
  158. min_ = self.getInputSignal(curIteration, "IN2").value
  159. max_ = self.getInputSignal(curIteration, "IN3").value
  160. x = self.getInputSignal(curIteration, "IN1").value
  161. self.appendToSignal(min(max(x, min_), max_))
  162. class GenericBlock(BaseBlock):
  163. """
  164. The generic block will evaluate the operator on the input
  165. operator is the name (a string) of a Python function from the math library
  166. which will be called when the block is evaluated
  167. by default, initialized to None
  168. """
  169. def __init__(self, block_name, block_operator=None):
  170. # operator is the name (a string) of a Python function from the math library
  171. BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])
  172. self.__block_operator = block_operator
  173. def getBlockOperator(self):
  174. """
  175. Gets the block operator.
  176. """
  177. return self.__block_operator
  178. def compute(self, curIteration):
  179. # TO IMPLEMENT
  180. a = self.getInputSignal(curIteration, "IN1").value
  181. self.appendToSignal(getattr(math, self.getBlockOperator())(a))
  182. def __repr__(self):
  183. repr = BaseBlock.__repr__(self)
  184. if self.__block_operator is None:
  185. repr += " No operator given\n"
  186. else:
  187. repr += " Operator :: " + self.__block_operator + "\n"
  188. return repr
  189. class MultiplexerBlock(BaseBlock):
  190. """
  191. The multiplexer block will output the signal from IN1 if select == 0; otherwise
  192. the signal from IN2 is outputted.
  193. """
  194. def __init__(self, block_name):
  195. BaseBlock.__init__(self, block_name, ["IN1", "IN2", "select"], ["OUT1"])
  196. def compute(self, curIteration):
  197. select = self.getInputSignal(curIteration, "select").value
  198. self.appendToSignal(self.getInputSignal(curIteration, "IN1" if select == 0 else "IN2").value)
  199. class MaxBlock(BaseBlock):
  200. """
  201. The max block will output the maximal value of all its inputs.
  202. """
  203. def __init__(self, block_name, numberOfInputs=2):
  204. BaseBlock.__init__(self, block_name, ["IN%d" % (x+1) for x in range(numberOfInputs)], ["OUT1"])
  205. self.__numberOfInputs = numberOfInputs
  206. def compute(self, curIteration):
  207. # TO IMPLEMENT
  208. result = []
  209. for i in range(1, self.__numberOfInputs+1):
  210. result.append(self.getInputSignal(curIteration, "IN%d"%i).value)
  211. self.appendToSignal(max(result))
  212. def getNumberOfInputs(self):
  213. """
  214. Gets the total number of input ports.
  215. """
  216. return self.__numberOfInputs
  217. class MinBlock(BaseBlock):
  218. """
  219. The min block will output the minimal value of all its inputs.
  220. """
  221. def __init__(self, block_name, numberOfInputs=2):
  222. BaseBlock.__init__(self, block_name, ["IN%d" % (x+1) for x in range(numberOfInputs)], ["OUT1"])
  223. self.__numberOfInputs = numberOfInputs
  224. def compute(self, curIteration):
  225. # TO IMPLEMENT
  226. result = []
  227. for i in range(1, self.__numberOfInputs+1):
  228. result.append(self.getInputSignal(curIteration, "IN%d"%i).value)
  229. self.appendToSignal(min(result))
  230. def getNumberOfInputs(self):
  231. """
  232. Gets the total number of input ports.
  233. """
  234. return self.__numberOfInputs
  235. class SplitBlock(BaseBlock):
  236. """
  237. The split block will split a signal over multiple paths.
  238. While this block can generally be omitted, it may still be
  239. used for clarity and clean-ness of the resulting models.
  240. Args:
  241. block_name (str): The name of the block.
  242. numberOfOutputs (int): The amount of paths to split into.
  243. """
  244. def __init__(self, block_name, numberOfOutputs=2):
  245. BaseBlock.__init__(self, block_name, ["IN1"], ["OUT%d" % (i+1) for i in range(numberOfOutputs)])
  246. self.__numberOfOutputs = numberOfOutputs
  247. def compute(self, curIteration):
  248. value = self.getInputSignal(curIteration).value
  249. for i in range(self.__numberOfOutputs):
  250. self.appendToSignal(value, "OUT%d" % (i+1))
  251. def getNumberOfOutputs(self):
  252. """
  253. Gets the total number of output ports.
  254. """
  255. return self.__numberOfOutputs
  256. class LessThanBlock(BaseBlock):
  257. """
  258. A simple block that will test if the IN1 is smaller than IN2 (output == 1 if true else 0)
  259. """
  260. def __init__(self, block_name):
  261. BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])
  262. def compute(self, curIteration):
  263. gisv = lambda s: self.getInputSignal(curIteration, s).value
  264. self.appendToSignal(1 if gisv("IN1") < gisv("IN2") else 0)
  265. class EqualsBlock(BaseBlock):
  266. """
  267. A simple block that will test if the IN1 is equal to IN2 (output == 1 if true else 0)
  268. """
  269. def __init__(self, block_name):
  270. BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])
  271. def compute(self, curIteration):
  272. gisv = lambda s: self.getInputSignal(curIteration, s).value
  273. self.appendToSignal(1 if gisv("IN1") == gisv("IN2") else 0)
  274. class LessThanOrEqualsBlock(BaseBlock):
  275. """
  276. A simple block that will test if the IN1 is smaller than or equals to IN2 (output == 1 if true else 0)
  277. """
  278. def __init__(self, block_name):
  279. BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])
  280. def compute(self, curIteration):
  281. gisv = lambda s: self.getInputSignal(curIteration, s).value
  282. self.appendToSignal(1 if gisv("IN1") <= gisv("IN2") else 0)
  283. class NotBlock(BaseBlock):
  284. """
  285. A simple Not block that will set a 0 to 1 and vice versa
  286. """
  287. def __init__(self, block_name):
  288. BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])
  289. def compute(self, curIteration):
  290. result = 0 if self.getInputSignal(curIteration, "IN1").value else 1
  291. self.appendToSignal(result)
  292. class OrBlock(BaseBlock):
  293. """
  294. A simple Or block with possibly multiple inputlines
  295. """
  296. def __init__(self, block_name, numberOfInputs=2):
  297. BaseBlock.__init__(self, block_name, ["IN{0}".format(i) for i in range(1,numberOfInputs+1)], ["OUT1"])
  298. self.__numberOfInputs = numberOfInputs
  299. def compute(self, curIteration):
  300. result = 0
  301. for i in range(1, self.__numberOfInputs+1):
  302. result = result or self.getInputSignal(curIteration, "IN"+str(i)).value
  303. self.appendToSignal(result)
  304. def getNumberOfInputs(self):
  305. """
  306. Gets the total number of input ports.
  307. """
  308. return self.__numberOfInputs
  309. class AndBlock(BaseBlock):
  310. """
  311. A simple And block with possibly multiple inputlines
  312. """
  313. def __init__(self, block_name, numberOfInputs=2):
  314. BaseBlock.__init__(self, block_name, ["IN{0}".format(i) for i in range(1,numberOfInputs+1)], ["OUT1"])
  315. self.__numberOfInputs = numberOfInputs
  316. def compute(self, curIteration):
  317. result = 1
  318. for i in range(1, self.__numberOfInputs+1):
  319. result = result and self.getInputSignal(curIteration, "IN"+str(i)).value
  320. self.appendToSignal(result)
  321. def getNumberOfInputs(self):
  322. """
  323. Gets the total number of input ports.
  324. """
  325. return self.__numberOfInputs
  326. class DelayBlock(BaseBlock):
  327. """
  328. A delay block that takes the last value from the list
  329. IC: Initial Condition
  330. """
  331. def __init__(self, block_name):
  332. BaseBlock.__init__(self, block_name, ["IN1", "IC"], ["OUT1"])
  333. def getDependencies(self, curIteration):
  334. # TO IMPLEMENT: This is a helper function you can use to create the dependency graph
  335. # Treat dependencies differently. For instance, at the first iteration (curIteration == 0), the block only depends on the IC;
  336. if curIteration == 0:
  337. return [self._linksIn["IC"].block]
  338. return []
  339. def compute(self, curIteration):
  340. if curIteration == 0:
  341. self.appendToSignal(self.getInputSignal(curIteration, "IC").value)
  342. else:
  343. self.appendToSignal(self.getInputSignal(curIteration - 1).value)
  344. class LoggingBlock(BaseBlock):
  345. """
  346. A simple Logging block
  347. """
  348. def __init__(self, block_name, string, lev=level.WARNING):
  349. BaseBlock.__init__(self, block_name, ["IN1"], [])
  350. self.__string = string
  351. self.__logger = naivelog.getLogger("WarningLog")
  352. self.__lev = lev
  353. def compute(self, curIteration):
  354. if self.getInputSignal(curIteration, "IN1").value == 1:
  355. if self.__lev == level.WARNING:
  356. self.__logger.warning("Time " + str(self.getClock().getTime(curIteration)) + ": " + self.__string)
  357. elif self.__lev == level.ERROR:
  358. self.__logger.error("Time " + str(self.getClock().getTime(curIteration)) + ": " + self.__string)
  359. elif self.__lev == level.FATAL:
  360. self.__logger.fatal("Time " + str(self.getClock().getTime(curIteration)) + ": " + self.__string)
  361. class AddOneBlock(CBD):
  362. """
  363. Block adds a one to the input (used a lot for mux)
  364. """
  365. def __init__(self, block_name):
  366. CBD.__init__(self, block_name, ["IN1"], ["OUT1"])
  367. self.addBlock(ConstantBlock(block_name="OneConstant", value=1))
  368. self.addBlock(AdderBlock("PlusOne"))
  369. self.addConnection("IN1", "PlusOne")
  370. self.addConnection("OneConstant", "PlusOne")
  371. self.addConnection("PlusOne", "OUT1")
  372. class DerivatorBlock(CBD):
  373. """
  374. The derivator block is a CBD that calculates the derivative.
  375. """
  376. def __init__(self, block_name):
  377. CBD.__init__(self, block_name, ["IN1", "delta_t", "IC"], ["OUT1"])
  378. # TO IMPLEMENT
  379. self.addBlock(ProductBlock(block_name="multIc"))
  380. self.addBlock(NegatorBlock(block_name="neg1"))
  381. self.addBlock(AdderBlock(block_name="sum1"))
  382. self.addBlock(DelayBlock(block_name="delay"))
  383. self.addBlock(NegatorBlock(block_name="neg2"))
  384. self.addBlock(AdderBlock(block_name="sum2"))
  385. self.addBlock(ProductBlock(block_name="mult"))
  386. self.addBlock(InverterBlock(block_name="inv"))
  387. self.addConnection("IC", "multIc")
  388. self.addConnection("delta_t", "multIc")
  389. self.addConnection("multIc", "neg1")
  390. self.addConnection("neg1", "sum1")
  391. self.addConnection("IN1", "sum1")
  392. self.addConnection("sum1", "delay", input_port_name="IC")
  393. self.addConnection("IN1", "delay", input_port_name="IN1")
  394. self.addConnection("delay", "neg2")
  395. self.addConnection("neg2", "sum2")
  396. self.addConnection("IN1", "sum2")
  397. self.addConnection("sum2", "mult")
  398. self.addConnection("delta_t", "inv")
  399. self.addConnection("inv", "mult")
  400. self.addConnection("mult", "OUT1")
  401. class IntegratorBlock(CBD):
  402. """
  403. The integrator block is a CBD that calculates the integration.
  404. The block is implemented according to the backwards Euler rule.
  405. """
  406. def __init__(self, block_name):
  407. CBD.__init__(self, block_name, ["IN1", "delta_t", "IC"], ["OUT1"])
  408. # TO IMPLEMENT
  409. # TRAPEZOID RULE // TODO: fix the circularity issue => other solver?
  410. # # self.addBlock(ConstantBlock(block_name="zero", value=0.0))
  411. # self.addBlock(ConstantBlock(block_name="half", value=0.5))
  412. # self.addBlock(DelayBlock(block_name="delayIn"))
  413. # self.addBlock(ProductBlock(block_name="multDelta"))
  414. # self.addBlock(ProductBlock(block_name="multX"))
  415. # self.addBlock(DelayBlock(block_name="delayState"))
  416. # self.addBlock(AdderBlock(block_name="sumX"))
  417. # self.addBlock(AdderBlock(block_name="sumState"))
  418. # self.addBlock(NegatorBlock(block_name="neg"))
  419. # # self.addBlock(TimedGateBlock(block_name="gate", time=0.0))
  420. #
  421. # self.addConnection("IN1", "delayIn")
  422. # self.addConnection("IN1", "neg")
  423. # # self.addConnection("zero", "delayIn", input_port_name="IC")
  424. # self.addConnection("neg", "delayIn", input_port_name="IC")
  425. # self.addConnection("IN1", "sumX")
  426. # self.addConnection("delayIn", "sumX")
  427. # self.addConnection("sumX", "multX")
  428. # self.addConnection("delta_t", "multDelta")
  429. # self.addConnection("half", "multDelta")
  430. # self.addConnection("multDelta", "multX")
  431. # self.addConnection("sumState", "delayState")
  432. # # self.addConnection("zero", "gate", input_port_name="IN1")
  433. # # self.addConnection("multX", "gate", input_port_name="IN2")
  434. # # self.addConnection("gate", "sumState")
  435. # self.addConnection("multX", "sumState")
  436. # self.addConnection("IC", "delayState", input_port_name="IC")
  437. # self.addConnection("delayState", "sumState")
  438. # self.addConnection("sumState", "OUT1")
  439. # FORWARD EULER:
  440. # self.addBlock(ProductBlock("mul"))
  441. # self.addBlock(AdderBlock("sum"))
  442. # self.addBlock(AdderBlock("sum_ic"))
  443. # self.addBlock(DelayBlock("delay"))
  444. # self.addBlock(NegatorBlock("neg"))
  445. #
  446. # self.addConnection("IN1", "mul")
  447. # self.addConnection("delta_t", "mul")
  448. # self.addConnection("mul", "sum")
  449. # self.addConnection("delay", "sum")
  450. # self.addConnection("sum", "delay")
  451. # self.addConnection("IC", "neg")
  452. # self.addConnection("neg", "sum_ic")
  453. # self.addConnection("sum_ic", "delay", input_port_name="IC")
  454. # self.addConnection("mul", "sum_ic")
  455. # self.addConnection("sum", "OUT1")
  456. # BACKWARD EULER:
  457. self.addBlock(ConstantBlock(block_name="zero", value=0))
  458. self.addBlock(DelayBlock(block_name="delayIn"))
  459. self.addBlock(ProductBlock(block_name="multDelta"))
  460. self.addBlock(DelayBlock(block_name="delayState"))
  461. self.addBlock(AdderBlock(block_name="sumState"))
  462. self.addConnection("zero", "delayIn", input_port_name="IC")
  463. self.addConnection("IN1", "delayIn", input_port_name="IN1")
  464. self.addConnection("delayIn", "multDelta")
  465. self.addConnection("delta_t", "multDelta")
  466. self.addConnection("multDelta", "sumState")
  467. self.addConnection("IC", "delayState", input_port_name="IC")
  468. self.addConnection("delayState", "sumState")
  469. self.addConnection("sumState", "delayState", input_port_name="IN1")
  470. self.addConnection("sumState", "OUT1")
  471. class Clock(CBD):
  472. """
  473. System clock. **Must be present in a simulation model.**
  474. Args:
  475. block_name (str): The name of the block.
  476. start_time (float): Time at which the simulation starts. Defaults to 0.
  477. :Input Ports:
  478. - :code:`h`: The delta in-between timesteps. For fixed-rate simulations,
  479. this must be linked up to a constant value (e.g. a :class:`ConstantBlock`).
  480. :Output Ports:
  481. - :code:`time`: The current simulation time.
  482. - :code:`rel_time`: The relative simulation time, ignoring the start time.
  483. """
  484. def __init__(self, block_name, start_time=0.0):
  485. # TODO: simplify if start_time is 0
  486. # TODO: rel_time must not depend on delay + h !!
  487. CBD.__init__(self, block_name, ["h"], ["time", "rel_time"])
  488. self.__start_time = start_time
  489. self.addBlock(ConstantBlock("IC", start_time))
  490. self.addBlock(DelayBlock("delay"))
  491. self.addBlock(AdderBlock("TSum"))
  492. self.addBlock(AdderBlock("STSum"))
  493. self.addBlock(NegatorBlock("STNeg"))
  494. self.addConnection("h", "TSum")
  495. self.addConnection("delay", "TSum")
  496. self.addConnection("TSum", "delay", input_port_name='IN1')
  497. self.addConnection("delay", "time")
  498. self.addConnection("IC", "delay", input_port_name='IC')
  499. self.addConnection("IC", "STNeg")
  500. self.addConnection("TSum", "STSum")
  501. self.addConnection("STNeg", "STSum")
  502. self.addConnection("STSum", "rel_time")
  503. def getTime(self, curIt):
  504. """
  505. Gets the current time of the clock.
  506. """
  507. sig = self.getBlockByName("TSum").getSignal("OUT1")
  508. if curIt == 0 or len(sig) == 0:
  509. return self.__start_time
  510. return sig[curIt - 1].value
  511. def getRelativeTime(self, curIt):
  512. """
  513. Gets the relative simulation time (ignoring the start time).
  514. """
  515. return self.getTime(curIt) - self.__start_time
  516. class TimeBlock(BaseBlock):
  517. """
  518. Obtains the current time of the simulation.
  519. Args:
  520. block_name (str): The name of the block.
  521. Note:
  522. When manipulating and reading time values, it may be better to use the
  523. :class:`Clock` instead.
  524. """
  525. def __init__(self, block_name):
  526. BaseBlock.__init__(self, block_name, [], ["OUT1"])
  527. def compute(self, curIteration):
  528. time = self.getClock().getTime(curIteration)
  529. self.appendToSignal(time)