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.
  1. 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).
  2. 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).
  3. 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.




This manual was generated using htp, an HTML pre-processor Powered by htp