Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

Writing C/C++ based molecule file reader plugins for VMD

In addition to allowing extensions via the built-in mechanisms of the Tcl and Python languages, VMD also includes its own plugin interface for loading shared object files and dynamic link libraries written in C and C++.

Main plugin entry points

Compiled plugins work by providing a set of standard enry point functions that VMD is able to lookup via the operating system-specific dynamic linking functions. The three main entry points VMD requires from each plugin are: These three entry points are used to load, verify, initialize, and finalize access to a plugin. The register functions perform plugin/VMD version compatibility tests, initial linking of callbacks specific to the type and implementation of the plugin. The VMDPLUGIN_init function is called the first time the plugin is loaded by VMD, and before any other plugin routines or data are referenced, allowing the plugin to have an opportunity to setup any necessary data structures etc. The VMDPLUGIN_fini function similarly deallocates any internal plugin resources when a plugin is shutdown and unloaded.

An additional registration entry point is required for plugins which export new Tcl procedures:

Plugin registration in detail

Plugin registration is probably the main concept one needs to understand in order to fully grasp how the whole system works. The basic concept is identical to the methods commonly used in dynamically registering device drivers in operating system kernels. VMD implements a common interface API that all plugins conform to. The interface API consists of structures which are populated with the name and version information for a plugin, which type of plugin it is, and a set of type-specific function pointers which are either set to NULL (for unsupported features), or to an approprite function which VMD must call for a given operation.

Since VMD and the plugins it interfaces to must share common data structures used in the plugin API, one of the first things the plugin system does is to compare version numbers which are hard-coded into the header files that both the interface and the plugins are compiled against. Whenever the contents or layout of these common interface structures is changed, the version numbers in the header files are also updated. This gaurantees that the plugins and the plugin interface software are storing the same values in the same locations of these common structures, and that they agree on the semantics of these values. This is not quite enough to gaurantee that all compilers will pack these data structures in exactly the same way, but it does gaurantee that as long as one uses compilers that are otherwise compatible with each other to compile VMD and its plugins that they will work together.

Much like a device driver in Unix would register functions for operations such as open, close, read, write, and ioctl, a VMD plugin provides operations for open, close, read structure, write structure, read bonds, read coordinates, and various other operations which are specific to the type of plugin. Molecule file plugins support operations and function pointers which are oriented specifically towards manipulating files containing molecular structures and density data. Import graphics plugins on the other hand, only contain operations and functions for reading and writing purely geometric data such as triangles, spheres, and so on.

Plugin code development issues

In addition to the general structural requirements that all plugins must conform to, there are also several required code design practices which are required in order to achieve portability and reliable operation.

Plugins written in C++ should avoid using templates entirely. It is difficult to force templates to instantiate correctly in a form usable by a dynamically loadable plugin, particularly across multiple platforms. While it is perfectly reasonable to write code in C++, the code must be self-contained and thus written in a somewhat minimalistic style compared to normal C++ coding practices. In particular, anything that would normally be initialized by the C++ runtime library before main() would get called in a regular program will fail to operate correctly when used in a plugin, since there's no opportunity for the C++ runtime system to be invoked when a plugin is loaded by VMD. All initialization must happen within the register and init entry points, there can't be any dependencies on automatic initializations normally provided by the compiler that would be found in a regular program.

Another major issue associated with making the plugin interface reliable involves assuring that memory allocated by the plugin interface (VMD) is deallocated by the interface (VMD), and that memory allocated by a plugin is deallocated by the same plugin. This also ensures that compilers with incompatible memory allocation systems can be used to build plugins that VMD can still use effectively. This also avoids problems that can occur with the implementation of memory allocation with dynamic link libraries on Microsoft Windows with the same compiler, but with different runtime library settings.

Data structures, flags, and return codes used in molfile plugins

The key data structures used by molfile plugins when communicating with VMD are molfile_metadata_t, molfile_atom_t, molfile_timestep_t, molfile_volumetric_t, and molfile_graphics_t. Each of these structures is used with matching plugin entry points that operate on the appropriate type of data. These structures consist of elementary strings, integers, and floats, which are easily handled by code written in C, C++, Tcl, and presumably any other language one might want to use in writing a VMD plugin. Molecule data flags are stored as integers created by binary ORing several optional data availability flags together. The flags are used to indicate the presence of optional data fields, and to help VMD combine newly loaded data with any existing molecular structure already in VMD. Every plugin entry point is expected to return the appropriate status code based on the results of the attempted reading/writing operations. If no errors occur then MOLFILE_SUCCESS is returned. The symbolic constants MOLFILE_NUMATOMS_NONE and MOLFILE_NUMATOMS_UNKNOWN are used to improve code readability rather than returning 0 or -1 directly.

Atom naming conventions used by VMD

For various historical reasons, the atom and residue names VMD currently looks for when analyzing structures and generating representations are based on the atom and residue names assigned by the PDB. Current versions of VMD use PDB-like atom names to identify protein and nucleic acid residues, for secondary structure calculations using the STRIDE program (which takes input structures in PDB format), and for rendering Cartoon and Ribbon representations. The atom names that VMD currently looks for and their use are listed explicitly below:

Atom names used to identify protein (initial structure analysis):
  CA, C, O, N, OT1, OT2, O1, O2
Atom names used to identify nucleic acids (initial structure analysis):
  O1P, O2P, O3', C3', C4', C5', O5', O3*, C3*, C4*, C5*, O5*

Atom names use when building Cartoon/Ribbon reps:
  CA, O, OT1, P

Residue names used to identify water:

Residue names used for drawing nucleic acids:

Example: AMBER Parm reader plugin

The AMBER Parm reader parmplugin.C is a plugin for reading structure information (only bond information in this case) into VMD. Private data is stored in a parmdata structure, consisting of the file handle, number of atoms, bond arrays, and a pointer to a ReadPARM structure used by lower level reader code. As with many plugins, the Parm reader plugin was developed using an older generation of VMD molecule file reader code as the starting point. Rather than rewriting the old code from scratch, the plugin simply wraps the old routines which were well tested and battle proven, with the necessary glue code to make them work within the appropriate molfile plugin entry points. The entry points implemented in the Parm reader are open_parm_read(), read_parm_structure(), read_parm_bonds(), and close_parm_read(). The open_parm_read() procedure opens the file, allocates the parmdata private data handle, and reads metadata, such as the number of atoms, needed by the subsequent structure reading functions. The read_parm_structure() procedure loops over all atoms reading in the atom names, types, charges, masses, and so on by calling the functions implemented by the older Parm reader code in ReadPARM.h. Bond lists (1-based indexing) are returned by the read_parm_bonds() procedure. Since the Parm reader plugin doesn't provide bond order information, the bond order pointer is set to NULL. The close_parm_read() procedure frees memory and closes the open Parm file handle, destroying all resources used by the file reading context.

Example: Situs volumetric grid reader plugin

The Situs plugin situsplugin.C is a relatively simple example of how one can write a volumetric grid reader plugin. The situs_t structure contains private data needed in parsing and interpreting the contents of the Situs grid file. The open_situs_read() procedure opens the Situs grid, saves the file handle and some elementary metadata about the file in the situs_t handle, and returns the situs_t handle for use in subsequent calls reading the the volumetric data contained in the file. The read_situs_metadata() routine simply reuses the contents of the situs_t structure which was populated when the file was first opened, returning the number of datasets contained in the file and a list of their associated volumetric metadata such as grid size, origin, etc. The read_situs_data() procedure loads the Situs grid data into the provided datablock pointer. As you may have already guessed, the close_situs_read() procedure closes the open file handles, frees all memory allocation in the private situs_t handle, returning all resources used in that file reading context back to the operating system. Since the Situs plugin doesn't read any atomic information, all of the function pointers related to atomic data are left NULL by the VMDPLUGIN_register() in situsplugin.C.

plg_molfile.dox,v 1.6 2008/03/31 19:40:26 johns Exp

Generated on Tue Apr 23 04:49:03 2024 for VMD Plugins (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002