Writing New ChemShell Modules
Overview
This sections provides an example of a piece of C code that
implements a new command within ChemShell interpreter.
Descriptions of the practical steps needed to link the file
may be found elsewhere
Organisation of ChemShell data objects
It will helpful when considering the example below to understand the
general scheme adopted for storing data.
- Base data for a given class of object is stored as a C structure,
currently defined types including the fragment, matrix, field and graph
structures. Pointer types are defined, with the convention that
the first letter is capitalised (Frag, Matrix, Field, Graph).
- These are organised by creating an object structure which contains
a pointer to the data, as well as information on the object type. A
pointer type is defined (Obj). Internally, ChemShell uses the object
type information to store a family of functions that act on the object,
(creation, deletion, i/o, copying etc).
- The objects in 2) are managed in ChemShell by organisation into a list,
each list entry has an identifying character string (tag) which is
used to refer to the object within a Tcl scripts. The pointer type
for the list entry is ObjList. In principle, each object (see 2 above) can
be stored in more than one list, a feature which allows heirarchical
organisation of the data using compound data objects.
At the present time however, all objects created are stored in a single list.
Access to an object is performed by referencing these structures in
reverse order; given a tag the ObjList structure can be obtained. A pointer
within this structure accesses the Obj structure. A field of this structure
identifies the structure containing the final data.
A simple example
A full example, a module which computes the sum of atomic numbers
of a molecule is given below.
#include <stdio.h>
#include "tcl.h"
#include "objects.h"
#include "chemsh.h"
#include "dutil.h"
#include "dfragment.h"
/*
GAMESS-UK interface functions
This file provides a routine to generate the atom/bq list
*/
int declare_gamess1(Tcl_Interp *interp)
{
int C_sum_atomic_numbers();
Tcl_CreateCommand(interp, "sum_atomic_numbers",C_sum_atomic_numbers,
(ClientData) "sumz", (Tcl_CmdDeleteProc *) NULL);
return 0;
}
/*
Helper routine for gamess interface, will output the
atoms or bqs in gamess format to the file pointer presented
in the argument list
*/
int C_sum_atomic_numbers(ClientData clientData,
Tcl_Interp *interp,
int argc,
char *argv[])
{
ObjList l;
int n, i, iz;
char sym[20];
double cx, cy, cz;
char *s;
Frag f;
char buff[20];
int sumz;
sumz=0;
GetVar(s,"coords");
l = get_objlist(s,"fragment",CHEMSH_OBJ_EXISTS,0);
if(!l){
printf("%s: problem with structure\n",argv[0]);
return TCL_ERROR;
}
f = (Frag) l->object->data;
n = FRAG_get_number_of_atoms(f);
for(i=1;i<=n;i++){
FRAG_get_atom(f,i,sym,&cx,&cy,&cz,&iz);
sumz += iz;
}
sprintf(buff,"%d",sumz);
Tcl_SetResult(interp, buff, TCL_VOLATILE);
rel_objlist(l);
return TCL_OK;
}
Include files
<stdio.h>
C standard library include files are referenced first.
<tcl.h>
The Tcl include file is needed for Tcl functions (in this
case Tcl_CreateCommand and Tcl_SetResult).
"chemsh.h"
Definition of some macros (e.g. GetVar), declaration of the
object list structure, and it's pointer type ObjList. Definition
of macros for various constants ( CHEMSH_OBJ_CREATE etc).
"objects.h"
Definition of the ChemShell generic object structure (struct object_struct)
and it's pointer type Obj.
"dutil.h"
Header file for a number of utility functions. This is the header for
the version dealing in double precision floating point quantities as
indicated by the leading d, the corresponding single precision version
s called "util.h"
This includes the definition of the structure for a floating point vector
(a component of many other structures).
"dfragment.h"
Header file for the fragment structure (which holds all or part
of a molecular structure). Declares the structure and the
interface functions.
declare_gamess1
This function uses the Tcl command Tcl_CreateCommand to add a command
to the Tcl interpreter. It is executed when ChemShell starts up. The
new command is implemented as a C routine, C_sum_atomic_numbers().
C_sum_atomic_numbers
This is the new function, the argument types are defined by the
header file "tcl.h" and will contain the arguments passed (in this
case no arguments are used).
GetVar
A Macro (from header file "chemsh.h") which accesses a Tcl variable
"coords" and returns a pointer to the contents, and error condition
occurs if the variable is not set.
It is assumed that the calling Tcl procedure has set the Tcl variable
"coords" to the tag referring to the molecular structure we want to
act on.
get_objlist
The character tag (in s) is used to access the corresponding
object list entry (returned as l). The 4 arguments are
char *tag the objects tag
char *type the object type
int status statis of object - valid settings:
CHEMSH_OBJ_CREATE | CHEMSH_OBJ_EXISTS
If the reference is to a pre-existing object, this must be
indicated, this will result in the data being read in
from a file (if necessary). A object specified with
CHEMSH_OBJ_CREATE will not be read in,
int persist An integer indicating if the data is to be retained after
all object list entries for the object have been
deleted, valid values:
CHEMSH_OBJ_PERSISTENT | CHEMSH_OBJ VOLATILE | 0
0 is to be used when accessing preexisting objects, and implies
that the new access does not change the persistence.
Accessing the fragment structure
Now that the object list entry (l) has been obtained, the pointer
to the data object itself (f) can be obtained. Note that a cast is
performed because objects of a number of different types are managed
via the generic object structure.
Once the pointer f has been obtained, functions from the corresponding
API can be used to perform the required functions.
f = (Frag) l->object->data;
n = FRAG_get_number_of_atoms(f);
for(i=1;i<=n;i++){
FRAG_get_atom(f,i,sym,&cx,&cy,&cz,&iz);
sumz += iz;
}
rel_objlist(l)
Releases the object list entry l, and there should be one such a call
for every get_objlist call. Once all list entry pointers to a given
object have been released, the memory occupied by the object is
freed. At this point, the contents of the memory are written to disk
if the object was created as persistent, or discarded if the object
was created as volatile.
|