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

psfplugin.c

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr
00003  *cr            (C) Copyright 1995-2016 The Board of Trustees of the
00004  *cr                        University of Illinois
00005  *cr                         All Rights Reserved
00006  *cr
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  * RCS INFORMATION:
00011  *
00012  *      $RCSfile: psfplugin.c,v $
00013  *      $Author: johns $       $Locker:  $             $State: Exp $
00014  *      $Revision: 1.84 $       $Date: 2019/09/17 00:59:04 $
00015  *
00016  ***************************************************************************/
00017 
00018 #include "molfile_plugin.h"
00019 
00020 #include <stdio.h>
00021 #include <string.h>
00022 #include <stdlib.h>
00023 #include <ctype.h>
00024 
00025 #include "fortread.h"
00026 
00027 #define PSF_RECORD_LENGTH 256  /* extended to handle Charmm CMAP/CHEQ/DRUDE */
00028 
00029 typedef struct {
00030   FILE *fp;
00031   int numatoms;
00032   int namdfmt;     /* NAMD-specific PSF file                                */
00033   int charmmfmt;   /* whether psf was written in charmm format              */
00034   int charmmcmap;  /* cross-term maps                                       */
00035   int charmmcheq;  /* stuff used by charmm for polarizable force fields     */
00036   int charmmext;   /* flag used by charmm for IOFOrmat EXTEnded             */
00037   int charmmdrude; /* flag used by charmm for Drude polarizable force field */
00038   int nbonds;
00039   int *from, *to;
00040   int numangles, *angles;
00041   int numdihedrals, *dihedrals;
00042   int numimpropers, *impropers;
00043   int numcterms, *cterms;
00044 } psfdata;
00045 
00046 
00047 /* Formatted reads:
00048  *
00049  * copy at most 'maxlen' characters from source to target allowing overflow.
00050  *
00051  * leading white space up to 'len' is skipped over but counts towards 'maxlen'.
00052  * the copy stops at first whitspace or a '\0'.
00053  * unlike strncpy(3) the result will always be \0 terminated.
00054  *
00055  * intended for copying (short) strings from formatted fortran
00056  * i/o files that must not contain whitespace (e.g. residue names,
00057  * atom name/types etc. in .pdb, .psf and alike.).
00058  *
00059  * returns number of bytes of overflow.
00060  */
00061 static int strnwscpy_shift(char *target, const char *source,
00062                            const int len, const int maxlen) {
00063   int i, c;
00064 
00065   for (i=0, c=0; i<maxlen; ++i) {
00066     if (*source == '\0' || (c > 0 && *source == ' ') || (c == 0 && i == len)) {
00067       break;
00068     }
00069 
00070     if (*source == ' ') {
00071       source++;
00072     } else {
00073       *target++ = *source++;
00074       c++;
00075     }
00076   }
00077   *target = '\0';
00078   return ( i > len ? i - len : 0 );
00079 }
00080 
00081 /* atoi() replacement
00082  *
00083  * reads int with field width fw handling various overflow cases to
00084  * support both " %7d %7d" and "%8d%8d" writers up to 100M atoms.
00085  *
00086  */
00087 
00088 static int atoifw(char **ptr, int fw) {
00089   char *op = *ptr;
00090   int ival = 0;
00091   int iws = 0;
00092   char tmpc;
00093 
00094   sscanf(op, "%d%n", &ival, &iws);
00095   if ( iws == fw ) { /* "12345678 123..." or " 1234567 123..." */
00096     *ptr += iws;
00097   } else if ( iws < fw ) { /* left justified? */
00098     while ( iws < fw && op[iws] == ' ' ) ++iws;
00099     *ptr += iws;
00100   } else if ( iws < 2*fw ) { /* " 12345678 123..." */
00101     *ptr += iws;
00102   } else { /* " 123456712345678" or "1234567812345678" */
00103     tmpc = op[fw];  op[fw] = '\0';
00104     ival = atoi(op);
00105     op[fw] = tmpc;
00106     *ptr += fw;
00107   }
00108   return ival;
00109 }
00110 
00111 
00112 /* Read in the next atom info into the given storage areas; this assumes
00113    that file has already been moved to the beginning of the atom records.
00114    Returns the serial number of the atom. If there is an error, returns -1.*/
00115 static int get_psf_atom(FILE *f, char *name, char *atype, char *resname,
00116                         char *segname, int *resid, char *insertion, float *q, float *m, 
00117                         int namdfmt, int charmmext, int charmmdrude) {
00118   char inbuf[PSF_RECORD_LENGTH+2];
00119   int num;
00120 
00121   if (inbuf != fgets(inbuf, PSF_RECORD_LENGTH+1, f)) {
00122     return(-1); /* failed to read in an atom */
00123   }
00124 
00125   if (strlen(inbuf) < 50) {
00126     fprintf(stderr, "Line too short in psf file: \n%s\n", inbuf);
00127     return -1;
00128   }
00129 
00130   num = atoi(inbuf); /* atom index */
00131 
00132   if (namdfmt == 1) {
00133     int cnt, rcnt;
00134     char residstr[12], trash;
00135     cnt = sscanf(inbuf, "%d %7s %10s %7s %7s %7s %f %f",
00136                  &num, segname, residstr, resname, name, atype, q, m);
00137     insertion[0] = ' ';  insertion[1] = '\0';
00138     rcnt = sscanf(residstr, "%d%c%c", resid, insertion, &trash);
00139     if (cnt != 8 || rcnt < 1 || rcnt > 2) {
00140       printf("psfplugin) Failed to parse atom line in NAMD PSF file:\n");
00141       printf("psfplugin)   '%s'\n", inbuf);
00142       return -1;
00143     }
00144   } else if (charmmdrude == 1 || charmmext == 1) {
00145     int xplorshift;
00146     /* CHARMM PSF format is (if DRUDE or (?) CHEQ are enabled):
00147      *  '(I10,1X,A8,1X,A8,1X,A8,1X,A8,1X,I4,1X,2G14.6,I8,2G14.6)'
00148      */
00149     if ( inbuf[10] != ' ' ||
00150          inbuf[19] != ' ' ||
00151          inbuf[28] != ' ' ||
00152          inbuf[37] != ' ' ||
00153          inbuf[46] != ' ' ) {
00154       printf("psfplugin) Failed to parse atom line in PSF file:\n");
00155       printf("psfplugin)   '%s'\n", inbuf);
00156       return -1;
00157     }
00158 
00159     strnwscpy(segname, inbuf+11, 7);
00160     strnwscpy(resname, inbuf+29, 7);
00161     strnwscpy(name, inbuf+38, 7);
00162 
00163     xplorshift = 0;
00164     strnwscpy(atype, inbuf+47, 4);
00165     if ( ! isdigit(atype[0]) ) {
00166       strnwscpy(atype, inbuf+47, 6);
00167       xplorshift = 2;
00168     }
00169 
00170     if ( inbuf[51+xplorshift] != ' ' ) {
00171       printf("psfplugin) Failed to parse atom line in PSF file:\n");
00172       printf("psfplugin)   '%s'\n", inbuf);
00173       return -1;
00174     }
00175     
00176     insertion[0] = ' ';  insertion[1] = '\0';
00177     sscanf(inbuf+20, "%d%c", resid, insertion);
00178     *q = (float) atof(inbuf+52+xplorshift);
00179     *m = (float) atof(inbuf+66+xplorshift);
00180 
00181     /* data we don't currently read:
00182      * if (charmmdrude == 1) {
00183      *   *imove = atoi(inbuf+80+xplorshift);
00184      *   *alphadp = atof(inbuf+88+xplorshift);
00185      *   *tholei = atof(inbuf+102+xplorshift);
00186      * }
00187      */
00188   } else {
00189     /* CHARMM PSF format is 
00190      *  '(I8,1X,A4,1X,A4,1X,A4,1X,A4,1X,I4,1X,2G14.6,I8)'
00191      */
00192     const char *rdbuf = inbuf;
00193     char intbuf[16];
00194 
00195     intbuf[0] = '\0';
00196     rdbuf += strnwscpy_shift(intbuf, rdbuf, 8, 10);
00197     if ( rdbuf[8] != ' ' ) {
00198       printf("psfplugin) Failed to parse atom index in PSF file:\n");
00199       printf("psfplugin)   '%s'\n", inbuf);
00200       return -1;
00201     }
00202     rdbuf += strnwscpy_shift(segname, rdbuf+9, 4, 7);
00203     if ( rdbuf[13] != ' ' ) {
00204       printf("psfplugin) Failed to parse segname in PSF file:\n");
00205       printf("psfplugin)   '%s'\n", inbuf);
00206       return -1;
00207     }
00208     intbuf[0] = '\0';
00209     rdbuf += strnwscpy_shift(intbuf, rdbuf+14, 4, 8);
00210     insertion[0] = ' ';  insertion[1] = '\0';
00211     sscanf(intbuf, "%d%c", resid, insertion);
00212     if ( rdbuf[18] != ' ' ) {
00213       printf("psfplugin) Failed to parse resid in PSF file:\n");
00214       printf("psfplugin)   '%s'\n", inbuf);
00215       return -1;
00216     }
00217     rdbuf += strnwscpy_shift(resname, rdbuf+19, 4, 7);
00218     if ( rdbuf[23] != ' ' ) {
00219       printf("psfplugin) Failed to parse resname in PSF file:\n");
00220       printf("psfplugin)   '%s'\n", inbuf);
00221       return -1;
00222     }
00223     rdbuf += strnwscpy_shift(name, rdbuf+24, 4, 7);
00224     if ( rdbuf[28] != ' ' ) {
00225       printf("psfplugin) Failed to parse atom name in PSF file:\n");
00226       printf("psfplugin)   '%s'\n", inbuf);
00227       return -1;
00228     }
00229     rdbuf += strnwscpy_shift(atype, rdbuf+29, 4, 7);
00230     if ( rdbuf[33] != ' ' ) {
00231       printf("psfplugin) Failed to parse atom type in PSF file:\n");
00232       printf("psfplugin)   '%s'\n", inbuf);
00233       return -1;
00234     }
00235     *q = (float) atof(rdbuf+34);
00236     *m = (float) atof(rdbuf+48);
00237   }
00238 
00239 #if 0
00240   /* if this is a Charmm31 PSF file, there may be two extra */
00241   /* columns containing polarizable force field data.       */
00242   if (psf->charmmcheq) {
00243     /* do something to read in these columns here */
00244   }
00245 #endif
00246 
00247   return num;
00248 }
00249 
00250 
00251 /*
00252  * Read in the beginning of the bond/angle/dihed/etc information,
00253  * but don't read in the data itself.  Returns the number of the record type
00254  * for the molecule.  If error, returns (-1). 
00255  */
00256 static int psf_start_block(FILE *file, const char *blockname) {
00257   char inbuf[PSF_RECORD_LENGTH+2];
00258   int nrec = -1;
00259   
00260   /* check if we had a parse error earlier, which is indicated
00261      by the file descriptor set to NULL */
00262   if (!file)
00263     return -1;
00264 
00265   /* keep reading the next line until a line with blockname appears */
00266   do {
00267     if(inbuf != fgets(inbuf, PSF_RECORD_LENGTH+1, file)) {
00268       /* EOF encountered with no blockname line found ==> error, return (-1) */
00269       return -1;
00270     }
00271     if(strlen(inbuf) > 0 && strstr(inbuf, blockname))
00272       nrec = atoi(inbuf);
00273   } while (nrec == -1);
00274 
00275   return nrec;
00276 }
00277 
00278 
00279 /* Read in the bond info into the given integer arrays, one for 'from' and
00280    one for 'to' atoms; remember that .psf files use 1-based indices,
00281    not 0-based.  Returns 1 if all nbond bonds found; 0 otherwise.  */
00282 static int psf_get_bonds(FILE *f, int nbond, int fromAtom[], int toAtom[], int charmmext, int namdfmt) {
00283   char *bondptr=NULL;
00284   int fw = charmmext ? 10 : 8;
00285   char inbuf[PSF_RECORD_LENGTH+2];
00286   int i=0;
00287   size_t minlinesize;
00288   int rc=0;
00289 
00290   while (i < nbond) {
00291     if (namdfmt) {
00292       /* NAMD assumes a space-delimited variant of the PSF file format */
00293       int cnt = fscanf(f, "%d %d", &fromAtom[i], &toAtom[i]);
00294       if (cnt < 2) {
00295         fprintf(stderr, "Bonds line too short in NAMD psf file.\n");
00296         break;
00297       }
00298     } else {
00299       if ((i % 4) == 0) {
00300         /* must read next line */
00301         if (!fgets(inbuf, PSF_RECORD_LENGTH+2, f)) {
00302           /* early EOF encountered */
00303           break;
00304         }
00305 
00306         /* Check that there is enough space in the line we are about to read */
00307         if (nbond-i >= 4) {
00308           minlinesize = 2*fw*4; 
00309         } else {
00310           minlinesize = 2*fw*(nbond-i); 
00311         }
00312 
00313         if (strlen(inbuf) < minlinesize) {
00314           fprintf(stderr, "Bonds line too short in psf file: \n%s\n", inbuf);
00315           break;
00316         }
00317         bondptr = inbuf;
00318       }
00319 
00320       if ((fromAtom[i] = atoifw(&bondptr,fw)) < 1) {
00321         printf("psfplugin) ERROR: Bond %d references atom with index < 1!\n", i);
00322         rc=-1;
00323         break;
00324       }
00325   
00326       if ((toAtom[i] = atoifw(&bondptr,fw)) < 1) {
00327         printf("psfplugin) ERROR: Bond %d references atom with index < 1!\n", i);
00328         rc=-1;
00329         break;
00330       }
00331     }
00332 
00333     i++;
00334   }
00335 
00336   if (rc == -1) {
00337     printf("psfplugin) ERROR: skipping bond info due to bad atom indices\n");
00338   } else if (i != nbond) {
00339     printf("psfplugin) ERROR: unable to read the specified number of bonds!\n");
00340     printf("psfplugin) Expected %d bonds but only read %d\n", nbond, i);
00341   }
00342 
00343   return (i == nbond);
00344 }
00345 
00346 
00347 /*
00348  * API functions
00349  */
00350 
00351 static void *open_psf_read(const char *path, const char *filetype, 
00352     int *natoms) {
00353   FILE *fp;
00354   char inbuf[PSF_RECORD_LENGTH*8+2];
00355   psfdata *psf;
00356   const char *progname = "Charmm";
00357   
00358   /* Open the .psf file and skip past the remarks to the first data section.
00359    * Returns the file pointer, or NULL if error.  Also puts the number of
00360    * atoms in the molecule into the given integer.  
00361    */
00362   if (!path)
00363     return NULL;
00364 
00365   if ((fp = fopen(path, "r")) == NULL) {
00366     fprintf(stderr, "Couldn't open psf file %s\n", path);
00367     return NULL;
00368   }
00369 
00370   *natoms = MOLFILE_NUMATOMS_NONE; /* initialize to none */
00371 
00372   psf = (psfdata *) malloc(sizeof(psfdata));
00373   memset(psf, 0, sizeof(psfdata));
00374   psf->fp = fp;
00375   psf->namdfmt = 0;   /* off unless we discover otherwise */
00376   psf->charmmfmt = 0; /* off unless we discover otherwise */
00377   psf->charmmext = 0; /* off unless we discover otherwise */
00378 
00379   /* read lines until a line with NATOM and without REMARKS appears    */
00380   do {
00381     /* be prepared for long lines from CNS remarks */
00382     if (inbuf != fgets(inbuf, PSF_RECORD_LENGTH*8+1, fp)) {
00383       /* EOF encountered with no NATOM line found ==> error, return null */
00384       *natoms = MOLFILE_NUMATOMS_NONE;
00385       fclose(fp);
00386       free(psf);
00387       return NULL;
00388     }
00389 
00390     if (strlen(inbuf) > 0) {
00391       if (!strstr(inbuf, "REMARKS")) {
00392         if (strstr(inbuf, "PSF")) {
00393           if (strstr(inbuf, "NAMD")) {
00394             psf->namdfmt = 1;      
00395           }
00396           if (strstr(inbuf, "EXT")) {
00397             psf->charmmfmt = 1; 
00398             psf->charmmext = 1;      
00399           }
00400           if (strstr(inbuf, "CHEQ")) {
00401             psf->charmmfmt = 1; 
00402             psf->charmmcheq = 1;      
00403           }
00404           if (strstr(inbuf, "CMAP")) {
00405             psf->charmmfmt = 1; 
00406             psf->charmmcmap = 1;      
00407           }
00408           if (strstr(inbuf, "DRUDE")) {
00409             psf->charmmfmt = 1; 
00410             psf->charmmdrude = 1;      
00411           }
00412         } else if (strstr(inbuf, "NATOM")) {
00413           *natoms = atoi(inbuf);
00414         }
00415       } 
00416     }
00417   } while (*natoms == MOLFILE_NUMATOMS_NONE);
00418 
00419   if (psf->namdfmt) {
00420     progname = "NAMD";
00421   } else {
00422     progname = "Charmm";
00423   }
00424   if (psf->charmmcheq || psf->charmmcmap) {
00425     printf("psfplugin) Detected a %s PSF file\n", progname);
00426   }
00427   if (psf->charmmext) {
00428     printf("psfplugin) Detected a %s PSF EXTEnded file\n", progname);
00429   }
00430   if (psf->charmmdrude) {
00431     printf("psfplugin) Detected a %s Drude polarizable force field file\n", progname);
00432     printf("psfplugin) WARNING: Support for Drude FF is currently experimental\n");
00433   }
00434 
00435   psf->numatoms = *natoms;
00436 
00437   return psf;
00438 }
00439 
00440 static int read_psf(void *v, int *optflags, molfile_atom_t *atoms) {
00441   psfdata *psf = (psfdata *)v;
00442   int i;
00443   
00444   /* we read in the optional mass and charge data */
00445   *optflags = MOLFILE_INSERTION | MOLFILE_MASS | MOLFILE_CHARGE;
00446 
00447   for (i=0; i<psf->numatoms; i++) {
00448     molfile_atom_t *atom = atoms+i; 
00449     if (get_psf_atom(psf->fp, atom->name, atom->type, 
00450                      atom->resname, atom->segid,
00451                      &atom->resid, atom->insertion, &atom->charge, &atom->mass, 
00452                      psf->namdfmt, psf->charmmext, psf->charmmdrude) < 0) {
00453       fprintf(stderr, "couldn't read atom %d\n", i);
00454       fclose(psf->fp);
00455       psf->fp = NULL;
00456       return MOLFILE_ERROR;
00457     }
00458     atom->chain[0] = atom->segid[0];
00459     atom->chain[1] = '\0';
00460   }
00461 
00462   return MOLFILE_SUCCESS;
00463 }
00464 
00465 
00466 static int read_bonds(void *v, int *nbonds, int **fromptr, int **toptr, 
00467                       float **bondorder, int **bondtype, 
00468                       int *nbondtypes, char ***bondtypename) {
00469   psfdata *psf = (psfdata *)v;
00470 
00471   *nbonds = psf_start_block(psf->fp, "NBOND"); /* get bond count */
00472 
00473   if (*nbonds > 0) {
00474     psf->from = (int *) malloc(*nbonds*sizeof(int));
00475     psf->to = (int *) malloc(*nbonds*sizeof(int));
00476 
00477     if (!psf_get_bonds(psf->fp, *nbonds, psf->from, psf->to, 
00478                        psf->charmmext, psf->namdfmt)) {
00479       fclose(psf->fp);
00480       psf->fp = NULL;
00481       return MOLFILE_ERROR;
00482     }
00483     *fromptr = psf->from;
00484     *toptr = psf->to;
00485     *bondorder = NULL; /* PSF files don't provide bond order or type information */
00486     *bondtype = NULL;
00487     *nbondtypes = 0;
00488     *bondtypename = NULL;
00489   } else {
00490     *fromptr = NULL;
00491     *toptr = NULL;
00492     *bondorder = NULL; /* PSF files don't provide bond order or type information */
00493     *bondtype = NULL;
00494     *nbondtypes = 0;
00495     *bondtypename = NULL;
00496     printf("psfplugin) WARNING: no bonds defined in PSF file.\n");
00497   }
00498 
00499   return MOLFILE_SUCCESS;
00500 }
00501 
00502 
00503 static int psf_get_angles(FILE *f, int n, int *angles, int charmmext) {
00504   char inbuf[PSF_RECORD_LENGTH+2];
00505   char *bondptr = NULL;
00506   int fw = charmmext ? 10 : 8;
00507   int i=0;
00508   while (i<n) {
00509     if((i % 3) == 0) {
00510       /* must read next line */
00511       if(!fgets(inbuf,PSF_RECORD_LENGTH+2,f)) {
00512         /* early EOF encountered */
00513         break;
00514       }
00515       bondptr = inbuf;
00516     }
00517     if((angles[3*i] = atoifw(&bondptr,fw)) < 1)
00518       break;
00519     if((angles[3*i+1] = atoifw(&bondptr,fw)) < 1)
00520       break;
00521     if((angles[3*i+2] = atoifw(&bondptr,fw)) < 1)
00522       break;
00523     i++;
00524   }
00525 
00526   return (i != n);
00527 }
00528 
00529 
00530 static int psf_get_dihedrals_impropers(FILE *f, int n, int *dihedrals, int charmmext) {
00531   char inbuf[PSF_RECORD_LENGTH+2];
00532   char *bondptr = NULL;
00533   int fw = charmmext ? 10 : 8;
00534   int i=0;
00535   while (i<n) {
00536     if((i % 2) == 0) {
00537       /* must read next line */
00538       if(!fgets(inbuf,PSF_RECORD_LENGTH+2,f)) {
00539         /* early EOF encountered */
00540         break;
00541       }
00542       bondptr = inbuf;
00543     }
00544     if((dihedrals[4*i] = atoifw(&bondptr,fw)) < 1)
00545       break;
00546     if((dihedrals[4*i+1] = atoifw(&bondptr,fw)) < 1)
00547       break;
00548     if((dihedrals[4*i+2] = atoifw(&bondptr,fw)) < 1)
00549       break;
00550     if((dihedrals[4*i+3] = atoifw(&bondptr,fw)) < 1)
00551       break;
00552     i++;
00553   }
00554 
00555   return (i != n);
00556 }
00557 
00558 #if vmdplugin_ABIVERSION > 14
00559 static int read_angles(void *v, int *numangles, int **angles, 
00560                        int **angletypes, int *numangletypes, 
00561                        char ***angletypenames, int *numdihedrals,
00562                        int **dihedrals, int **dihedraltypes, 
00563                        int *numdihedraltypes, char ***dihedraltypenames,
00564                        int *numimpropers, int **impropers, 
00565                        int **impropertypes, int *numimpropertypes, 
00566                        char ***impropertypenames, int *numcterms, 
00567                        int **cterms, int *ctermcols, int *ctermrows) {
00568   psfdata *psf = (psfdata *)v;
00569 
00570   /* initialize data to zero */
00571   *numangles         = 0;
00572   *angles            = NULL;
00573   *angletypes        = NULL;
00574   *numangletypes     = 0;
00575   *angletypenames    = NULL;
00576   *numdihedrals      = 0;
00577   *dihedrals         = NULL;
00578   *dihedraltypes     = NULL;
00579   *numdihedraltypes  = 0;
00580   *dihedraltypenames = NULL;
00581   *numimpropers      = 0;
00582   *impropers         = NULL;
00583   *impropertypes     = NULL;
00584   *numimpropertypes  = 0;
00585   *impropertypenames = NULL;
00586   *numcterms         = 0;
00587   *cterms            = NULL;
00588   *ctermrows         = 0;
00589   *ctermcols         = 0;
00590 
00591   psf->numangles    = psf_start_block(psf->fp, "NTHETA"); /* get angle count */
00592   if (psf->numangles > 0) {
00593     psf->angles = (int *) malloc(3*psf->numangles*sizeof(int));
00594     psf_get_angles(psf->fp, psf->numangles, psf->angles, psf->charmmext);
00595   } else {
00596     printf("psfplugin) WARNING: no angles defined in PSF file.\n");
00597   }
00598  
00599   psf->numdihedrals = psf_start_block(psf->fp, "NPHI");   /* get dihed count */
00600   if (psf->numdihedrals > 0) {
00601     psf->dihedrals = (int *) malloc(4*psf->numdihedrals*sizeof(int));
00602     psf_get_dihedrals_impropers(psf->fp, psf->numdihedrals, psf->dihedrals, psf->charmmext);
00603   } else {
00604     printf("psfplugin) WARNING: no dihedrals defined in PSF file.\n");
00605   }
00606  
00607   psf->numimpropers = psf_start_block(psf->fp, "NIMPHI"); /* get imprp count */
00608   if (psf->numimpropers > 0) {
00609     psf->impropers = (int *) malloc(4*psf->numimpropers*sizeof(int));
00610     psf_get_dihedrals_impropers(psf->fp, psf->numimpropers, psf->impropers, psf->charmmext);
00611   } else {
00612     printf("psfplugin) WARNING: no impropers defined in PSF file.\n");
00613   }
00614 
00615   psf->numcterms = psf_start_block(psf->fp, "NCRTERM"); /* get cmap count */
00616   if (psf->numcterms > 0) {
00617     psf->cterms = (int *) malloc(8*psf->numcterms*sizeof(int));
00618 
00619     /* same format as dihedrals, but double the number of terms */
00620     psf_get_dihedrals_impropers(psf->fp, psf->numcterms * 2, psf->cterms, psf->charmmext);
00621   } else {
00622     printf("psfplugin) no cross-terms defined in PSF file.\n");
00623   }
00624 
00625   *numangles = psf->numangles;
00626   *angles = psf->angles;
00627 
00628   *numdihedrals = psf->numdihedrals;
00629   *dihedrals = psf->dihedrals;
00630 
00631   *numimpropers = psf->numimpropers;
00632   *impropers = psf->impropers;
00633 
00634   *numcterms = psf->numcterms;
00635   *cterms = psf->cterms;
00636 
00637   *ctermcols = 0;
00638   *ctermrows = 0;
00639 
00640   return MOLFILE_SUCCESS;
00641 }
00642 #else
00643 static int read_angles(void *v,
00644                int *numangles,    int **angles,    double **angleforces,
00645                int *numdihedrals, int **dihedrals, double **dihedralforces,
00646                int *numimpropers, int **impropers, double **improperforces,
00647                int *numcterms,    int **cterms,
00648                int *ctermcols,    int *ctermrows,  double **ctermforces) {
00649   psfdata *psf = (psfdata *)v;
00650 
00651   psf->numangles    = psf_start_block(psf->fp, "NTHETA"); /* get angle count */
00652   if (psf->numangles > 0) {
00653     psf->angles = (int *) malloc(3*psf->numangles*sizeof(int));
00654     psf_get_angles(psf->fp, psf->numangles, psf->angles);
00655   } else {
00656     printf("psfplugin) WARNING: no angles defined in PSF file.\n");
00657   }
00658  
00659   psf->numdihedrals = psf_start_block(psf->fp, "NPHI");   /* get dihed count */
00660   if (psf->numdihedrals > 0) {
00661     psf->dihedrals = (int *) malloc(4*psf->numdihedrals*sizeof(int));
00662     psf_get_dihedrals_impropers(psf->fp, psf->numdihedrals, psf->dihedrals);
00663   } else {
00664     printf("psfplugin) WARNING: no dihedrals defined in PSF file.\n");
00665   }
00666  
00667   psf->numimpropers = psf_start_block(psf->fp, "NIMPHI"); /* get imprp count */
00668   if (psf->numimpropers > 0) {
00669     psf->impropers = (int *) malloc(4*psf->numimpropers*sizeof(int));
00670     psf_get_dihedrals_impropers(psf->fp, psf->numimpropers, psf->impropers);
00671   } else {
00672     printf("psfplugin) WARNING: no impropers defined in PSF file.\n");
00673   }
00674 
00675   psf->numcterms = psf_start_block(psf->fp, "NCRTERM"); /* get cmap count */
00676   if (psf->numcterms > 0) {
00677     psf->cterms = (int *) malloc(8*psf->numcterms*sizeof(int));
00678 
00679     /* same format as dihedrals, but double the number of terms */
00680     psf_get_dihedrals_impropers(psf->fp, psf->numcterms * 2, psf->cterms);
00681   } else {
00682     printf("psfplugin) no cross-terms defined in PSF file.\n");
00683   }
00684 
00685   *numangles = psf->numangles;
00686   *angles = psf->angles;
00687   *angleforces = NULL;
00688 
00689   *numdihedrals = psf->numdihedrals;
00690   *dihedrals = psf->dihedrals;
00691   *dihedralforces = NULL;
00692 
00693   *numimpropers = psf->numimpropers;
00694   *impropers = psf->impropers;
00695   *improperforces = NULL;
00696 
00697   *numcterms = psf->numcterms;
00698   *cterms = psf->cterms;
00699 
00700   *ctermcols = 0;
00701   *ctermrows = 0;
00702   *ctermforces = NULL;
00703 
00704   return MOLFILE_SUCCESS;
00705 }
00706 #endif
00707 
00708 static void close_psf_read(void *mydata) {
00709   psfdata *psf = (psfdata *)mydata;
00710   if (psf) {
00711     if (psf->fp != NULL) 
00712       fclose(psf->fp);
00713 
00714     /* free bond data */
00715     if (psf->from != NULL) 
00716       free(psf->from);
00717     if (psf->to != NULL) 
00718       free(psf->to);
00719 
00720     /* free angle data */
00721     if (psf->angles != NULL)
00722       free(psf->angles);
00723     if (psf->dihedrals != NULL)
00724       free(psf->dihedrals);
00725     if (psf->impropers != NULL)
00726       free(psf->impropers);
00727 
00728     /* free cross-term data */
00729     if (psf->cterms != NULL)
00730       free(psf->cterms);
00731 
00732     free(psf);
00733   }
00734 }  
00735 
00736 
00737 static void *open_psf_write(const char *path, const char *filetype,
00738     int natoms) {
00739   FILE *fp;
00740   psfdata *psf;
00741 
00742   fp = fopen(path, "w");
00743   if (!fp) {
00744     fprintf(stderr, "Unable to open file %s for writing\n", path);
00745     return NULL;
00746   }
00747   psf = (psfdata *) malloc(sizeof(psfdata));
00748   memset(psf, 0, sizeof(psfdata));
00749   psf->fp = fp; 
00750   psf->numatoms = natoms;
00751   psf->namdfmt = 0;     /* initialize to off for now */
00752   psf->charmmfmt = 0;   /* initialize to off for now */
00753   psf->charmmext = 0;   /* off unless we discover we need it */
00754   psf->charmmcmap = 0;  /* off unless we discover we need it */
00755   psf->charmmcheq = 0;  /* off unless we discover we need it */
00756   psf->charmmdrude = 0; /* off unless we discover we need it */
00757   psf->nbonds = 0;
00758   psf->to = NULL;
00759   psf->from = NULL;
00760   return psf;
00761 }
00762 
00763 static int write_psf_structure(void *v, int optflags,
00764                                const molfile_atom_t *atoms) {
00765   psfdata *psf = (psfdata *)v;
00766   const molfile_atom_t *atom;
00767   int i, fullrows;
00768   int xplorfmt = 0;
00769 
00770   for (i=0; i<psf->numatoms; i++) {
00771     if ( ! isdigit(atoms[i].type[0]) ) xplorfmt = 1;
00772   }
00773 
00774   /* determine if we must write out an EXT formatted PSF file */
00775   /* check the field width of the PSF atom records            */
00776   if (psf->namdfmt == 0) {
00777     int fw = xplorfmt ? 6 : 4;
00778     for (i=0; i<psf->numatoms; i++) {
00779       if (strlen(atoms[i].type) > fw) {
00780         psf->namdfmt = 1;   /* force output to NAMD PSF variant because */
00781                             /* the atom types are too long              */
00782       }
00783     }
00784   }
00785   if (psf->namdfmt) {
00786     psf->charmmext = 0; /* using space-delimited format anyway */
00787   } else {
00788     if (psf->numatoms > 9999999) { /* allow space-delimited readers */
00789       psf->charmmext = 1; /* force output to EXTended PSF format      */
00790     }
00791     if (psf->charmmext == 0) {
00792       for (i=0; i<psf->numatoms; i++) {
00793         if (strlen(atoms[i].name) > 4) {
00794           psf->charmmext = 1; /* force output to EXTended PSF format      */
00795         }
00796         if (xplorfmt && strlen(atoms[i].type) > 4) {
00797           psf->charmmext = 1; /* force output to EXTended PSF format      */
00798         }
00799       }
00800     }
00801   }
00802   if (psf->namdfmt == 1) {
00803     printf("psfplugin) Structure requires space-delimited NAMD PSF format\n");
00804   } else if (psf->charmmext == 1) {
00805     printf("psfplugin) Structure requires EXTended PSF format\n");
00806   }
00807 
00808   /* check to see if we'll be writing cross-term maps */
00809   if (psf->numcterms > 0) {
00810     psf->charmmcmap = 1;
00811   }
00812 
00813   /* write out the PSF header */
00814   fprintf(psf->fp, "PSF");
00815   if (psf->namdfmt == 1) 
00816     fprintf(psf->fp, " NAMD");
00817   if (psf->charmmext == 1)
00818     fprintf(psf->fp, " EXT");
00819   if (psf->charmmcmap == 1)
00820     fprintf(psf->fp, " CMAP");
00821   fprintf(psf->fp, psf->charmmext ? "\n\n%10d !NTITLE\n" : "\n\n%8d !NTITLE\n", 1);
00822 
00823   if (psf->charmmfmt) {
00824     fprintf(psf->fp," REMARKS %s\n","VMD-generated Charmm PSF structure file");
00825 
00826     printf("psfplugin) WARNING: Charmm format PSF file is incomplete, atom type ID\n");
00827     printf("psfplugin)          codes have been emitted as '0'. \n");
00828   } else {
00829     fprintf(psf->fp," REMARKS %s\n","VMD-generated NAMD/X-Plor PSF structure file");
00830   }
00831   fprintf(psf->fp, "\n");
00832 
00833   /* write out total number of atoms */
00834   fprintf(psf->fp, psf->charmmext ? "%10d !NATOM\n" : "%8d !NATOM\n", psf->numatoms);
00835 
00836   /* write out all of the atom records */
00837   for (i=0; i<psf->numatoms; i++) {
00838     const char *atomname; 
00839     atom = &atoms[i];
00840     atomname = atom->name;
00841 
00842     /* skip any leading space characters given to us by VMD */ 
00843     while (*atomname == ' ')
00844       atomname++;
00845 
00846     if (psf->charmmext) {
00847       /* fmt02=  '(I10,1X,A8,1X,A8,1X,A8,1X,A8,1X,A6,1X,2G14.6,I8,2G14.6)' */
00848       /* fmt01=  '(I10,1X,A8,1X,A8,1X,A8,1X,A8,1X,I4,1X,2G14.6,I8)'        */
00849       fprintf(psf->fp, xplorfmt ? 
00850                        "%10d %-8s %-8d %-8s %-8s %-6s %14.6g %13.6g %7d\n"
00851                      : "%10d %-8s %-8d %-8s %-8s %-4s %14.6g %13.6g %7d\n",
00852               i+1, atom->segid, atom->resid, atom->resname,
00853               atomname, atom->type, atom->charge, atom->mass, 0);
00854     } else if (psf->charmmfmt) {
00855       /* XXX replace hard-coded 0 with proper atom type ID code  */
00856       /* fmt01='(I8,1X,A4,1X,A4,1X,A4,1X,A4,1X,I4,1X,2G14.6,I8)' */
00857       fprintf(psf->fp, "%8d %-4s %-4d %-4s %-4s %4d %14.6g %13.6g %7d\n",
00858               i+1, atom->segid, atom->resid, atom->resname,
00859               atomname, /* atom->typeid */ 0, atom->charge, atom->mass, 0);
00860     } else {
00861       /* fmt02='(I8,1X,A4,1X,A4,1X,A4,1X,A4,1X,A4,1X,2G14.6,I8)' */
00862       fprintf(psf->fp, "%8d %-4s %-4d %-4s %-4s %-4s %14.6g %13.6g %7d\n",
00863               i+1, atom->segid, atom->resid, atom->resname,
00864               atomname, atom->type, atom->charge, atom->mass, 0);
00865     }
00866   } 
00867   fprintf(psf->fp, "\n");
00868 
00869   /* write out bonds if we have bond information */
00870   /* XXX Note: We are generating bond records the same way for both the  */
00871   /*           normal and EXT format PSF files, which seems odd, but was */
00872   /*           seemingly validated by the CHARMM 31 test files I have.   */
00873   if (psf->nbonds > 0 && psf->from != NULL && psf->to != NULL) {
00874     fprintf(psf->fp, psf->charmmext ? "%10d !NBOND: bonds\n" : "%8d !NBOND: bonds\n", psf->nbonds);
00875     for (i=0; i<psf->nbonds; i++) {
00876       if (psf->namdfmt)
00877         fprintf(psf->fp, " %7d %7d", psf->from[i], psf->to[i]);
00878       else if (psf->charmmext)
00879         fprintf(psf->fp, "%10d%10d", psf->from[i], psf->to[i]);
00880       else
00881         fprintf(psf->fp, "%8d%8d", psf->from[i], psf->to[i]);
00882 
00883       if ((i % 4) == 3) 
00884         fprintf(psf->fp, "\n");
00885     }
00886     if ((i % 4) != 0) 
00887       fprintf(psf->fp, "\n");
00888     fprintf(psf->fp, "\n");
00889   } else {
00890     fprintf(psf->fp, psf->charmmext ? "%10d !NBOND: bonds\n" : "%8d !NBOND: bonds\n", 0);
00891     fprintf(psf->fp, "\n\n");
00892   }
00893 
00894   if (psf->numangles == 0 && psf->numdihedrals == 0 && psf->numimpropers == 0 && psf->numcterms == 0) {
00895     printf("psfplugin) WARNING: PSF file is incomplete, no angles, dihedrals,\n");
00896     printf("psfplugin)          impropers, or cross-terms will be written. \n");
00897 
00898     fprintf(psf->fp, psf->charmmext ? "%10d !NTHETA: angles\n\n\n" : "%8d !NTHETA: angles\n\n\n", 0);
00899     fprintf(psf->fp, psf->charmmext ? "%10d !NPHI: dihedrals\n\n\n" : "%8d !NPHI: dihedrals\n\n\n", 0);
00900     fprintf(psf->fp, psf->charmmext ? "%10d !NIMPHI: impropers\n\n\n" : "%8d !NIMPHI: impropers\n\n\n", 0);
00901   } else {
00902     int i, numinline;
00903 
00904     printf("psfplugin) Writing angles/dihedrals/impropers...\n");
00905 
00906     fprintf(psf->fp, psf->charmmext ? "%10d !NTHETA: angles\n" : "%8d !NTHETA: angles\n", psf->numangles);
00907     for (numinline=0,i=0; i<psf->numangles; i++) {
00908       if ( numinline == 3 ) { fprintf(psf->fp, "\n");  numinline = 0; }
00909       fprintf(psf->fp, psf->charmmext ? "%10d%10d%10d" : " %7d %7d %7d", 
00910               psf->angles[i*3], psf->angles[i*3+1], psf->angles[i*3+2]);
00911       numinline++;
00912     }
00913     fprintf(psf->fp, "\n\n");
00914 
00915     fprintf(psf->fp, psf->charmmext ? "%10d !NPHI: dihedrals\n" : "%8d !NPHI: dihedrals\n", psf->numdihedrals);
00916     for (numinline=0,i=0; i<psf->numdihedrals; i++) {
00917       if ( numinline == 2 ) { fprintf(psf->fp, "\n");  numinline = 0; }
00918       fprintf(psf->fp, psf->charmmext ? "%10d%10d%10d%10d" : " %7d %7d %7d %7d", 
00919               psf->dihedrals[i*4], psf->dihedrals[i*4+1], 
00920               psf->dihedrals[i*4+2], psf->dihedrals[i*4+3]);
00921       numinline++;
00922     }
00923     fprintf(psf->fp, "\n\n");
00924 
00925     fprintf(psf->fp, psf->charmmext ? "%10d !NIMPHI: impropers\n" : "%8d !NIMPHI: impropers\n", psf->numimpropers);
00926     for (numinline=0,i=0; i<psf->numimpropers; i++) {
00927       if ( numinline == 2 ) { fprintf(psf->fp, "\n");  numinline = 0; }
00928       fprintf(psf->fp, psf->charmmext ? "%10d%10d%10d%10d" : " %7d %7d %7d %7d",
00929               psf->impropers[i*4  ], psf->impropers[i*4+1], 
00930               psf->impropers[i*4+2], psf->impropers[i*4+3]);
00931       numinline++;
00932     }
00933     fprintf(psf->fp, "\n\n");
00934   }
00935 
00936 
00937   /*
00938    * write out empty donor/acceptor records since we don't
00939    * presently make use of this information 
00940    */
00941   if (psf->charmmext) {
00942     fprintf(psf->fp, "%10d !NDON: donors\n\n\n", 0);
00943     fprintf(psf->fp, "%10d !NACC: acceptors\n\n\n", 0);
00944     fprintf(psf->fp, "%10d !NNB\n\n", 0);
00945   } else {
00946     fprintf(psf->fp, "%8d !NDON: donors\n\n\n", 0);
00947     fprintf(psf->fp, "%8d !NACC: acceptors\n\n\n", 0);
00948     fprintf(psf->fp, "%8d !NNB\n\n", 0);
00949   }
00950   
00951 
00952   /* Pad with zeros, one for every atom */
00953   fullrows = psf->numatoms/8;
00954   for (i=0; i<fullrows; ++i)
00955     fprintf(psf->fp, psf->charmmext ? "%10d%10d%10d%10d%10d%10d%10d%10d\n" :
00956                      "%8d%8d%8d%8d%8d%8d%8d%8d\n", 0, 0, 0, 0, 0, 0, 0, 0);
00957   for (i=psf->numatoms - fullrows*8; i; --i)
00958     fprintf(psf->fp, psf->charmmext ? "%10d" : "%8d", 0);
00959   fprintf(psf->fp, "\n\n");
00960   fprintf(psf->fp, psf->charmmext ? "%8d %7d !NGRP\n%10d%10d%10d\n\n" :
00961                    "%8d %7d !NGRP\n%8d%8d%8d\n\n", 1, 0, 0, 0, 0);
00962 
00963 
00964   /* write out cross-terms */
00965   if (psf->numcterms > 0) {
00966     fprintf(psf->fp, psf->charmmext ? "%10d !NCRTERM: cross-terms\n" : "%8d !NCRTERM: cross-terms\n", psf->numcterms);
00967     for (i=0; i<psf->numcterms; i++) {
00968       fprintf(psf->fp, psf->charmmext ? "%10d%10d%10d%10d%10d%10d%10d%10d\n" :
00969                        " %7d %7d %7d %7d %7d %7d %7d %7d\n",
00970               psf->cterms[i*8  ], psf->cterms[i*8+1], 
00971               psf->cterms[i*8+2], psf->cterms[i*8+3],
00972               psf->cterms[i*8+4], psf->cterms[i*8+5],
00973               psf->cterms[i*8+6], psf->cterms[i*8+7]);
00974     }
00975     fprintf(psf->fp, "\n\n");
00976   }
00977 
00978   return MOLFILE_SUCCESS;
00979 }
00980 
00981 static int write_bonds(void *v, int nbonds, int *fromptr, int *toptr, 
00982                        float *bondorderptr, int *bondtype, 
00983                        int nbondtypes, char **bondtypename) {
00984   psfdata *psf = (psfdata *)v;
00985 
00986   /* save info until we actually write out the structure file */
00987   psf->nbonds = nbonds;
00988   psf->from = (int *) malloc(nbonds * sizeof(int));
00989   memcpy(psf->from, fromptr, nbonds * sizeof(int));
00990   psf->to = (int *) malloc(nbonds * sizeof(int));
00991   memcpy(psf->to, toptr, nbonds * sizeof(int));
00992 
00993   return MOLFILE_SUCCESS;
00994 }
00995 
00996 #if vmdplugin_ABIVERSION > 14
00997 static int write_angles(void * v, int numangles, const int *angles,
00998                         const int *angletypes, int numangletypes,
00999                         const char **angletypenames, int numdihedrals, 
01000                         const int *dihedrals, const int *dihedraltype,
01001                         int numdihedraltypes, const char **dihedraltypenames,
01002                         int numimpropers, const int *impropers, 
01003                         const int *impropertypes, int numimpropertypes, 
01004                         const char **impropertypenames, int numcterms, 
01005                         const int *cterms, int ctermcols, int ctermrows) {
01006   psfdata *psf = (psfdata *)v;
01007 
01008   /* save info until we actually write out the structure file */
01009   psf->numangles = numangles;
01010   psf->numdihedrals = numdihedrals;
01011   psf->numimpropers = numimpropers;
01012   psf->numcterms = numcterms;
01013 
01014   psf->angles = (int *) malloc(3*psf->numangles*sizeof(int));
01015   memcpy(psf->angles, angles, 3*psf->numangles*sizeof(int));
01016 
01017   psf->dihedrals = (int *) malloc(4*psf->numdihedrals*sizeof(int));
01018   memcpy(psf->dihedrals, dihedrals, 4*psf->numdihedrals*sizeof(int));
01019 
01020   psf->impropers = (int *) malloc(4*psf->numimpropers*sizeof(int));
01021   memcpy(psf->impropers, impropers, 4*psf->numimpropers*sizeof(int));
01022 
01023   psf->cterms = (int *) malloc(8*psf->numcterms*sizeof(int));
01024   memcpy(psf->cterms, cterms, 8*psf->numcterms*sizeof(int));
01025 
01026   return MOLFILE_SUCCESS;
01027 }
01028 #else
01029 static int write_angles(void * v,
01030         int numangles,    const int *angles,    const double *angleforces,
01031         int numdihedrals, const int *dihedrals, const double *dihedralforces,
01032         int numimpropers, const int *impropers, const double *improperforces,
01033         int numcterms,   const int *cterms,
01034         int ctermcols, int ctermrows, const double *ctermforces) {
01035   psfdata *psf = (psfdata *)v;
01036 
01037   /* save info until we actually write out the structure file */
01038   psf->numangles = numangles;
01039   psf->numdihedrals = numdihedrals;
01040   psf->numimpropers = numimpropers;
01041   psf->numcterms = numcterms;
01042 
01043   psf->angles = (int *) malloc(3*psf->numangles*sizeof(int));
01044   memcpy(psf->angles, angles, 3*psf->numangles*sizeof(int));
01045 
01046   psf->dihedrals = (int *) malloc(4*psf->numdihedrals*sizeof(int));
01047   memcpy(psf->dihedrals, dihedrals, 4*psf->numdihedrals*sizeof(int));
01048 
01049   psf->impropers = (int *) malloc(4*psf->numimpropers*sizeof(int));
01050   memcpy(psf->impropers, impropers, 4*psf->numimpropers*sizeof(int));
01051 
01052   psf->cterms = (int *) malloc(8*psf->numcterms*sizeof(int));
01053   memcpy(psf->cterms, cterms, 8*psf->numcterms*sizeof(int));
01054 
01055   return MOLFILE_SUCCESS;
01056 }
01057 #endif
01058 
01059 static void close_psf_write(void *v) {
01060   psfdata *psf = (psfdata *)v;
01061   fclose(psf->fp);
01062 
01063   /* free bonds if we have them */
01064   if (psf->from != NULL) 
01065     free(psf->from);
01066   if (psf->to != NULL) 
01067     free(psf->to);
01068 
01069   /* free angles if we have them */
01070   if (psf->angles)
01071     free(psf->angles);
01072   if (psf->dihedrals)
01073     free(psf->dihedrals);
01074   if (psf->impropers)
01075     free(psf->impropers);
01076 
01077   /* free cross-terms if we have them */
01078   if (psf->cterms)
01079     free(psf->cterms);
01080 
01081   free(psf);
01082 }
01083 
01084 
01085 /*
01086  * Initialization stuff down here
01087  */
01088 
01089 static molfile_plugin_t plugin;
01090 
01091 VMDPLUGIN_API int VMDPLUGIN_init() {
01092   memset(&plugin, 0, sizeof(molfile_plugin_t));
01093   plugin.abiversion = vmdplugin_ABIVERSION;
01094   plugin.type = MOLFILE_PLUGIN_TYPE;
01095   plugin.name = "psf";
01096   plugin.prettyname = "CHARMM,NAMD,XPLOR PSF";
01097   plugin.author = "Justin Gullingsrud, John Stone";
01098   plugin.majorv = 1;
01099   plugin.minorv = 9;
01100   plugin.is_reentrant = VMDPLUGIN_THREADSAFE;
01101   plugin.filename_extension = "psf";
01102   plugin.open_file_read = open_psf_read;
01103   plugin.read_structure = read_psf;
01104   plugin.read_bonds = read_bonds;
01105 #if vmdplugin_ABIVERSION > 9
01106   plugin.read_angles = read_angles;
01107 #endif
01108   plugin.close_file_read = close_psf_read;
01109   plugin.open_file_write = open_psf_write;
01110   plugin.write_structure = write_psf_structure;
01111   plugin.close_file_write = close_psf_write;
01112   plugin.write_bonds = write_bonds;
01113 #if vmdplugin_ABIVERSION > 9
01114   plugin.write_angles = write_angles;
01115 #endif
01116   return VMDPLUGIN_SUCCESS;
01117 }
01118 
01119 VMDPLUGIN_API int VMDPLUGIN_register(void *v, vmdplugin_register_cb cb) {
01120   (*cb)(v, (vmdplugin_t *)&plugin);
01121   return VMDPLUGIN_SUCCESS;
01122 }
01123 
01124 VMDPLUGIN_API int VMDPLUGIN_fini() {
01125   return VMDPLUGIN_SUCCESS;
01126 }

Generated on Fri Apr 19 03:09:30 2024 for VMD Plugins (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002