Realtime Simulation

Besides normal (as-fast-as-possible) simulation, it is also possible to simulate CBD models in realtime. Here, the time (and therefore delta t as well) will be interpreted as seconds and the simulator will wait non-blocking until the required time has passed. There are several supported backend that provide this functionality. These backends are based on the backends provided by PyPDEVS.

While there doesn’t have to be feedback duing a simulation, the CBD.simulator.Simulator.setProgressBar() function provides a tqdm progress bar. When running long simulations, this might be a useful feature. Note that, when combined with a termination condition, the progress bar may yield inaccurate values.

Note

Note

Unlike PyPDEVS, interrupt events are not possible. However, as can be seen in the TkInter Dashboard with Editable Values example, the CBD.lib.std.ConstantBlock allows for altering the internal value it outputs during the simulation. This mechanism may be manipulated to allow for external interrupts if necessary.

Example Model

To simplify the explanations of the following sections, we will be using the Sine Generator as a basis model. To recap:

from CBD.Core import CBD
from CBD.simulator import Simulator
from CBD.lib.std import TimeBlock, GenericBlock
from CBD.lib.endpoints import SignalCollectorBlock

class SinGen(CBD):
    def __init__(self, name="SinGen"):
        CBD.__init__(self, name, input_ports=[], output_ports=[])

        # Create the blocks
        self.addBlock(TimeBlock("time"))
        self.addBlock(GenericBlock("sin", block_operator="sin"))
        self.addBlock(SignalCollectorBlock("collector"))

        # Connect the blocks
        self.addConnection("time", "sin")
        self.addConnection("sin", "collector")

sinGen = SinGen("SinGen")
sim = Simulator(sinGen)
sim.setRealTime()

Note

Realtime simulation happens non-blocking. This means the CBD.simulator.Simulator.run() method will be called asynchronously. Additionally, simulation runs as a daemon thread, so exiting the main thread will automatically terminate the simulation. To keep the script alive until after the simulation, you can use:

while sim.is_running():
    pass

Python Threading Backend

The threading (or Python) backend/platform will use the threading module for delaying the simulation steps. This is the default simulation backend.

Warning

Python threads can sometimes have a rather low granularity in CPython 2. So while we are simulating in soft realtime anyway, it is important to note that delays could potentially become significant.

sim.setRealTimePlatformThreading()
sim.setDeltaT(0.3)
sim.run(100.0)

# Keep it alive
while sim.is_running(): pass

print("FINISHED!")

See also

  • CBD.simulator.Simulator.setRealTimePlatform()

  • CBD.simulator.Simulator.setRealTimePlatformThreading()

  • CBD.simulator.Simulator.is_running()

TkInter Backend

The TkInter event loop can become quite complex, as it is required to interface to the GUI as wel as to the simulation. Luckily, this backend will wrap all the complexity into a white box. It is, however, required to define the GUI application and start the mainloop, but afterwards, all will be handled for you.

import tkinter as tk

root = tk.Tk()
sim.setRealTimePlatformTk(root)
sim.setDeltaT(0.3)
sim.run(100.0)
root.mainloop()

print("FINISHED!")

See also

  • CBD.simulator.Simulator.setRealTimePlatform()

  • CBD.simulator.Simulator.setRealTimePlatformTk()

GameLoop Backend

Whenever it is required to control the invocation of delays from another execution loop, like e.g. a gameloop, it is pertinent to make use of the GameLoop backend. Delays won’t happen internally anymore, as they should be handled by the execution loop. By making use of the CBD.simulator.Simulator.realtime_gameloop_call(), the simulation can advance to the next timestep.

sim.setRealTimePlatformGameLoop()
sim.setDeltaT(0.3)
sim.run(100.0)

while sim.is_running():
    # do some fancy computations
    ...

    # do some rendering
    ...

    # advance the model's state
    sim.realtime_gameloop_call()

print("FINISHED!")

Warning

The simulation is still variable on the time constraints of your current system. Use the CBD.realtime.threadingGameLoopAlt.ThreadingGameLoopAlt instead to fully control the time yourself. In this case, the CBD.simulator.Simulator.realtime_gameloop_call() requires the simulation time to be passed as an argument.

While this is an option, it is highly encouraged to use the other backends instead. The alternative gameloop runs on the bare bones of the simulator, making system invalidities possible when not fully understanding the simulator itself. Additionally, exploiting time in a simulation in this way is heavily discouraged and is considered to be a bad practice.

See also

  • CBD.simulator.Simulator.setRealTimePlatform()

  • CBD.simulator.Simulator.setRealTimePlatformGameLoop()

  • CBD.simulator.Simulator.realtime_gameloop_call()

  • CBD.realtime.threadingGameLoopAlt.ThreadingGameLoopAlt