PowerDEVS - A C++ Implementation of the DEVS Formalism
As mentioned in the
introductory section, PowerDEVS served as kind of a template for ModelicaDEVS: although the simulator of ModelicaDEVS is fundamentally different the components of ModelicaDEVS have been built according to the blocks in the PowerDEVS library.
A short but accurate description of the software's main achievements is given in the abstract of "PowerDEVS: A DEVS-Based Environment for Hybrid System Modeling and Simulation" [Kofman03]: "PowerDEVS allows defining atomic DEVS models in C++ language which can be then graphically coupled in hierarchical block diagrams to create more complex systems. Both, atomic an coupled models, can be organized in libraries which facilitate the reusability features. The environment automatically translates the graphically coupled models into a C++ code which executes the simulation."
This section gives a brief introduction into the simulation mechanism of PowerDEVS. For more details you may also download the PowerDEVS code/software from http://www.fceia.unr.edu.ar/lsd/powerdevs/ .
The Modelling Environment
Like many other simulation software systems, PowerDEVS provides a graphical editor where models can be built graphically. Note that it would be possible to declare them directly in C++ code. This however would be rather cumbersome and error-prone, and using the automatic transformation of PowerDEVS is recommended.
Since the handling of the modelling environment is fairly intuitive and anyway not critical to the understanding of the simulator, it is not explained in greater detail here.
An interesting subject to dwell some more on, however, is the way the graphical representation of a model is transformed into C++ code, in order to be utilisable by the simulator framework.
Assume the sample model in the obove figure. When the "run" button in the modelling environment is pressed, PowerDEVS creates a file called "model.h" that holds a C++ description of the model. All information that will be needed for the simulation later on is stored in this file.
The following code corresponds to the model in the figure above, but for simplicity, the output components "ToDisk1", "ToDisk2", "ToDisk3" and "QuickScope1" have been removed:
As a last point worth mentioning, there is the incorporation of the tie-breaking function by the so called Priority Window (Accessible through the menu Edit, Priorities), which allows the modeller to specify a particular priority order among the components:
The Simulator
The PowerDEVS simulator framework enables the user to simulate a system that is available in the C++ format shown before. Normally, this is the result of modelling it graphically in the PowerDEVS graphical editor followed by an automatic transformation into the aforementioned C++ data structures.
The subsequent sections will give insight into the simulator architecture both on a more theoretical level as well as on the level of its actual implementation in C++.
Simulator Theory
The PowerDEVS simulator is based on the abstract simulator concept developed by Zeigler [Zeigler84]. To make allowance for the possibility of coupled/hierarchical DEVS systems, PowerDEVS models embody the structure shown in the following figure, whereas simulators represent atomic models and coordinators represent coupled models.
As soon as there are two or more components constituting the model, it is necessary to make them able to communicate with each other (send and receive output/input signals). The most intuitive approach would probably be the following: each block keeps a list of the blocks that are connected to its output port, and whenever the block produces an output event, it sends a message to the connected blocks in order to make them undergo their external transition. It is however not the approach chosen in Zeigler's abstract simulator/PowerDEVS. PowerDEVS rather leaves the control of the interaction within a set of blocks to the coordinator that is declared to be responsible for these particular blocks. If for example block A has to generate an output event that has to be sent to block B, it is the coordinator that a) sends a message (called "lambdamessage" in PowerDEVS) to block A in order to trigger the λ-function and b) passes the generated output event to the block B. Of course, both approaches (the intuitive one as well as the one applied in PowerDEVS) lead to the same result. Thus, a modeller does not have to think of the complicated coordinator-simulator concept but may consider the blocks to act autonomously ("block A decides to produce an event and sends it to block B").
Let us study the PowerDEVS solution to the component interaction issue in more detail. The figure above depicts the possible hierarchical structure and the corresponding simulation scheme of a DEVS model. The messages between coordinators and simulators up and down the tree consist of the following two types:
Down-messages: coordinators send messages to their children, triggering the execution of the different functions (δint, δext, λ-function).
Up-messages: when the λ-function of a simulator has been called by the coordinator, it returns the output value to its parent coordinator (the caller).
Note that coordinators look like simple simulators to their parent coordinators. Hence, if a coordinator (e.g. coupled1 in the figure above) receives an output value that has to be propagated outside the corresponding coupled model, it sends this value to its own parent coordinator (coupled2).
We can see that simulators do not interact directly with other simulators on the same layer, but only pass their outputs to the parent coordinator which then is responsible for the propagation of the signal to the appropriate simulators (i.e. those which are connected to the "firing" simulator). The communication between simulators can thus be said to be monitored by the associated coordinator.
Since both coordinators and simulators have to be valid DEVS models, they have to feature the typical DEVS functions: δint, δext, the time-advance and the λ-function. In simulators, these functions simply define the inherent behaviour of the block in question. In the case of a coordinator however, they have a slightly different meaning/effect:
External transition - the purpose of the external transition of a coordinator is to invoke external transitions in the appropriate simulators:
When a coordinator receives a signal, it simply forwards it to those among its children that have their input ports connected to the input ports of the coordinator (see the following figure), and thereby triggers these simulators to execute their external function.
Internal transition -- the purpose of the internal transition of a coordinator is to invoke internal transitions in the appropriate simulators: All simulators and coordinators have a local variable tn which indicates the time when their next internal transition will take place. In a simulator, this variable corresponds simply to the sum of the current time and the value of the time-advance function (or σ). The value of tn in a coordinator on the other hand is the minimum of the tn values of its associated components, thus indicating the next time instant when a simulator undergoes its internal transition. Besides the time when the transition is executed, the coordinator also stores the information where (at which simulator) it takes place. For this reason, it features a second variable dast (corresponding to d*) where it stores the ID of the respective simulator. Having this information at its disposal, the internal transition of a coordinator just consists of sending an internal-transition-message (called "dintmessage" in PowerDEVS) to the simulator stored in dast, which then will execute its own internal transition.
Note that in order to always have an updated pair dast/tn available, a coordinator scans its children every time it receives the instruction to execute an internal or external transition, and stores the block with the smallest time-advance into dast and the corresponding time-advance value into tn.
Let us investigate now, how a signal moves through the tree of coordinators and simulators. Consider a hierarchy like the one in the subsequent figure, which has the same topology as the example above, but with directed connections).
Suppose that coupled2 has just noticed that atomic3 is ready to undergo its internal transition (apparently, it has the smallest tn value). For this reason, coupled2 sends a message to atomic3, which then executes its internal transition. Instead of sending the output directly to coupled1 (which looks like another simulator to atomic3), it returns it to its father coordinator, coupled2. coupled2 checks now what components are attached to the output port of atomic3 and finds coupled1, to which it propagates the output of atomic3. coupled1 has now just received an external event that makes it execute an external transition. Since coupled1 is a coordinator, this transition just consists of forwarding the signal to the simulators connected to its input ports. In our example, this is only atomic1, which now executes its external transition and thereby sets its local variable tn to a new value. As mentioned before, when a coordinator (coupled1 in this case) forwards an external event, it scans the tn values of its children in order to update dast and its own tn value. Note that this update already involves the new tn value of atomic1. The next step depends now on whether atomic1 or atomic2 has the smaller tn value and therefore is the imminent component to execute its internal transition.
So much for the theoretical background of the PowerDEVS model simulator. When comparing this simulation scheme to the way ModelicaDEVS works, it can be easily seen that there are several fundamental differences between the two simulator concepts.