Continuous Time Simulation
==========================
Given that continuous time simulation will always have to be discretized when
executed, it is important to ask *how* this discretization happens. In the
:doc:`SinGen` example, this was done by reducing the delta inbetween multiple
steps. However, this may compute too often for what is required in a given
simulation. Assume we have the following data (from a sine wave):

.. figure:: ../_figures/stepsize/sine.png
   :width: 600

Fixed Step Size
---------------
With the previously discussed technique, the data could be plot using a fixed
step size, where the new data is being computed every :code:`dt` time. The
smaller this :code:`dt` value is, the more precise the plot. Below, the same
function has been plot twice, for :code:`dt = 0.5` and :code:`dt = 0.1`, where
the marked points on the plot highlight *when* the data has been recomputed. It
is clear from these plots that a smaller step size identifies more accurate
results.

.. figure:: ../_figures/stepsize/sine-5.png
   :width: 600

.. figure:: ../_figures/stepsize/sine-1.png
   :width: 600

This behaviour is the default in the simulator. This can be explicitly set as follows
(assuming :code:`sim` is the simulator object):

.. code-block:: python

   sim.setDeltaT(0.5)

This was done for academic reasons, as it is much easier to explain CBDs with a
fixed step size, as compared to varying step sizes. By default, the :code:`dt` is
set to 1.

Manipulating the Clock
----------------------
To maintain the block structure of the simulator, the simulation clock
(see :class:`CBD.lib.std.Clock`) is implemented as an actual block. If this clock is
not used in the model to simulate, the simulator will automatically add a fixed-rate
clock, given the :code:`dt` information, as explained above. This can also be done
manually via calling :func:`CBD.Core.CBD.addFixedRateClock`. The clock will actually
be used to compute the simulation time. The :class:`CBD.lib.std.TimeBlock` outputs the
current simulation time and can therefore be used to access the current time without
the need for the actual clock. However, blocks that depend on the :code:`dt` value
either need to be linked to a Clock block, or should have an input that yields the
correct value; i.e., a :class:`CBD.lib.std.ConstantBlock`.

Adaptive Step Size
------------------
Adaptive step size (or variable step size) is a simulation method in which the delta
changes throughout the simulation time. The clock-as-a-block structure allows the
variation of the :code:`dt`, as is required for adaptive step size. This can be done
manually by computing some simulation outputs, or via RK-preprocessing.

The :class:`CBD.preprocessing.rungekutta.RKPreprocessor` transforms the original CBD
model into a new block diagram that applies the Runge-Kutta algorithm with error
estimation. The full family of Runge-Kutta algorithms can be used as long as they are
representable in a Butcher tableau. Take a look at
:class:`CBD.preprocessing.butcher.ButcherTableau` to see which algorithms are automatically
included.

For instance, to apply the Runge-Kutta Fehlberg method for 4th and 5th order to ensure
adaptive step size of a CBD model called :code:`sinGen`, the following code can be used:

.. code-block:: python

   from CBD.preprocessing.butcher import ButcherTableau as BT
   from CBD.preprocessing.rungekutta import RKPreprocessor

   tableau = BT.RKF45()
   RKP = RKPreprocessor(tableau, atol=2e-5, hmin=0.1, safety=.84)
   newModel = RKP.preprocess(sinGen)

Notice how the :code:`preprocess` method returns a new model that must be used in the simulation.
Make sure to refer to this model when reading output traces or changing constants (see
:doc:`Dashboard`). Note that this will only work if one or more :code:`CBD.lib.std.IntegratorBlock`
are used (otherwise, the original model will be returned). To obtain a block from the original
model, the path :code:`RK.RK-K_0.block_name` should be used. However, it is perfectly possible there
are multiple variations in the transformed model. Hence, it is highly encouraged to only read data
from the output ports and not from blocks in the model itself. This is an unfortunate side-effect
of "transforming" the model to comply to adaptive step size simulation.
