| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- 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.
- .. note::
- Runge-Kutta preprocessing is only available if there are one or more instances of
- :class:`CBD.lib.std.IntegratorBlock` in the original model. Also make sure not to
- use a flattened model to prevent errors.
- 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
- # Add a clock to the model, or RK will not work, 1e-4 is the starting delta
- sinGen.addFixedRateClock("clock", 1e-4)
- tableau = BT.RKF45()
- RKP = RKPreprocessor(tableau, atol=2e-5, hmin=0.1, safety=.84)
- newModel = RKP.preprocess(oldModel)
- .. warning::
- 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 also
- :doc:`Dashboard`).
- .. warning::
- To obtain a block from the original model, the path :code:`RK.RK-K_0.block_name` could be used. However,
- because of the way RK works, it is perfectly possible there are multiple copies in the transformed model.
- It is discouraged to use the internal blocks for signal information. Therefore, please 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.
|