6.2.5 Module System

The core of ViennaMOS’ module system is based on Qt’s plugin mechanism, providing convenient facilities to extend an application with additional functionality. Therefore, each module is implemented by a plugin, similar to the plugin system discussed with the component execution framework ViennaX (Section 5.3). Each module has access to the multiview facility, enabling direct access to the rendering backends. Although this approach introduces visualization responsibilities with a module, it enables a module to setup its own specific visualization needs by, for instance, applying a series of VTK algorithms to a quantity dataset prior to rendering the result.

Each module implementation has to adhere to a specific class interface, induced by the applied virtual plugin class hierarchy. The interface enables the individual module implementations to, for instance, provide general information on each module, such as the name, description, and version of the module. Also, a readiness method is used by the framework to evaluate whether all input dependencies of a module are satisfied. Each module is thus required to perform the required check routines defined by the module developer, for instance, testing whether the available data stored in the central database (Section 6.2.1) can be processed by the respective module. An update method enables to access a possibly updated state of the database triggered by the execution of other modules. For instance, another module stored new data into the database, therefore the update method allows for reevaluating whether the new data is suitable.

A reset method gives the module the opportunity to fallback into an initial, defined state. Such a mechanism is required to clear the module without reloading it. Furthermore, each module’s GUI provides an execution button which executes the module by calling the corresponding execution method. Usually, external simulation code is wrapped and executed in this method, thus this particular method contains implementations with potentially significant run times.

A quantity meta-information retrieval method enables the framework to access the information on the module’s visualization quantities, i.e., quantities which are copied by the module to the visualization backend as implemented by a developer and are thus available to the end user - via a drop-down widget containing the individual quantities - for selection. In this context, each module is responsible to transfer its result data, i.e., result quantities such as a potential distribution, into ViennaMOS’ visualization data storages - implemented by VTK objects - centrally governed by the multiview facility (Section 6.2.4). More concretely, this step requires copying data from module-specific data structures to VTK objects, such as a vtkTable. This is required as it is to be expected that the module wraps an external simulation tool, offering its own specialized data structure. Therefore, only the module has the knowledge - implemented by the module developer - about the utilized data structure and is thus in the unique position to transfer the data appropriately. The fact that the data transfer to the visualization backend is explicitly coded with respect to VTK, is an acceptable limitation, as the visualization backend is based on VTK. However, utilizing a different rendering backend, such as the Visualization and Computer Graphics library [172] would require a transfer to the corresponding native data structures. Providing access to different rendering backends would trigger further investigations regarding a generic visualization backend data transfer mechanism to further decouple the implementations and thus by extension the maintainability and extendability of ViennaMOS.

Coupled to this data transfer is a registration mechanism, meaning that each module registers the transferred quantity by providing the quantity name, the unit, the cell-level (cell-based or vertex-based data), the tensor-level, e.g., scalar field, and the module name. The latter enables the framework - or other modules - to associate the data with a specific module at a later point of the simulation process. In essence, the registration is based on a set of quantity registration objects, each holding the required meta-information for a specific quantity. The framework has access to this set via the module interface and can thus perform data-agnostic specializations, for instance the main GUI can therefore use a different icon for indicating vertex- or cell-based data. Also, this registration mechanism allows the rendering backend to determine the appropriate visualization technique, for instance, map the quantities on the mesh vertices (Section 6.2.2).

With respect to the module loading mechanism, Qt’s QPluginLoader class provides the essential mechanisms. Each module implementation is required - aside from sticking to the mentioned virtual class interface - to be a valid Qt object, i.e., a class which not only derives from QObject but also uses the Q_OBJECT macro in the class’s state, automatically generating boilerplate code required to, for instance, enable the class to utilize Qt’s signal/slot system. By adhering to this Qt-specific implementation guideline, no manual factory mechanism has to be implemented, contrary to the previously discussed component execution framework (Section 5.3.2).

As previously mentioned, each ViennaMOS module potentially provides its own GUI, being plugged into the framework’s main frontend upon the selection of the corresponding module (Figure 6.13). As all Qt GUI elements derive from Qt’s QWidget, forwarding a module-specific frontend from the module to the main GUI is straightforward. If indeed the module does not provide a frontend, an empty widget is used. Overall, typically each module provides two classes, one implementing the module interface and the other implementing the GUI, providing a QWidget object containing the GUI.


pict


Figure 6.13: Each module provides its own specific GUI being presented to the end user as part of the central ViennaMOS GUI.


A module can outsource computationally intensive parts to a separate thread by using Qt’s QThread facility. In essence, the computational intensive part is outsourced to a new worker object, modeling Qt’s Q_OBJECT concept. The new class is associated with a new QThread instance, execution controls, e.g., which method of the worker object has to be called when the thread starts up, are linked via Qt’s signal/slot mechanism, and finally the thread is executed (Figure 6.14). This approach can be further simplified by providing macros, as the required code is to a large extent boilerplate code.


pict


Figure 6.14: Computational intensive parts can be outsourced from the GUI thread to a separate thread, thus keeping the GUI responsive during extended computations. The computation part is moved to a new class (Worker), modeling Qt’s Q_OBJECT concept. The worker is then assigned to a new QThread and executed separately from the main GUI thread.