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

BaseMolecule.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr                                                                       
00003  *cr            (C) Copyright 1995-2019 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: BaseMolecule.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.278 $      $Date: 2022/05/06 15:12:34 $
00015  *
00016  ***************************************************************************/
00028 #include <ctype.h>
00029 #include <stdio.h>
00030 #include <stdlib.h>
00031 #include <math.h>
00032 #include "Inform.h"
00033 #include "utilities.h"
00034 #include "intstack.h"
00035 #include "WKFUtils.h"
00036 
00037 #include "BaseMolecule.h"
00038 #include "VolumetricData.h"
00039 #include "QMData.h"
00040 #include "QMTimestep.h"
00041 
00042 #ifdef VMDWITHCARBS
00043 #include <vector> /* XXX this needs to go, gives trouble with MSVC */
00044 #endif
00045 
00046 #define MAXBONDERRORS 25
00047 
00049 
00050 BaseMolecule::BaseMolecule(int myID) : residueList(10) , fragList(1),
00051 #if defined(VMDWITHCARBS)
00052      pfragList(1), nfragList(1), smallringList(), smallringLinkages(), currentMaxRingSize(-1), currentMaxPathLength(-1), ID(myID) {
00053 #else
00054      pfragList(1), nfragList(1), ID(myID) {
00055 #endif
00056 
00057   // initialize all variables
00058   nAtoms = 0;
00059   nResidues = 0;
00060   nWaters = 0;
00061   nSegments = 0;
00062   nFragments = 0;
00063   nProteinFragments = 0;
00064   nNucleicFragments = 0;
00065 
00066   cur_atom = 0;
00067   atomList = NULL;
00068   moleculename = NULL;
00069   lastbonderratomid=-1;
00070   bonderrorcount=0;
00071   datasetflags=NODATA;
00072   qm_data = NULL;
00073 
00074   radii_minmax_need_update = 1;
00075   radii_min = 0.0f;
00076   radii_max = 0.0f;
00077 
00078   need_find_bonds = 0;
00079 }
00080 
00081 
00083 
00084 BaseMolecule::~BaseMolecule(void) {
00085   int i;
00086 
00087 #if 0
00088   // XXX still need to track down ringlist leak(s)
00089   // delete carbohydrate ring data structures
00090   smallringList.clear();
00091   smallringLinkages.clear();
00092 #endif
00093 
00094   // delete structural data
00095   delete [] atomList;
00096   for (i=0; i<residueList.num(); i++) {
00097     delete residueList[i];
00098   }
00099   for (i=0; i<nfragList.num(); i++) {
00100     delete nfragList[i];
00101   }
00102 #if defined(VMDFASTRIBBONS)
00103   for (i=0; i<nfragCPList.num(); i++) {
00104     delete [] nfragCPList[i];
00105   }
00106 #endif
00107   for (i=0; i<pfragList.num(); i++) {
00108     delete pfragList[i];
00109   }
00110 #if defined(VMDFASTRIBBONS)
00111   for (i=0; i<pfragCPList.num(); i++) {
00112     delete [] pfragCPList[i];
00113   }
00114 #endif
00115   for (i=0; i<fragList.num(); i++) {
00116     delete fragList[i];
00117   }
00118   for (i=0; i<volumeList.num(); i++) {
00119     delete volumeList[i];
00120   }
00121 
00122   // delete optional per-atom fields
00123   for (i=0; i<extraflt.num(); i++) {
00124     delete [] extraflt.data(i);
00125   }
00126   for (i=0; i<extraint.num(); i++) {
00127     delete [] extraint.data(i);
00128   }
00129   for (i=0; i<extraflg.num(); i++) {
00130     delete [] extraflg.data(i);
00131   }
00132 
00133   if (qm_data)
00134     delete qm_data;
00135 }
00136 
00137 
00139 
00140 // initialize the atom list ... should be called before adding any atoms
00141 int BaseMolecule::init_atoms(int n) {
00142   if (n <= 0) {
00143     msgErr << "BaseMolecule: init_atoms called with invalid number of atoms: "
00144            << n << sendmsg;
00145     return FALSE;
00146   }
00147   if (cur_atom != 0 && nAtoms != n) {
00148     msgErr << "BaseMolecule: attempt to init atoms while structure building in progress!" << sendmsg;
00149     return FALSE;
00150   }
00151 
00152   if (!atomList) {
00153     int i;
00154     // first call to init_atoms
00155     nAtoms = n; // only place where nAtoms is set!
00156     atomList = new MolAtom[nAtoms];
00157     memset(atomList, 0, long(nAtoms)*sizeof(MolAtom));
00158 
00159     // initialize NULL extra data field, which is returned when
00160     // querying a non-existent field with extra*.data("fielddoesntexist")
00161     extraflt.add_name("NULL", NULL);
00162     extraint.add_name("NULL", NULL);
00163     extraflg.add_name("NULL", NULL);
00164 
00165     // initialize "default" extra data fields.
00166     extraflt.add_name("beta", new float[nAtoms]);
00167     extraflt.add_name("occupancy", new float[nAtoms]);
00168     extraflt.add_name("charge", new float[nAtoms]);
00169     extraflt.add_name("mass", new float[nAtoms]);
00170     extraflt.add_name("radius", new float[nAtoms]);
00171 
00172     // initialize default per-atom flags
00173     extraflg.add_name("flags", new unsigned char[nAtoms]);
00174 
00175     // initialize "default" extra floating point data fields.
00176     for (i=0; i<extraflt.num(); i++) {
00177       void *data = extraflt.data(i);
00178       if (data != NULL) 
00179         memset(data, 0, long(nAtoms)*sizeof(float));
00180     }
00181 
00182     // initialize "default" extra integer data fields.
00183     for (i=0; i<extraint.num(); i++) {
00184       void *data = extraint.data(i);
00185       if (data != NULL)
00186         memset(data, 0, long(nAtoms)*sizeof(int));
00187     }
00188 
00189     // initialize "default" extra flags data fields.
00190     for (i=0; i<extraflg.num(); i++) {
00191       void *data = extraflg.data(i);
00192 
00193       // 8 per-atom flags per unsigned char
00194       if (data != NULL)
00195         memset(data, 0, long(nAtoms)*sizeof(unsigned char));
00196     }
00197 
00198     return TRUE;
00199   }
00200   if (n != nAtoms) {
00201     msgErr << "The number of atoms in this molecule has already been assigned."
00202            << sendmsg;
00203     return FALSE;
00204   }
00205   return TRUE;
00206 }
00207 
00208 
00209 // add a new atom; return it's index, or (-1) if error.
00210 int BaseMolecule::add_atoms(int addatomcount,
00211       const char *name, const char *atomtype, 
00212       int atomicnumber, const char *resname, int resid, 
00213       const char *chain, const char *segname, 
00214       const char *insertion, const char *altloc) {
00215   if (addatomcount < 1) {
00216     msgErr << "BaseMolecule: Cannot add negative atom count!" << sendmsg;
00217     return -1;
00218   }
00219 
00220   int newtotalcount = cur_atom + addatomcount;
00221   if (newtotalcount > nAtoms) {
00222     msgErr << "BaseMolecule: Cannot add more atoms to molecule!" << sendmsg;
00223     return -1;
00224   }
00225 
00226   if (!atomList || cur_atom >= nAtoms) {
00227     msgErr << "BaseMolecule: Cannot add new atom; currently " << nAtoms;
00228     msgErr << " atoms in structure." << sendmsg;
00229     return -1;
00230   }
00231 
00232   // add names to namelist, and put indices in MolAtom object
00233   int nameindex = atomNames.add_name(name, atomNames.num());
00234   int typeindex = atomTypes.add_name(atomtype, atomTypes.num());
00235   int resnameindex = resNames.add_name(resname, resNames.num());
00236   int segnameindex = segNames.add_name(segname, segNames.num());
00237   int altlocindex = altlocNames.add_name(altloc, altlocNames.num());
00238 
00239   // use default of 'X' for chain if not given
00240   int chainindex = -1;
00241   if (!chain || ! (*chain) || *chain == ' ')
00242     chainindex = chainNames.add_name("X", chainNames.num());
00243   else
00244     chainindex = chainNames.add_name(chain, chainNames.num());
00245 
00246   // create first atom  
00247   MolAtom *newatom = atom(cur_atom);
00248   newatom->init(cur_atom, resid, insertion);
00249 
00250   // set atom member variables
00251   newatom->nameindex = nameindex;
00252   newatom->typeindex = typeindex;
00253   newatom->atomicnumber = atomicnumber;
00254   newatom->resnameindex = resnameindex;
00255   newatom->segnameindex = segnameindex;
00256   newatom->altlocindex = altlocindex;
00257   newatom->chainindex = chainindex;
00258 
00259   // check for integer overflow/wraparound condition, which can occur
00260   // if an evil plugin defines 100,000 unique atom names, for example
00261   if (newatom->nameindex != nameindex ||
00262       newatom->typeindex != typeindex ||
00263       newatom->atomicnumber != atomicnumber ||
00264       newatom->resnameindex != resnameindex ||
00265       newatom->segnameindex != segnameindex ||
00266       newatom->altlocindex != altlocindex ||
00267       newatom->chainindex != chainindex) {
00268     msgErr << "BaseMolecule: Cannot add atom; namelist index value too large." << sendmsg;
00269     msgErr << "Recompile VMD with larger index types." << sendmsg;
00270     msgErr << "Atom namelist index values at time of overflow:" << sendmsg;
00271     msgErr << "  nameindex: " << nameindex << sendmsg;;
00272     msgErr << "  typeindex: " << typeindex << sendmsg;;
00273     msgErr << "  resnameindex: " << resnameindex << sendmsg;;
00274     msgErr << "  segnameindex: " << segnameindex << sendmsg;;
00275     msgErr << "  altlocindex: " << altlocindex << sendmsg;;
00276     msgErr << "  chainindex: " << chainindex << sendmsg;
00277     return -1;
00278   }
00279 
00280   cur_atom++; // first atom was added without difficulty
00281 
00282   // Now we can do the rest in a tight loop without
00283   // all of the per-atom checking we would do normally,
00284   // giving us a big speed boost with large counts.
00285   while (cur_atom < newtotalcount) {
00286     newatom = atom(cur_atom);
00287     newatom->init(cur_atom, resid, insertion);
00288 
00289     newatom->nameindex = nameindex;       // set atom member variables
00290     newatom->typeindex = typeindex;
00291     newatom->atomicnumber = atomicnumber;
00292     newatom->resnameindex = resnameindex;
00293     newatom->segnameindex = segnameindex;
00294     newatom->altlocindex = altlocindex;
00295     newatom->chainindex = chainindex;
00296 
00297     cur_atom++;
00298   } 
00299 
00300   return cur_atom;
00301 }
00302 
00303 
00304 // add a new bond; return 0 on success, or (-1) if error.
00305 int BaseMolecule::add_bond(int a, int b, float bondorder, 
00306                            int bondtype, int backbonetype) {
00307   if (!nAtoms || a >= nAtoms || b >= nAtoms) {
00308     msgErr << "BaseMolecule: Atoms must be added before bonds." << sendmsg;
00309     return (-1);
00310   } 
00311 
00312   if (a == b) {
00313     msgErr << "BaseMolecule: Cannot bond atom " <<a<< " to itself." << sendmsg;
00314     return (-1);
00315   }
00316 
00317   // put the bond in the atom list
00318   if (atom(a)->add_bond(b, backbonetype)) {
00319     if (bonderrorcount < MAXBONDERRORS) {
00320       if (lastbonderratomid != a) {
00321         msgErr << "MolAtom " << a << ": Exceeded maximum number of bonds ("
00322                << atom(a)->bonds << ")." << sendmsg;
00323         lastbonderratomid=a;
00324         bonderrorcount++;
00325       }
00326     } else if (bonderrorcount == MAXBONDERRORS) {
00327       msgErr << "BaseMolecule: Excessive bonding errors encountered, perhaps atom coordinates are in the wrong units?" << sendmsg;
00328       msgErr << "BaseMolecule: Silencing bonding error messages." << sendmsg;
00329       bonderrorcount++;
00330     }
00331     return (-1);
00332   }
00333 
00334   if (atom(b)->add_bond(a, backbonetype)) {
00335     if (bonderrorcount < MAXBONDERRORS) {
00336       if (lastbonderratomid != b) {
00337         msgErr << "MolAtom " << b << ": Exceeded maximum number of bonds ("
00338                << atom(b)->bonds << ")." << sendmsg;
00339         lastbonderratomid=b;
00340         bonderrorcount++;
00341       }
00342     } else if (bonderrorcount == MAXBONDERRORS) {
00343       msgErr << "BaseMolecule: Excessive bonding errors encountered, perhaps atom coordinates are in the wrong units?" << sendmsg;
00344       msgErr << "BaseMolecule: Silencing bonding error messages." << sendmsg;
00345       bonderrorcount++;
00346     }
00347     return (-1);
00348   }
00349 
00350   // store bond orders and types
00351   setbondorder(a, atom(a)->bonds-1, bondorder);
00352   setbondorder(b, atom(b)->bonds-1, bondorder);
00353 
00354   setbondtype(a, atom(a)->bonds-1, bondtype);
00355   setbondtype(b, atom(b)->bonds-1, bondtype);
00356 
00357   return 0;
00358 }
00359 
00360 // Add a bond to a structure but check to make sure there isn't a 
00361 // duplicate, as may be the case when merging bondlists from a file 
00362 // and from a distance-based bond search
00363 int BaseMolecule::add_bond_dupcheck(int a, int b, float bondorder, int bondtype) {
00364   int i;
00365 
00366   if (!nAtoms || a >= nAtoms || b >= nAtoms) {
00367     msgErr << "BaseMolecule: Atoms must be added before bonds." << sendmsg;
00368     return (-1);
00369   }
00370 
00371   MolAtom *atm = atom(a);
00372   int nbonds = atm->bonds;
00373   const int *bonds = &atm->bondTo[0];
00374   for (i=0; i<nbonds; i++) {
00375     if (bonds[i] == b) {
00376       return 0; // skip bond that already exists
00377     }
00378   }
00379   add_bond(a, b, bondorder, bondtype); // add it if it doesn't already exist
00380 
00381   return 0;
00382 }
00383 
00384 int BaseMolecule::add_angle(int a, int b, int c, int type) {
00385   int i,n;
00386   // make sure that a < c to make it easier to find duplicates later.
00387   if (a > c) { i = a; a = c; c = i; }
00388   
00389   angles.append3(a, b, c); 
00390 
00391   n = num_angles()-1;
00392   set_angletype(n, type);
00393   return n;
00394 }
00395 
00396 
00397 int BaseMolecule::set_angletype(int nangle, int type) {
00398   // type -1 is the anonymous type and is handled special w/o storage.
00399   // we also bail out in case we get an illegal angle index.
00400   if ((type < 0) || (nangle >= num_angles()))
00401     return -1;
00402 
00403   // fill array if not there
00404   if (angleTypes.num() <= nangle) {
00405     set_dataset_flag(BaseMolecule::ANGLETYPES);
00406     int addcnt = num_angles() - angleTypes.num();
00407     if (addcnt > 0)
00408       angleTypes.appendN(-1, addcnt);
00409   }
00410   
00411   angleTypes[nangle] = type;
00412   return type;
00413 }
00414 
00415 int BaseMolecule::get_angletype(int nangle) {
00416   if ((nangle < 0) || (nangle >= angleTypes.num()))
00417     return -1;
00418 
00419   return angleTypes[nangle];
00420 }
00421 
00422 
00423 int BaseMolecule::add_dihedral(int a, int b, int c, int d, int type) {
00424   int i, j, n;
00425   // make sure that b < c so that it is easier to find duplicates later.
00426   if (b > c) { i = a; j = b ; a = d ; b = c; d = i; c = j; }
00427   
00428   dihedrals.append4(a, b, c, d); 
00429 
00430   n = num_dihedrals()-1;
00431   set_dihedraltype(n, type);
00432   return n;
00433 }
00434 
00435 
00436 int BaseMolecule::set_dihedraltype(int ndihedral, int type) {
00437   // type -1 is the anonymous type and is handled special w/o storage.
00438   // we also bail out in case we get an illegal dihedral index.
00439   if ((type < 0) || (ndihedral >= num_dihedrals()))
00440     return -1;
00441 
00442   // fill array if not there
00443   if (dihedralTypes.num() <= ndihedral) {
00444     set_dataset_flag(BaseMolecule::ANGLETYPES);
00445     int addcnt = num_dihedrals() - dihedralTypes.num();
00446     if (addcnt > 0)
00447       dihedralTypes.appendN(-1, addcnt);
00448   }
00449   
00450   dihedralTypes[ndihedral] = type;
00451   return type;
00452 }
00453 
00454 int BaseMolecule::get_dihedraltype(int ndihedral) {
00455   if ((ndihedral < 0) || (ndihedral >= dihedralTypes.num()))
00456     return -1;
00457 
00458   return dihedralTypes[ndihedral];
00459 }
00460 
00461 int BaseMolecule::add_improper(int a, int b, int c, int d, int type) {
00462   int i, j, n;
00463   // make sure that b < c so that it is easier to find duplicates later.
00464   if (b > c) { i = a; j = b ; a = d ; b = c; d = i; c = j; }
00465   
00466   impropers.append4(a, b, c, d); 
00467 
00468   n = num_impropers()-1;
00469   set_impropertype(n, type);
00470   return n;
00471 }
00472 
00473 
00474 int BaseMolecule::set_impropertype(int nimproper, int type) {
00475   // type -1 is the anonymous type and is handled special w/o storage.
00476   // we also bail out in case we get an illegal improper index.
00477   if ((type < 0) || (nimproper >= num_impropers()))
00478     return -1;
00479 
00480   // fill array if not there
00481   if (improperTypes.num() <= nimproper) {
00482     set_dataset_flag(BaseMolecule::ANGLETYPES);
00483     int addcnt = num_impropers() - improperTypes.num();
00484     if (addcnt > 0)
00485       improperTypes.appendN(-1, addcnt);
00486   }
00487   
00488   improperTypes[nimproper] = type;
00489   return type;
00490 }
00491 
00492 int BaseMolecule::get_impropertype(int nimproper) {
00493   if ((nimproper < 0) || (nimproper >= improperTypes.num()))
00494     return -1;
00495 
00496   return improperTypes[nimproper];
00497 }
00498 
00500 
00501 void BaseMolecule::setbondorder(int atom, int bond, float order) {
00502   float *bondOrders = extraflt.data("bondorders");
00503 
00504   // if not already there, add it
00505   if (bondOrders == NULL) {
00506     if (order != 1) {
00507       int i;
00508       extraflt.add_name("bondorders", new float[nAtoms*MAXATOMBONDS]);
00509       bondOrders = extraflt.data("bondorders");
00510       for (i=0; i<nAtoms*MAXATOMBONDS; i++)
00511         bondOrders[i] = 1.0f;    
00512 
00513       bondOrders[atom * MAXATOMBONDS + bond] = order;
00514     } 
00515     return;
00516   }
00517 
00518   bondOrders[atom * MAXATOMBONDS + bond] = order;
00519 }
00520 
00521 float BaseMolecule::getbondorder(int atom, int bond) {
00522   float *bondOrders = extraflt.data("bondorders");
00523 
00524   // if not already there, add it
00525   if (bondOrders == NULL) { 
00526     return 1;
00527   }
00528    
00529   return bondOrders[atom * MAXATOMBONDS + bond];
00530 }
00531 
00532 
00533 void BaseMolecule::setbondtype(int atom, int bond, int type) {
00534   int *bondTypes = extraint.data("bondtypes");
00535 
00536   // if not already there, add it
00537   if (bondTypes == NULL) {
00538     if (type != -1) {
00539       int i;
00540       extraint.add_name("bondtypes", new int[nAtoms*MAXATOMBONDS]);
00541       bondTypes = extraint.data("bondtypes");
00542       for (i=0; i<nAtoms*MAXATOMBONDS; i++)
00543         bondTypes[i] = -1;    
00544 
00545       bondTypes[atom * MAXATOMBONDS + bond] = type;
00546     } 
00547     return;
00548   }
00549 
00550   bondTypes[atom * MAXATOMBONDS + bond] = type;
00551 }
00552 
00553 int BaseMolecule::getbondtype(int atom, int bond) {
00554   int *bondTypes = extraint.data("bondtypes");
00555 
00556   // if not already there, add it
00557   if (bondTypes == NULL) { 
00558     return -1;
00559   }
00560    
00561   return bondTypes[atom * MAXATOMBONDS + bond];
00562 }
00563 
00564 
00565 // return the Nth residue
00566 Residue *BaseMolecule::residue(int n) {
00567   return residueList[n];
00568 }
00569 
00570 
00571 // return the Nth fragment
00572 Fragment *BaseMolecule::fragment(int n) {
00573   return fragList[n];
00574 }
00575 
00576 
00577 // given an atom index, return the residue object for the residue it
00578 // is in.  If it is not in a residue, return NULL.
00579 Residue *BaseMolecule::atom_residue(int n) {
00580   MolAtom *atm = atom(n);
00581   if(atm->uniq_resid < 0)
00582     return NULL;
00583   else
00584     return residue(atm->uniq_resid);
00585 }
00586 
00587 
00588 // given an atom index, return the fragment object for the fragment it
00589 // is in.  If it is not in a fragment, return NULL.
00590 Fragment *BaseMolecule::atom_fragment(int n) {
00591   MolAtom *atm = atom(n);
00592   int frag = residue(atm->uniq_resid)->fragment;
00593   if(frag < 0)
00594     return NULL;
00595   else
00596     return fragment(frag);
00597 }
00598 
00599 // return a 'default' value for a given atom name
00600 float BaseMolecule::default_radius(const char *nm) {
00601   float val = 1.5;
00602 
00603   // some names start with a number
00604   while (*nm && isdigit(*nm)) 
00605     nm++;
00606 
00607   if (*nm != '\0') {
00608     switch(toupper(nm[0])) {
00609       // These are similar to the values used by X-PLOR with sigma=0.8
00610       // see page 50 of the X-PLOR 3.1 manual
00611       case 'H': val = 1.00f; break;
00612       case 'C': val = 1.50f; break;
00613       case 'N': val = 1.40f; break;
00614       case 'O': val = 1.30f; break;
00615       case 'F': val = 1.20f; break;
00616       case 'S': val = 1.90f; break;
00617     }
00618   }
00619 
00620   return val;
00621 }
00622 
00623 
00624 // return a 'default' value for a given atom name
00625 float BaseMolecule::default_mass(const char *nm) {
00626   float val = 12.0;
00627 
00628   // some names start with a number
00629   while (*nm && isdigit(*nm)) 
00630     nm++;
00631 
00632   if (*nm != '\0') {
00633     char c0 = toupper(nm[0]);
00634     char c1 = toupper(nm[1]); // might be '\0'...
00635     switch(c0) {
00636       case 'H': val = 1.00800f; break;
00637 
00638       case 'C': 
00639         if (c1 == 'L')
00640           val = 35.453f; // "CL"
00641         else
00642           val = 12.011f; // "C" 
00643         break;
00644 
00645       case 'M': 
00646         if (c1 == 'G')
00647           val = 24.3050f; // "MG"
00648         break;
00649 
00650       case 'N': 
00651         if (c1 == 'A')
00652           val = 22.98977f; // "NA"
00653         else
00654           val = 14.00700f; // "N"
00655         break;
00656 
00657       case 'O': val = 15.99900f; break;
00658   
00659       case 'F':
00660         if (c1 == 'E') 
00661           val = 55.84700f; // "FE"
00662         else
00663           val = 18.998f;   // "F"
00664         break;
00665 
00666       case 'B':
00667         if (c1 == 'R')
00668           val = 79.9040f; // "BR"
00669         break;
00670 
00671       case 'Z':
00672         if (c1 == 'N')
00673           val = 65.3700f; // "ZN"
00674         break;
00675 
00676       case 'I': val = 126.90447f; break;                                    
00677       case 'K': val = 39.09830f; break;       
00678       case 'P': val = 30.97376f; break;
00679       case 'S': val = 32.06000f; break;
00680     }
00681   }
00682 
00683   return val;
00684 }
00685 
00686 
00687 float BaseMolecule::default_charge(const char *) {
00688   // return 0 for everything; later, when we put in a more reliable
00689   // system for determining the charge that's user-configurable,
00690   // we can start assigning more realistic charges.
00691   return 0.0f;
00692 }
00693 
00694 
00695 // count the number of unique bonds in the structure
00696 int BaseMolecule::count_bonds(void) {
00697   int i, j;
00698   int count=0;
00699 
00700   for (i=0; i<nAtoms; i++) {
00701     int nbonds = atomList[i].bonds;
00702     const int *bonds = &atomList[i].bondTo[0];
00703 
00704     for (j=0; j<nbonds; j++) {
00705       if (bonds[j] > i)
00706         count++;
00707     }
00708   }
00709 
00710   return count;
00711 }
00712 
00713 
00714 void BaseMolecule::clear_bonds(void) {
00715   int i;
00716   for (i=0; i<nAtoms; i++)
00717     atomList[i].bonds = 0;
00718 }
00719 
00720 
00721 // analyze the molecule for more than just the atom/bond information
00722 // This is here since it is called _after_ the molecule is added to
00723 // the MoleculeList.  Thus, there is a Tcl callback to allow the
00724 // user to update the bond information (or other fields?) before
00725 // the actual search.
00726 void BaseMolecule::analyze(void) {
00727   need_find_bonds = 0; // at this point it's too late
00728 
00729   // I have to let 0 atoms in because I want to be able to read things
00730   // like electron density maps, which have no atoms.
00731   // It is kinda wierd, then to make BaseMolecule be at the top of the
00732   // heirarchy.  Oh well.
00733   if (nAtoms < 1)
00734     return;
00735 
00736   wkf_timerhandle tm = wkf_timer_create();
00737   wkf_timer_start(tm);
00738 
00739   // call routines to find different characteristics of the molecule
00740   msgInfo << "Analyzing structure ..." << sendmsg;
00741   msgInfo << "   Atoms: " << nAtoms << sendmsg;
00742 
00743   // count unique bonds/angles/dihedrals/impropers/cterms
00744   int total_bondcount = count_bonds();
00745   double bondtime = wkf_timer_timenow(tm);
00746 
00747   msgInfo << "   Bonds: " << total_bondcount << sendmsg;
00748   msgInfo << "   Angles: " << num_angles()
00749           << "  Dihedrals: " << num_dihedrals()
00750           << "  Impropers: " << num_impropers()
00751           << "  Cross-terms: " << num_cterms()
00752           << sendmsg;
00753 
00754   msgInfo << "   Bondtypes: " << bondTypeNames.num()
00755           << "  Angletypes: " << angleTypeNames.num()
00756           << "  Dihedraltypes: " << dihedralTypeNames.num()
00757           << "  Impropertypes: " << improperTypeNames.num()
00758           << sendmsg;
00759 
00760   // restore residue and fragment lists to pristine state
00761   residueList.clear();
00762   fragList.clear();
00763   pfragList.clear();   
00764   pfragCyclic.clear(); 
00765 #if defined(VMDFASTRIBBONS)
00766   pfragCPList.clear(); 
00767 #endif
00768   nfragList.clear();   
00769   nfragCyclic.clear(); 
00770 #if defined(VMDFASTRIBBONS)
00771   nfragCPList.clear(); 
00772 #endif
00773 
00774   // assign per-atom backbone types
00775   find_backbone();
00776   double backbonetime = wkf_timer_timenow(tm);
00777 
00778   // find all the atoms in a resid connected to DNA/RNA/PROTEIN/WATER
00779   // also, assign a unique resid (uniq_resid) to each atom
00780   nResidues = find_residues();
00781   double findrestime = wkf_timer_timenow(tm);
00782   msgInfo << "   Residues: " << nResidues << sendmsg;
00783 
00784   nWaters = find_waters();
00785   double findwattime = wkf_timer_timenow(tm);
00786   msgInfo << "   Waters: " << nWaters << sendmsg;
00787   
00788   // determine which residues are connected to each other
00789   bonderrorcount=0; // reset error count before residue connectivity search
00790   find_connected_residues(nResidues); 
00791   double findconrestime = wkf_timer_timenow(tm);
00792  
00793   nSegments = find_segments(); 
00794   msgInfo << "   Segments: " << nSegments << sendmsg;
00795 
00796   nFragments = find_fragments();
00797   msgInfo << "   Fragments: " << nFragments;
00798 
00799   nProteinFragments = pfragList.num();
00800   msgInfo << "   Protein: " << nProteinFragments;
00801 
00802   nNucleicFragments = nfragList.num();
00803   msgInfo << "   Nucleic: " << nNucleicFragments << sendmsg;
00804   double findfragstime = wkf_timer_timenow(tm);
00805   
00806   // NOTE: The current procedure incorrectly identifies some lipid 
00807   // atoms as "ATOMNUCLEICBACK" (but not as "nucleic") as well
00808   // as some single water oxygens as "backbone". Here, we 
00809   // correct this by setting all atoms of non-polymeric residue types
00810   // to be "ATOMNORMAL" (i.e.: not backbone).
00811   int i;
00812   for (i=0; i<nAtoms; i++) {
00813     MolAtom *a = atom(i); 
00814     if ((a->residueType != RESNUCLEIC) && (a->residueType != RESPROTEIN)) 
00815       a->atomType = ATOMNORMAL;
00816   }
00817   double fixlipidtime = wkf_timer_timenow(tm);
00818 
00819   // Search for hydrogens
00820   // XXX Must be done after the rest of the structure finding routines,
00821   // because those routines assume that anything that isn't NORMAL is
00822   // a backbone atom.
00823   // We use the name-based definition used in the IS_HYDROGEN macro
00824   for (i=0; i<nAtoms; i++) {
00825     MolAtom *a = atom(i);
00826     const char *aname = atomNames.name(a->nameindex);
00827     if (aname != NULL && IS_HYDROGEN(aname))
00828       a->atomType = ATOMHYDROGEN;
00829   }
00830   double findhydrogentime = wkf_timer_timenow(tm);
00831 
00832 #if defined(VMDFASTRIBBONS)
00833   calculate_ribbon_controlpoints();
00834 #endif
00835   double calcribbontime = wkf_timer_timenow(tm);
00836 
00837 #if 1
00838   if (getenv("VMDBASEMOLECULETIMING") != NULL) {
00839     printf("BaseMolecule::analyze() runtime breakdown:\n");
00840     printf("  %.2f bonds\n", bondtime);
00841     printf("  %.2f backbone\n", backbonetime - bondtime);
00842     printf("  %.2f findres\n", findrestime -  backbonetime);
00843     printf("  %.2f findwat\n", findwattime - findrestime);
00844     printf("  %.2f findconres\n", findconrestime - findwattime);
00845     printf("  %.2f findfrags\n", findfragstime - findconrestime);
00846     printf("  %.2f fixlipds\n", fixlipidtime - findfragstime);
00847     printf("  %.2f findH\n", findhydrogentime - fixlipidtime);
00848     printf("  %.2f calcribbons\n", calcribbontime - findhydrogentime);
00849   }
00850 #endif
00851  
00852   wkf_timer_destroy(tm);
00853 }
00854 
00855 
00857 int BaseMolecule::find_backbone(void) {
00858 #if 1
00859   const char * protnames[] = { "CA", "C", "O", "N", NULL };
00860   const char * prottermnames[] = { "OT1", "OT2", "OXT", "O1", "O2", NULL };
00861   const char * nucnames[] = { "P", "O1P", "O2P", "OP1", "OP2", 
00862                               "C3*", "C3'", "O3*", "O3'", "C4*", "C4'", 
00863                               "C5*", "C5'", "O5*", "O5'", NULL };
00864 #if 0
00865   // non-backbone nucleic acid atom names
00866   const char * nucnames2[] = { "C1*", "C1'", "C2*", "C2'", 
00867                                "O2*", "O2'", "O4*", "O4'", NULL };
00868 #endif
00869   const char *nuctermnames[] = { "H5T", "H3T", NULL };
00870 
00871   ResizeArray<int> prottypecodes;
00872   ResizeArray<int> prottermtypecodes;
00873   ResizeArray<int> nuctypecodes;
00874   ResizeArray<int> nuctermtypecodes;
00875   const char ** str;
00876   int tc, i, j, k;
00877 
00878   // proteins
00879   for (str=protnames; *str!=NULL; str++) {
00880     if ((tc = atomNames.typecode((char *) *str)) >= 0) 
00881       prottypecodes.append(tc);
00882   }
00883   for (str=prottermnames; *str!=NULL; str++) {
00884     if ((tc = atomNames.typecode((char *) *str)) >= 0) 
00885       prottermtypecodes.append(tc);
00886   }
00887 
00888   // nucleic acids
00889   for (str=nucnames; *str!=NULL; str++) {
00890     if ((tc = atomNames.typecode((char *) *str)) >= 0) 
00891       nuctypecodes.append(tc);
00892   }
00893   for (str=nuctermnames; *str!=NULL; str++) {
00894     if ((tc = atomNames.typecode((char *) *str)) >= 0) 
00895       nuctermtypecodes.append(tc);
00896   }
00897 
00898   int numprotcodes = prottypecodes.num();
00899   int numprottermcodes = prottermtypecodes.num();
00900   int numnuccodes = nuctypecodes.num();
00901   int numnuctermcodes = nuctermtypecodes.num();
00902   
00903   int *protcodes = &prottypecodes[0];
00904   int *prottermcodes = &prottermtypecodes[0];
00905   int *nuccodes = &nuctypecodes[0];
00906   int *nuctermcodes = &nuctermtypecodes[0];
00907 
00908   // short-circuit protein and nucleic residue analysis if we 
00909   // don't find any atom names we recognize
00910   if ((numprotcodes + numprottermcodes + 
00911        numnuccodes + numnuctermcodes) == 0) {
00912     // initialize all atom types to non-backbone
00913     for (i=0; i<nAtoms; i++) {
00914       MolAtom *a = atom(i);
00915       a->atomType = ATOMNORMAL;
00916     }
00917   } else {
00918     // loop over all atoms assigning atom backbone type flags
00919     for (i=0; i<nAtoms; i++) {
00920       MolAtom *a = atom(i);
00921  
00922       // initialize atom type to non-backbone
00923       a->atomType = ATOMNORMAL;
00924 
00925       // check for protein backbone atom names
00926       for (j=0; j < numprotcodes; j++) {
00927         if (a->nameindex == protcodes[j]) {
00928           a->atomType = ATOMPROTEINBACK;
00929           break;
00930         }
00931       }
00932 
00933       // check terminal residue names as well
00934       for (j=0; j < numprottermcodes; j++) {
00935         if (a->nameindex == prottermcodes[j]) { // check if OT1, OT2
00936           for (k=0; k < a->bonds; k++) {
00937             if (atom(a->bondTo[k])->atomType == ATOMPROTEINBACK) {
00938               a->atomType = ATOMPROTEINBACK;
00939               break;
00940             }
00941           }
00942         }
00943       }
00944 
00945       // check if in nucleic backbone, if not already set
00946       if (!(a->atomType)) {
00947         for (j=0; j < numnuccodes; j++) {
00948           if (a->nameindex == nuccodes[j]) {
00949             a->atomType = ATOMNUCLEICBACK;
00950             break;
00951           }
00952         }
00953       }
00954 
00955       // check if nucleic terminal atom names
00956       for (j=0; j < numnuctermcodes; j++) {
00957         if (a->nameindex == nuctermcodes[j]) {
00958           for (k=0; k < a->bonds; k++) {
00959             if (atom(a->bondTo[k])->atomType == ATOMNUCLEICBACK) {
00960               a->atomType = ATOMNUCLEICBACK;
00961               break;
00962             }
00963           }
00964         }
00965       }
00966     }
00967   }
00968 #else
00969   int i, j, k;
00970 
00971   // Search for the protein backbone
00972   int protypes[4];
00973   protypes[0] = atomNames.typecode((char *) "CA");
00974   protypes[1] = atomNames.typecode((char *) "C");
00975   protypes[2] = atomNames.typecode((char *) "O");
00976   protypes[3] = atomNames.typecode((char *) "N");
00977 
00978   // special case for terminal oxygens that miss the search for O
00979   // by looking for ones connected to a C
00980   int termtypes[5];
00981   termtypes[0] = atomNames.typecode((char *) "OT1"); // standard PDB names
00982   termtypes[1] = atomNames.typecode((char *) "OT2");
00983   termtypes[2] = atomNames.typecode((char *) "OXT"); // synonym for OT2
00984   termtypes[3] = atomNames.typecode((char *) "O1");  // Gromacs force field 
00985   termtypes[4] = atomNames.typecode((char *) "O2");  // atom names
00986 
00987   // search for the DNA/RNA backbone;  the atom names are:
00988   // for the phosphate:  P, O1P, O2P, OP1, OP2
00989   // for the rest: O3', C3', C4', C5', O5'
00990   // (or O3*, C3*, C4*, C5*, O5*)
00991   int nuctypes[15];
00992   nuctypes[ 0] = atomNames.typecode((char *) "P");
00993   nuctypes[ 1] = atomNames.typecode((char *) "O1P"); // old PDB files
00994   nuctypes[ 2] = atomNames.typecode((char *) "O2P"); // old PDB files
00995   nuctypes[ 3] = atomNames.typecode((char *) "OP1"); // new PDB files
00996   nuctypes[ 4] = atomNames.typecode((char *) "OP2"); // new PDB files
00997   nuctypes[ 5] = atomNames.typecode((char *) "C3*");
00998   nuctypes[ 6] = atomNames.typecode((char *) "C3'");
00999   nuctypes[ 7] = atomNames.typecode((char *) "O3*");
01000   nuctypes[ 8] = atomNames.typecode((char *) "O3'");
01001   nuctypes[ 9] = atomNames.typecode((char *) "C4*");
01002   nuctypes[10] = atomNames.typecode((char *) "C4'");
01003   nuctypes[11] = atomNames.typecode((char *) "C5*");
01004   nuctypes[12] = atomNames.typecode((char *) "C5'");
01005   nuctypes[13] = atomNames.typecode((char *) "O5*");
01006   nuctypes[14] = atomNames.typecode((char *) "O5'");
01007 
01008 #if 0
01009   // non-backbone nucleic acid atom names
01010   nuctypes[  ] = atomNames.typecode((char *) "C1*");
01011   nuctypes[  ] = atomNames.typecode((char *) "C1'");
01012   nuctypes[  ] = atomNames.typecode((char *) "C2*");
01013   nuctypes[  ] = atomNames.typecode((char *) "C2'");
01014   nuctypes[  ] = atomNames.typecode((char *) "O2*");
01015   nuctypes[  ] = atomNames.typecode((char *) "O2'");
01016   nuctypes[  ] = atomNames.typecode((char *) "O4*");
01017   nuctypes[  ] = atomNames.typecode((char *) "O4'");
01018 #endif
01019 
01020   // special case for terminal nucleic residues
01021   int nuctermtypes[2];
01022   nuctermtypes[0] = atomNames.typecode((char *) "H5T"); // standard names
01023   nuctermtypes[1] = atomNames.typecode((char *) "H3T");
01024 
01025 
01026   // loop over all atoms assigning atom backbone type flags
01027   for (i=0; i<nAtoms; i++) {
01028     MolAtom *a = atom(i);
01029  
01030     // initialize atom type to non-backbone
01031     a->atomType = ATOMNORMAL;
01032 
01033     // check for protein backbone atom names
01034     for (j=0; j < 4; j++) {
01035       if (a->nameindex == protypes[j]) {
01036         a->atomType = ATOMPROTEINBACK;
01037         break;
01038       }
01039     }
01040 
01041     // check terminal residue names as well
01042     for (j=0; j < 4; j++) {
01043       if (a->nameindex == termtypes[j]) { // check if OT1, OT2
01044         for (k=0; k < a->bonds; k++) {
01045           if (atom(a->bondTo[k])->atomType == ATOMPROTEINBACK) {
01046             a->atomType = ATOMPROTEINBACK;
01047             break;
01048           }
01049         }
01050       }
01051     }
01052   
01053     // check if in nucleic backbone, if not already set
01054     if (!(a->atomType)) {
01055       for (j=0; j < 15; j++) {
01056         if (a->nameindex == nuctypes[j]) {
01057           a->atomType = ATOMNUCLEICBACK;
01058           break;
01059         }
01060       }
01061     }
01062 
01063     // check if nucleic terminal atom names
01064     for (j=0; j < 2; j++) {
01065       if (a->nameindex == nuctermtypes[j]) {
01066         for (k=0; k < a->bonds; k++) {
01067           if (atom(a->bondTo[k])->atomType == ATOMNUCLEICBACK) {
01068             a->atomType = ATOMNUCLEICBACK;
01069             break;
01070           }
01071         }
01072       }
01073     }
01074   }
01075 #endif
01076 
01077   return 0; 
01078 }
01079 
01080 
01081 
01082 // find water molecules based on the residue name
01083 // from the documentation for molscript, these are possible
01084 // waters:
01085 // type H2O HH0 OHH HOH OH2 SOL WAT
01086 // as well, I add TIP, TIP2, TIP3, and TIP4
01087 // The count is the number of sets of connected RESWATERS
01088 int BaseMolecule::find_waters(void) {
01089 #if 1
01090   const char *watresnames[] = { "H2O", "HHO", "OHH", "HOH", "OH2", 
01091                                 "SOL", "WAT", 
01092                                 "TIP", "TIP2", "TIP3", "TIP4",
01093                                 "SPC", NULL };
01094 
01095   // SPC conflicts with a PDB compound:
01096   //   http://minerva.roca.csic.es/hicup/SPC/spc_pdb.txt
01097   //
01098   // XXX we should add a check to make sure that there are only 
01099   //     three atoms in the residue when all is said and done. If there
01100   //     are more, then we should undo the assignment as a water and mark it
01101   //     as protein etc as appropriate.  This is tricky since at this stage
01102   //     we haven't done any connectivity tests etc, and we're only working
01103   //     with individual atoms.  Perhaps its time to re-think this logic.
01104 
01105   ResizeArray<int> watrestypecodes;
01106   const char ** str;
01107   int tc, i, j;
01108 
01109   for (str=watresnames; *str!=NULL; str++) {
01110     if ((tc = resNames.typecode((char *) *str)) >= 0)
01111       watrestypecodes.append(tc);
01112   }
01113   int numwatrescodes = watrestypecodes.num();
01114   int *watrescodes = &watrestypecodes[0];
01115 
01116   // Short-circuit the water search if no water residue name typecodes
01117   if (numwatrescodes == 0) {
01118     return 0;
01119   } else {
01120     for (i=0; i<nAtoms; i++) {
01121       MolAtom *a = atom(i);
01122       if (a->residueType == RESNOTHING) {  // make sure it isn't named yet
01123         for (j=0; j<numwatrescodes; j++) {
01124           if (a->resnameindex == watrescodes[j]) {
01125             a->residueType = RESWATERS;
01126             break;
01127           }
01128         }
01129       }
01130     }
01131   }
01132 #else
01133   int i, j;
01134   int watertypes[12];
01135   watertypes[0] = resNames.typecode((char *) "H2O");
01136   watertypes[1] = resNames.typecode((char *) "HH0");
01137   watertypes[2] = resNames.typecode((char *) "OHH");
01138   watertypes[3] = resNames.typecode((char *) "HOH");
01139   watertypes[4] = resNames.typecode((char *) "OH2");
01140   watertypes[5] = resNames.typecode((char *) "SOL");
01141   watertypes[6] = resNames.typecode((char *) "WAT");
01142   watertypes[7] = resNames.typecode((char *) "TIP");
01143   watertypes[8] = resNames.typecode((char *) "TIP2");
01144   watertypes[9] = resNames.typecode((char *) "TIP3");
01145   watertypes[10] = resNames.typecode((char *) "TIP4");
01146 
01147   // this conflicts with a PDB compound:
01148   //   http://minerva.roca.csic.es/hicup/SPC/spc_pdb.txt
01149   //
01150   // XXX we should add a check to make sure that there are only 
01151   //     three atoms in the residue when all is said and done. If there
01152   //     are more, then we should undo the assignment as a water and mark it
01153   //     as protein etc as appropriate.  This is tricky since at this stage
01154   //     we haven't done any connectivity tests etc, and we're only working
01155   //     with individual atoms.  Perhaps its time to re-think this logic.
01156   watertypes[11] = resNames.typecode((char *) "SPC");
01157 
01158   // Short-circuit the water search if no water residue name typecodes
01159   // exist in the molecule yet.  This is a big performance gain in cases
01160   // when we're working with large structures containing billions of atoms
01161   // with no solvent added yet.
01162   int waterresnameexists=0;
01163   for (j=0; j<12; j++) {
01164     if (watertypes[j] != -1) {
01165       waterresnameexists=1;
01166     }
01167   }
01168   if (!waterresnameexists) {
01169     return 0;
01170   }
01171 
01172   for (i=0; i<nAtoms; i++) {
01173     MolAtom *a = atom(i);
01174     if (a->residueType == RESNOTHING) {  // make sure it isn't named yet
01175       for (j=0; j<12; j++) {
01176         if (watertypes[j] == a->resnameindex) {
01177           a->residueType = RESWATERS;
01178           break;
01179         }
01180       }
01181     }
01182   }
01183 #endif
01184  
01185   int count = find_connected_waters2();
01186 
01187   return count;   
01188 }
01189 
01190 
01191 // if this is a RESWATERS with index idx, mark it and find if
01192 // any of its neighbors are RESWATERS
01193 // this does a depth-first search with RECURSION.
01194 void BaseMolecule::find_connected_waters(int i, char *tmp) {
01195   MolAtom *a = atom(i);
01196   int j;
01197   if (a->residueType == RESWATERS && !tmp[i]) {
01198     tmp[i] = TRUE;
01199     for (j=0; j<a->bonds; j++) {
01200       find_connected_waters(a->bondTo[j], tmp);
01201     }
01202   }
01203 }
01204 
01205 
01206 // if this is a RESWATERS with index idx, mark it and find if
01207 // any of its neighbors are RESWATERS
01208 int BaseMolecule::find_connected_waters2(void) {
01209   MolAtom *a;
01210   int count, i;
01211   IntStackHandle s;
01212 
01213   // allocate cleared aray of tmp flags
01214   char *tmp = (char *) calloc(1, nAtoms * sizeof(char));
01215 
01216   s = intstack_create(nAtoms);
01217 
01218   for (count=0, i=0; i<nAtoms; i++) {
01219     if (atom(i)->residueType == RESWATERS && !tmp[i]) {
01220       int nextatom;
01221 
01222       count++;
01223       intstack_push(s, i);
01224     
01225       // find and mark all connected waters 
01226       while (!intstack_pop(s, &nextatom)) { 
01227         int j;
01228 
01229         a = atom(nextatom);
01230         tmp[nextatom] = TRUE;
01231 
01232         for (j=a->bonds - 1; j>=0; j--) {
01233           int bi = a->bondTo[j];
01234           MolAtom *b = atom(bi);
01235           if (b->residueType == RESWATERS && !tmp[bi])
01236             intstack_push(s, bi);
01237         }
01238       }
01239     }
01240   }
01241 
01242   intstack_destroy(s);
01243   free(tmp);
01244 
01245   return count;
01246 }
01247 
01248 
01249 
01250 // assign a uniq resid (uniq_resid) to each set of connected atoms
01251 // with the same residue id.  There could be many residues with the
01252 // same id, but not connected (the SSN problem - SSNs are not unique
01253 // so don't use them as the primary key)
01254 int BaseMolecule::make_uniq_resids(int *flgs) {
01255   int i;
01256   int num_residues = 0;
01257   IntStackHandle s = intstack_create(nAtoms);
01258 
01259   for (i=0; i<nAtoms; i++) {
01260     if (!flgs[i]) {  // not been numbered
01261       // find connected atoms to i with the same resid and label
01262       // it with the uniq_resid
01263       MolAtom *a = atom(i);
01264       int resid = a->resid;
01265 //      char *insertion = a->insertionstr;
01266       char insertioncode = a->insertionstr[0];
01267 
01268       intstack_push(s, i);
01269       int nextatom;
01270 
01271       // Loop over all atoms we're bonded to in the same chain/segname
01272       while (!intstack_pop(s, &nextatom)) {
01273         MolAtom *a = atom(nextatom);
01274         a->uniq_resid = num_residues;  // give it the new resid number
01275         flgs[nextatom] = TRUE;         // mark this atom done
01276   
01277         int j;
01278         for (j=a->bonds - 1; j>=0; j--) {
01279           int bi = a->bondTo[j];
01280           if (flgs[bi] == 0) {
01281             MolAtom *b = atom(bi);
01282             if (a->chainindex == b->chainindex && 
01283                 a->segnameindex == b->segnameindex &&
01284                 b->resid == resid && b->insertionstr[0] == insertioncode)
01285 //                b->resid == resid && !strcmp(b->insertionstr, insertion))
01286               intstack_push(s, bi);
01287           }
01288         }
01289       }
01290 
01291       num_residues++;
01292     }
01293   }
01294 
01295   intstack_destroy(s);
01296 
01297   return num_residues;
01298 }
01299 
01300 
01301 // find n backbone atoms connected together with the given residueid
01302 // return the total count
01303 // this assumes that the given atom (atomidx) is correct
01304 int BaseMolecule::find_connected_backbone(IntStackHandle s, int backbone,
01305                          int atomidx, int residueid, int tmpid, int *flgs) {
01306   if (flgs[atomidx] != 0)
01307     return 0; // already done
01308 
01309   MolAtom *x = atom(atomidx);
01310   if (x->atomType != backbone || x->resid != residueid)
01311     return 0; // not a backbone atom, or resid doesn't match
01312 
01313   intstack_popall(s); // just in case
01314   intstack_push(s, atomidx);
01315   int nextatom;
01316   int count = 0;
01317    
01318   // find and mark connected backbone atoms
01319   while (!intstack_pop(s, &nextatom)) {
01320     MolAtom *a = atom(nextatom);
01321     flgs[nextatom] = tmpid;
01322     count++;
01323 
01324     int j;
01325     for (j=a->bonds - 1; j>=0; j--) {
01326       int bi = a->bondTo[j];
01327       if (flgs[bi] == 0) {
01328         MolAtom *b = atom(bi);
01329 
01330         // skip connections to atoms on different chains/segnames
01331         if (a->chainindex != b->chainindex || 
01332             a->segnameindex != b->segnameindex)
01333           continue;
01334 
01335         if (b->atomType == backbone && b->resid == residueid)
01336           intstack_push(s, bi);
01337       }
01338     }
01339   }
01340 
01341   return count;
01342 }
01343 
01344 
01345 // the find_connected_backbone left terms of flgs which need to be cleaned up
01346 void BaseMolecule::clean_up_connection(IntStackHandle s, int i, int tmpid, int *flgs) {
01347   if (flgs[i] != tmpid)  // been here before
01348     return;
01349 
01350   intstack_popall(s); // just in case
01351   intstack_push(s, i);
01352   int nextatom;
01353  
01354   // find and null out non-matching atom flags
01355   while (!intstack_pop(s, &nextatom)) {
01356     flgs[nextatom] = 0;
01357     MolAtom *a = atom(nextatom);
01358     int j;
01359     for (j=a->bonds - 1; j>=0; j--) {
01360       int bi = a->bondTo[j];
01361       if (flgs[bi] == tmpid) {
01362         intstack_push(s, bi);
01363       }
01364     }
01365   }
01366 }
01367 
01368 
01369 // Find connected backbone atoms with the same resid
01370 // if found, find all the atoms with the same resid
01371 // which are connected to those backbone atoms only through
01372 // atoms of the same resid
01373 void BaseMolecule::find_and_mark(IntStackHandle s, int n, int backbone,
01374                                  int restype, int *tmpid, int *flgs) {
01375   int i;
01376   int residueid; // the real resid
01377 
01378   intstack_popall(s); // just in case
01379   for (i=0; i<nAtoms; i++) {
01380     MolAtom *a = atom(i);   // look for a new backbone atom
01381     if (a->atomType == backbone && flgs[i] == 0) {
01382       residueid = a->resid;
01383       if (find_connected_backbone(s, backbone, i, residueid, *tmpid, flgs) >= n) {
01384         // if find was successful, start all over again
01385         clean_up_connection(s, i, *tmpid, flgs);
01386         // but mark all the Atoms connected to here
01387         find_connected_atoms_in_resid(s, restype, i, residueid, *tmpid, flgs);
01388         // and one more was made
01389         (*tmpid)++;
01390       } else {
01391         // clean things up so I won't have problems later
01392         clean_up_connection(s, i, *tmpid, flgs);
01393       }
01394     }
01395   }
01396 }
01397 
01398 
01399 // now that I know this resid is okay, mark it so
01400 void BaseMolecule::find_connected_atoms_in_resid(IntStackHandle s,
01401     int restype, int i, int residueid, int tmpid, int *flgs)
01402 {
01403   if (flgs[i] != 0 || atom(i)->resid != residueid)
01404     return;
01405 
01406   intstack_popall(s); // just in case
01407   intstack_push(s, i);
01408   int nextatom;
01409 
01410   // find and mark all connected residues in the same chain/segname
01411   while (!intstack_pop(s, &nextatom)) {
01412     flgs[nextatom] = tmpid;
01413     MolAtom *a = atom(nextatom);
01414     a->residueType = restype;
01415 
01416     int j;
01417     for (j=a->bonds - 1; j>=0; j--) {
01418       int bi = a->bondTo[j];
01419       MolAtom *b = atom(bi);
01420       if (flgs[bi] == 0 &&
01421           a->chainindex == b->chainindex &&
01422           a->segnameindex == b->segnameindex &&
01423           b->resid == residueid) {
01424         intstack_push(s, bi);
01425       }
01426     }
01427   }
01428 }
01429 
01430 
01431 int BaseMolecule::find_residues(void) {
01432   // flags used for connected atom searches
01433   // zero cleared at allocation time..
01434   int *flgs = (int *) calloc(1, nAtoms * sizeof(int)); 
01435   
01436   // assign a uniq resid (uniq_resid) to each set of connected atoms
01437   // with the same residue id.  There could be many residues with the
01438   // same id, but not connected (the SSN problem - SSNs are not unique
01439   // so don't use them as the primary key)
01440   int num_residues = make_uniq_resids(flgs);
01441    
01442   int back_res_count = 1; // tmp count of number of residues on some backbone
01443   memset(flgs, 0, nAtoms * sizeof(int)); // clear flags array
01444 
01445   IntStackHandle s = intstack_create(nAtoms);
01446   
01447   //  hunt for the proteins
01448   // there must be 4 PROTEINBACK atoms connected and with the same resid
01449   // then all connected atoms will be marked as PROTEIN atoms
01450   // this gets everything except the terminals
01451   find_and_mark(s, 4, ATOMPROTEINBACK, RESPROTEIN, &back_res_count, flgs);
01452   
01453   // do the same for nucleic acids
01454   // XXX we might not want to check for the phosphate (P and 2 O's).  Here's
01455   // the quick way I can almost do that.  Unfortionately, that
01456   // messes up nfragList, since it needs a P to detect an end
01457   find_and_mark(s, 4, ATOMNUCLEICBACK, RESNUCLEIC, &back_res_count, flgs);
01458 
01459   intstack_destroy(s);
01460   
01461   free(flgs);
01462   return num_residues;
01463 }
01464 
01465 int BaseMolecule::find_atom_in_residue(const char *name, int residue) {
01466   int nametype = atomNames.typecode(name);
01467   if (nametype < 0)
01468     return -2;
01469 
01470   return find_atom_in_residue(nametype, residue);
01471 }
01472 
01473 
01474 // find which residues are connected to which
01475 // remember, I already have the uniq_id for each atom
01476 void BaseMolecule::find_connected_residues(int num_residues) {
01477   int i, j;
01478   residueList.appendN(NULL, num_residues); // init the list to NULLs
01479  
01480   for (i=nAtoms-1; i>=0; i--) {      // go through all the atoms
01481     MolAtom *a = atom(i);
01482     j = a->uniq_resid;
01483     if (residueList[j] == NULL) {    // then init the residue
01484       residueList[j] = new Residue(a->resid, a->residueType);
01485     }
01486     // Tell the residue that this atom is in it
01487     residueList[j]->add_atom(i);
01488   }
01489 
01490 
01491   // finally, check for unusual connections between residues, e.g. between
01492   // protein and water, or residues that have no atoms.
01493   int resmissingatomflag=0;
01494 
01495   // if we have more than 10M residues, skip the checks and text warnings
01496   if (num_residues > 10000000) {
01497     for (i=0; i<num_residues; i++) {
01498       Residue *res = residueList[i];
01499 
01500       // double check that everything was created
01501       if (res == NULL) {
01502         // no atom was found for this residue
01503         resmissingatomflag=1;
01504         res = new Residue((int) -1, RESNOTHING);
01505         residueList[i] = res;
01506       } 
01507     }
01508   } else {
01509     for (i=0; i<num_residues; i++) {
01510       Residue *res = residueList[i];
01511 
01512       // double check that everything was created
01513       if (res == NULL) {
01514         // no atom was found for this residue
01515         resmissingatomflag=1;
01516         res = new Residue((int) -1, RESNOTHING);
01517         residueList[i] = res;
01518       } 
01519 
01520       int bondfromtype = res->residueType;
01521       int numatoms = res->atoms.num();
01522       for (j=0; j<numatoms; j++) {
01523         MolAtom *a = atom(res->atoms[j]);
01524 
01525         // find off-residue bonds to residues of the same chain/segname
01526         int k;
01527         for (k=0; k<a->bonds; k++) {
01528           MolAtom *b = atom(a->bondTo[k]);
01529 
01530           // skip connections to atoms on different chains/segnames
01531           if (a->chainindex != b->chainindex || 
01532               a->segnameindex != b->segnameindex)
01533             continue;
01534          
01535           if (b->uniq_resid != i) {
01536             int bondtotype = residueList[b->uniq_resid]->residueType;
01537   
01538             if (bondfromtype != bondtotype) {
01539               if (i < b->uniq_resid) { // so that we only warn once
01540                 msgWarn << "Unusual bond between residues:  ";
01541                 msgWarn << residueList[i]->resid;
01542                 switch (bondfromtype) {
01543                   case RESPROTEIN: msgWarn << " (protein)"; break;
01544                   case RESNUCLEIC: msgWarn << " (nucleic)"; break;
01545                   case RESWATERS:  msgWarn << " (waters)"; break;
01546                   default:
01547                   case RESNOTHING: msgWarn << " (none)"; break;
01548                 }
01549                 msgWarn << " and ";
01550                 msgWarn << residueList[b->uniq_resid]->resid;
01551                 switch (bondtotype) {
01552                   case RESPROTEIN: msgWarn << " (protein)"; break;
01553                   case RESNUCLEIC: msgWarn << " (nucleic)"; break;
01554                   case RESWATERS:  msgWarn << " (waters)"; break;
01555                   default:
01556                   case RESNOTHING: msgWarn << " (none)"; break;
01557                 }
01558                 msgWarn << sendmsg;
01559               }
01560             }
01561           }
01562         }
01563       }
01564     }
01565   }
01566 
01567   // emit any warnings here, only once
01568   if (resmissingatomflag) {
01569     msgErr << "Mysterious residue creation in " 
01570            << "BaseMolecule::find_connected_residues." << sendmsg;
01571   }
01572 }
01573 
01574 
01575 // find all the residues connected to a specific residue
01576 int BaseMolecule::find_connected_fragments(void) {
01577   int i;
01578   int count = 0;
01579   // flags are all cleared to zeros initially
01580   char *flgs = (char *) calloc(1, residueList.num() * sizeof(char));
01581   IntStackHandle s = intstack_create(residueList.num());
01582 
01583   int atomsg = atomNames.typecode((char *) "SG"); // to find disulfide bonds
01584 
01585   int nextres;
01586   for (i=0; i<residueList.num(); i++) { // find unmarked fragment
01587     if (!flgs[i]) {
01588       fragList.append(new Fragment);
01589       intstack_push(s, i);
01590 
01591       // find and mark all connected residues with the same chain/segname
01592       while (!intstack_pop(s, &nextres)) {
01593         fragList[count]->append(nextres);
01594         Residue *res = residueList[nextres];
01595         res->fragment = count; // store residue's fragment
01596 
01597         int numatoms = res->atoms.num();
01598         int j;
01599         for (j=0; j<numatoms; j++) {
01600           MolAtom *a = atom(res->atoms[j]);
01601 
01602           // find all bonds to residues of the same chain/segname 
01603           int k;
01604           for (k=0; k<a->bonds; k++) {
01605             MolAtom *b = atom(a->bondTo[k]);
01606             int ri = b->uniq_resid;
01607 
01608             // skip connections to residues with different chains/segnames,
01609             // and don't follow disulfide bonds, as we want the order of
01610             // residue traversal to be correct so we can use it to build
01611             // subfragment lists later on
01612             if ((ri != i) &&
01613                 (flgs[ri] == 0) &&
01614                 (a->chainindex == b->chainindex) &&
01615                 (a->segnameindex == b->segnameindex) &&
01616                 ((a->nameindex != atomsg) || (b->nameindex != atomsg))) {
01617               flgs[ri] = TRUE;
01618               intstack_push(s, ri);
01619             }
01620           }
01621         }
01622       }
01623 
01624       count++;
01625     }
01626   }
01627 
01628   intstack_destroy(s);
01629   free(flgs);
01630 
01631   return count;
01632 }
01633 
01634 
01635 // find each collection of connected fragments
01636 int BaseMolecule::find_fragments(void) {
01637   int count = find_connected_fragments();  // find and mark its neighbors
01638 
01639 #if 1
01640   // find the protein subfragments
01641   find_subfragments(atomNames.typecode((char *) "N"), 
01642      -1,
01643      -1,
01644      atomNames.typecode((char *) "C"), 
01645      -1,
01646      -1,
01647      -1,
01648      RESPROTEIN, &pfragList);
01649 
01650 #if 0
01651   // find the nucleic acid subfragments
01652   find_subfragments(atomNames.typecode((char *) "P"), 
01653      atomNames.typecode((char *) "H5T"),
01654      -1,
01655      atomNames.typecode((char *) "O3'"),
01656      atomNames.typecode((char *) "O3*"),
01657      atomNames.typecode((char *) "H3T"),
01658      -1,
01659      RESNUCLEIC, &nfragList);
01660 #else
01661   // find the nucleic acid subfragments
01662   find_subfragments_topologically(
01663      RESNUCLEIC, &nfragList,
01664      atomNames.typecode((char *) "O3'"),
01665      atomNames.typecode((char *) "O3*"),
01666      atomNames.typecode((char *) "H3T"),
01667      -1);
01668 #endif
01669 #else
01670   find_subfragments_cyclic(&pfragList, RESPROTEIN);
01671   find_subfragments_cyclic(&nfragList, RESNUCLEIC);
01672 #endif
01673 
01674   // determine whether fragments are cyclic or not
01675   find_cyclic_subfragments(&pfragList, &pfragCyclic);
01676   find_cyclic_subfragments(&nfragList, &nfragCyclic);
01677 
01678   return count;
01679 }
01680 
01681 
01682 void BaseMolecule::find_subfragments_cyclic(ResizeArray<Fragment *> *subfragList, int restype) {
01683   int numfrags = fragList.num();
01684   int i, frag;
01685 
01686   // test each fragment to see if it's a candidate for the subfraglist
01687   for (frag=0; frag<numfrags; frag++) {
01688     int numres = fragList[frag]->num();       // residues in this frag
01689     int match=1; // start true, and falsify
01690 
01691     // check each residue to see they are all the right restype
01692     for (i=0; i<numres; i++) {
01693       int resid = (*fragList[frag])[i];
01694       if (residueList[resid]->residueType != restype) {
01695         match=0;
01696         break;
01697       }
01698     }
01699 
01700     // if we found a matching fragment, add it to the subfraglist
01701     if (match) {
01702       Fragment *frg = new Fragment;
01703 
01704       // add all of the residues for this fragment to the subfraglist
01705       for (i=0; i<numres; i++) {
01706         int resid = (*fragList[frag])[i];
01707         frg->append(resid);
01708       } 
01709 
01710       subfragList->append(frg);
01711     }    
01712   }
01713 }
01714 
01715 
01716 
01717 void BaseMolecule::find_cyclic_subfragments(ResizeArray<Fragment *> *subfragList, ResizeArray<int> *subfragCyclic) {
01718   int i, j, frag;
01719   int numfrags = subfragList->num();
01720 
01721   // check each fragment for cycles
01722   for (frag=0; frag<numfrags; frag++) {
01723     int numres   = (*subfragList)[frag]->num();       // residues in this frag
01724 
01725     // skip testing fragments containing zero residues
01726     if (numres < 1) {
01727       // record that this fragment is not cyclic
01728       subfragCyclic->append(0);
01729       continue;
01730     }
01731 
01732     int startres = (*(*subfragList)[frag])[0];        // first residue
01733     int endres   = (*(*subfragList)[frag])[numres-1]; // last residue
01734     int cyclic   = 0;
01735 
01736     // check for bonds between startres and endres
01737     int numatoms = residueList[endres]->atoms.num();
01738     int done = 0;
01739     for (i=0; (i < numatoms) && (!done); i++) {
01740       MolAtom *a = atom(residueList[endres]->atoms[i]);
01741       int nbonds = a->bonds;
01742       for (j=0; j < nbonds; j++) {
01743         MolAtom *b = atom(a->bondTo[j]);
01744 
01745         if (b->uniq_resid == startres) {
01746           cyclic=1;
01747           done=1;
01748           break;
01749         }
01750       }  
01751     }
01752 
01753     // record whether this fragment is cyclic or not
01754     subfragCyclic->append(cyclic);
01755   }
01756 }
01757 
01758 
01759 // this adds the current residue type to the *subfragList,
01760 // this finds the residue connected to the endatom atom type
01761 // and calls this function recursively on that residue
01762 // this will NOT work across NORMAL bonds
01763 void BaseMolecule::find_connected_subfragment(int resnum, int fragnum, 
01764          char *flgs, int endatom,  int altendatom, 
01765          int alt2endatom, int alt3endatom,
01766          int restype, 
01767          ResizeArray<Fragment *> *subfragList)
01768 {
01769   if (flgs[resnum] || residueList[resnum]->residueType != restype) 
01770       return;  // been here before, or this is no good
01771   (*subfragList)[fragnum]->append(resnum);    // add to the list
01772   flgs[resnum] = TRUE;                        // and prevent repeats
01773 
01774   // find the atom in this residue with the right type
01775   int i, j, nextres;
01776   MolAtom *a;
01777   for (i=residueList[resnum]->atoms.num() - 1; i>=0; i--) {
01778     a = atom(residueList[resnum]->atoms[i]);
01779     if (a->nameindex == endatom ||
01780         a->nameindex == altendatom ||
01781         a->nameindex == alt2endatom ||
01782         a->nameindex == alt3endatom) {   // found the end atom
01783       for (j=a->bonds-1; j>=0; j--) {    // look at the bonds
01784         // I can't look at if the residue "bond" is a PRO-PRO or NUC-NUC, since
01785         // that won't tell me if the atom involved is the endatom atom
01786         // This is important because I need to avoid things like S-S bonds
01787         // (note that I never checked that the end was bonded to a start on
01788         //  the next residue! - c'est la vie, or something like that
01789         if ((!(a->atomType == ATOMNORMAL && atom(a->bondTo[j])->atomType == ATOMNORMAL)) && // not backbone 
01790             (nextres = atom(a->bondTo[j])->uniq_resid) != resnum &&
01791             !flgs[nextres] ) { // found next residue, and unvisited
01792           find_connected_subfragment(nextres, fragnum, flgs, endatom,
01793               altendatom, alt2endatom, alt3endatom, restype, subfragList);
01794           return; // only find one; assume no branching
01795         }
01796       } // end of for
01797     } // end of found correct endtype
01798   } // searching atoms
01799 } // end of finding connected subfragment
01800 
01801 
01802 // find a class of fragments, and add them to the subfragment list
01803 void BaseMolecule::find_subfragments(int startatom, 
01804           int altstartatom, int alt2startatom,
01805           int endatom, int altendatom, int alt2endatom, int alt3endatom,
01806           int restype, ResizeArray<Fragment *> *subfragList)
01807 {
01808   // Short-circuit the fragment search if none of search typecodes exist
01809   if (startatom==-1 && altstartatom==-1 && alt2startatom==-1 && 
01810       endatom==-1 && altendatom==-1 && alt2endatom==-1 && alt3endatom==-1) {
01811     return;
01812   }
01813 
01814   int i, j, k;
01815   MolAtom *a;
01816   char *flgs = new char[residueList.num()];
01817   memset(flgs, 0, residueList.num() * sizeof(char));  // clear flags
01818 
01819   // Loop over all residues looking for candidate residues that start
01820   // a fragment.  A fragment starting residue must be an unvisited 
01821   // residue which has an startatom with no off residue bond to 
01822   // the same restype
01823   for (i=residueList.num()-1; i>=0; i--) {
01824     // test for previous visit, and whether it's the restype we want
01825     if (!flgs[i] && residueList[i]->residueType == restype) {
01826       // does this residue have a matching startatom
01827       for (j=residueList[i]->atoms.num()-1; j>=0; j--) { 
01828         int satom = (a=atom(residueList[i]->atoms[j]))->nameindex;
01829         if (satom == startatom || 
01830             satom == altstartatom || 
01831             satom == alt2startatom){
01832           for (k=a->bonds-1; k>=0; k--) {
01833             MolAtom *bondto = atom(a->bondTo[k]);
01834             // are there any off-residue bonds to the same restype
01835             if (bondto->uniq_resid != i && bondto->residueType == restype) {
01836               break; // if so then stop, so that k>=0
01837             }
01838           }
01839 
01840           // if we found a valid fragment start atom, find residues downchain
01841           if (k<0) { 
01842             subfragList->append(new Fragment);
01843             find_connected_subfragment(i, subfragList->num()-1, flgs, 
01844                   endatom, altendatom, alt2endatom, alt3endatom,
01845                   restype, subfragList);
01846           } // found starting residue
01847         } // found startatom
01848       } // going through atoms
01849     } // found restype
01850   } // going through residues
01851 
01852   // found 'em all
01853   delete [] flgs;
01854 } 
01855 
01856 
01857 // find a class of fragments, and add them to the subfragment list
01858 void BaseMolecule::find_subfragments_topologically(int restype, 
01859   ResizeArray<Fragment *> *subfragList, 
01860   int endatom, int altendatom, int alt2endatom, int alt3endatom) {
01861 
01862   // Short-circuit the fragment search if none of search typecodes exist
01863   if (endatom==-1 && altendatom==-1 && alt2endatom==-1 && alt3endatom==-1) {
01864     return;
01865   }
01866 
01867   int i; 
01868   char *flgs = new char[residueList.num()];
01869   memset(flgs, 0, residueList.num() * sizeof(char));  // clear flags
01870   int numres = residueList.num();
01871 
01872   // Loop over all residues looking for candidate residues that start
01873   // a fragment.  A fragment starting residue must be an unvisited
01874   // residue which has an startatom with no off residue bond to
01875   // the same restype
01876   for (i=0; i<numres; i++) {
01877     Residue *res = residueList[i];
01878 
01879     // test for previous visit, and whether it's the restype we want
01880     if (!flgs[i] && res->residueType == restype) {
01881       // if this residue only has 1 bond to a residue of the same restype
01882       // it must be a terminal residue
01883       int offresbondcount = 0;
01884       int j, k;
01885       int numatoms = res->atoms.num();
01886       for (j=0; j<numatoms; j++) {
01887         MolAtom *a = atom(res->atoms[j]);
01888 
01889         // find off-residue bonds
01890         for (k=0; k<a->bonds; k++) {
01891           MolAtom *b = atom(a->bondTo[k]);
01892           if (b->uniq_resid != i && 
01893               residueList[b->uniq_resid]->residueType == restype) {
01894             offresbondcount++;
01895           }
01896         }
01897       }
01898 
01899       // if we found a valid fragment start atom, find residues downchain
01900       if (offresbondcount == 1) {
01901         subfragList->append(new Fragment);
01902         find_connected_subfragment(i, subfragList->num()-1, flgs,
01903               endatom, altendatom, alt2endatom, alt3endatom,
01904               restype, subfragList);
01905       }
01906     } // found restype
01907   } // going through residues
01908 
01909   // found 'em all
01910   delete [] flgs;
01911 }
01912 
01913 
01914 #if defined(VMDFASTRIBBONS)
01915 // routine to pre-calculate lists of control point indices to
01916 // be used by the ribbon/cartoon representations
01917 void BaseMolecule::calculate_ribbon_controlpoints() {
01918   int onum, canum, frag, num, res;
01919 
01920   wkf_timerhandle tm = wkf_timer_create();
01921 
01922   msgInfo << "XXX Building pre-computed control point lists" << sendmsg;
01923 
01924   wkf_timer_start(tm);
01925 
01926   // Lookup atom typecodes ahead of time so we can use the most
01927   // efficient variant of find_atom_in_residue() in the main loop.
01928   // If we can't find the atom types we need, bail out immediately
01929   int CAtypecode  = atomNames.typecode("CA");
01930   int Otypecode   = atomNames.typecode("O");
01931   int OT1typecode = atomNames.typecode("OT1");
01932    
01933   // We can't draw a ribbon without CA and O atoms for guidance!
01934   if (CAtypecode < 0) {
01935     msgErr << "This structure has no identifiable CA atoms,"
01936            << "ribbon and cartoon representations will not be usable." 
01937            << sendmsg;
01938     return;
01939   }
01940   if ((Otypecode < 0) && (OT1typecode < 0)) {
01941     msgErr << "This structure has no identifiable Oxygen atoms,"
01942            << "ribbon and cartoon representations will not be usable." 
01943            << sendmsg;
01944     return;
01945   }
01946 
01947   //
01948   // calculate control point lists for the protein fragments
01949   //
01950 
01951   msgInfo << "XXX Building protein control point lists" << sendmsg;
01952 
01953   // go through each protein and find the CA and O atoms which are
01954   // eventually used to construct control points and perpendicular vectors
01955   ResizeArray<int> cpind;
01956   for (frag=0; frag<pfragList.num(); frag++) {
01957     num = pfragList[frag]->num();  // number of residues in this fragment
01958     if (num < 2) {
01959       // with less than two residues, we can't do anything useful, so skip
01960       nfragCPList.append(NULL);
01961       continue;
01962     }
01963 
01964     // check that we have a valid structure before continuing
01965       res = (*pfragList[frag])[0];
01966     canum = find_atom_in_residue(CAtypecode, res);
01967      onum = find_atom_in_residue(Otypecode, res);
01968 
01969     if (onum < 0 && OT1typecode >= 0) {
01970       onum = find_atom_in_residue(OT1typecode, res);
01971     }
01972     if (canum < 0 || onum < 0) {
01973       // can't find 1st CA or O of the protein fragment, so skip
01974       msgErr << "Fragment control point calc unable to identify target atoms" << sendmsg;
01975       nfragCPList.append(NULL);
01976       continue; 
01977     }
01978 
01979     // clear temp arrays before we begin
01980     cpind.clear();
01981 
01982     int loop;
01983     for (loop=0; loop<num; loop++) {
01984       res = (*pfragList[frag])[loop];
01985 
01986       // find next CA atom to be used as a spline control point
01987       canum = find_atom_in_residue(CAtypecode, res);
01988 
01989       // find next O atom to be used for spline orientation vector
01990       onum = find_atom_in_residue(Otypecode, res);
01991       if (onum < 0 && OT1typecode >= 0) {
01992         onum = find_atom_in_residue(OT1typecode, res);
01993       }
01994 
01995       // add the atom indices to the control point list
01996       cpind.append(canum);
01997       cpind.append(onum);
01998     }
01999 
02000     // copy this fragment's control point lists to permanent storage
02001     if (cpind.num() == 2L*num) {
02002       int *cpindices = new int[cpind.num()];
02003       memcpy(cpindices, &cpind[0], long(cpind.num()) * sizeof(int));
02004 
02005       // add control point list to the master list
02006       nfragCPList.append(cpindices);
02007     } else {
02008       msgErr << "Unexpected failure in control point calculation" << sendmsg;
02009       nfragCPList.append(NULL);
02010     }
02011   } 
02012 
02013   wkf_timer_stop(tm);
02014 
02015   msgInfo << "XXX Calc time for protein cplist: " 
02016           << wkf_timer_time(tm) << sendmsg;
02017   msgInfo << "XXX done building protein control point lists" << sendmsg;
02018 
02019   //
02020   // calculate control point lists for the nucleic fragments
02021   //
02022 
02023   wkf_timer_destroy(tm);
02024 }
02025 #endif
02026 
02027 #if defined(VMDWITHCARBS)
02028 
02029 // Routines for detecting carbohydrate rings
02030 // contributed by Simon Cross and Michelle Kuttel
02031 // XXX TODO:
02032 //   don't create hashes as pointers, rather just pass pointers for them
02033 //   into functions which need it.
02034 
02035 // find all small rings and links between them
02036 void BaseMolecule::find_small_rings_and_links(int maxpathlength, int maxringsize) {
02037   // skip ring finding if we've already done it
02038   if (maxpathlength == currentMaxPathLength && maxringsize == currentMaxRingSize)
02039     return;
02040   currentMaxPathLength = maxpathlength;
02041   currentMaxRingSize = maxringsize;
02042 
02043   // Clear smallringList, smallringLinkages
02044   smallringList.clear();
02045   smallringLinkages.clear();
02046 
02047   // find groups of atoms bonded into small rings  
02048   find_small_rings(maxringsize);
02049 #if 0
02050   msgInfo << "   Rings: " << smallringList.num() << sendmsg;
02051 #endif
02052   
02053   // orientate the rings if possible
02054   orientate_small_rings(maxringsize);
02055   int nOrientatedRings = 0;
02056   for (int i=0; i<smallringList.num(); i++) {
02057     if (smallringList[i]->orientated) 
02058       nOrientatedRings++;
02059   }
02060 #if 0
02061   msgInfo << "   Rings orientated: " << nOrientatedRings << sendmsg;
02062 #endif  
02063   // find paths between rings
02064   find_orientated_small_ring_linkages(maxpathlength, maxringsize);
02065 #if 0
02066   msgInfo << "   Ring Paths: " << smallringLinkages.paths.num() << sendmsg;
02067 #endif  
02068 }
02069 
02070 // find all loops less than a given size
02071 int BaseMolecule::find_small_rings(int maxringsize) {
02072   int n_back_edges, n_rings;
02073   ResizeArray<int> back_edge_src, back_edge_dest;
02074     
02075   n_back_edges = find_back_edges(back_edge_src, back_edge_dest);
02076   n_rings = find_small_rings_from_back_edges(maxringsize, back_edge_src, back_edge_dest);
02077 
02078 #if 1
02079   if (getenv("VMDFINDSMALLRINGSDEBUG") != NULL) {
02080     int i;
02081 
02082     msgInfo << "  BACK EDGES: " << n_back_edges << sendmsg;
02083     for (i=0; i < n_back_edges; i++) {
02084       msgInfo << "       SRC:" << back_edge_src[i] << ", DST:" << back_edge_dest[i] << sendmsg;
02085     }
02086 
02087     msgInfo << " SMALL RINGS: " << n_rings << sendmsg;
02088     for (i=0; i < n_rings; i++) {
02089       msgInfo << "    RING: " << *(smallringList[i]) << sendmsg;
02090     }
02091   }
02092 #endif
02093 
02094   return n_rings; // number of rings found
02095 }
02096 
02097 
02098 #define INTREE_NOT      -1   // not in tree
02099 #define INTREE_NOPARENT -2   // no parent 
02100 
02101 // find the back edges of an arbitrary spanning tree (or set of trees if the molecule is disconnected)
02102 int BaseMolecule::find_back_edges(ResizeArray<int> &back_edge_src, ResizeArray<int> &back_edge_dest) {
02103   int i;
02104   int n_back_edges = 0;  
02105   int *intree_parents = new int[nAtoms];
02106   memset(intree_parents, INTREE_NOT, nAtoms * sizeof(int));  // clear parents, -1 = not in tree, -2 = no parent
02107       
02108   for (i=0; i<nAtoms; i++) {
02109     if (intree_parents[i] == INTREE_NOT) {  // not been visited
02110       n_back_edges += find_connected_subgraph_back_edges(i,back_edge_src,back_edge_dest,intree_parents);
02111     }
02112   }
02113 
02114   delete [] intree_parents;
02115  
02116   return n_back_edges;
02117 }
02118 
02119 
02120 // find the back edges of a spanning tree (for a connected portion of the molecule)
02121 int BaseMolecule::find_connected_subgraph_back_edges(int atomid, ResizeArray<int> &back_edge_src, ResizeArray<int> &back_edge_dest,
02122                                                      int *intree_parents) {
02123   int i, n_new_back_edges, cur_atom_id, child_atom_id, parent_atom_id;
02124   MolAtom *curatom;
02125   ResizeArray<int> node_stack;
02126   
02127   node_stack.append(atomid);
02128   intree_parents[atomid] = INTREE_NOPARENT; // in tree, but doesn't have parent
02129   n_new_back_edges = 0;
02130 
02131   while (node_stack.num() > 0) {
02132 #if 1
02133     cur_atom_id = node_stack.pop();
02134 #else
02135     cur_atom_id = node_stack[node_stack.num()-1];
02136     node_stack.remove(node_stack.num()-1);
02137 #endif
02138     
02139     curatom = atom(cur_atom_id);
02140     parent_atom_id = intree_parents[cur_atom_id];
02141     
02142     for(i=0;i<curatom->bonds;i++) {
02143       child_atom_id = curatom->bondTo[i];
02144       if (intree_parents[child_atom_id] != INTREE_NOT) {
02145         // back-edge found
02146         if ((child_atom_id != parent_atom_id) && (child_atom_id > cur_atom_id)) {
02147             // we ignore edges back to the parent
02148             // and only add each back edge once
02149             // (it'll crop up twice since each bond is listed on both atoms)
02150             back_edge_src.append(cur_atom_id);
02151             back_edge_dest.append(child_atom_id);
02152             n_new_back_edges++;
02153         }
02154       } else {
02155         // extended tree
02156         intree_parents[child_atom_id] = cur_atom_id;
02157         node_stack.append(child_atom_id);
02158       }
02159     }
02160   }
02161 
02162   return n_new_back_edges;
02163 }
02164 
02165 // find rings smaller than maxringsize given list of back edges
02166 int BaseMolecule::find_small_rings_from_back_edges(int maxringsize, ResizeArray<int> &back_edge_src, ResizeArray<int> &back_edge_dest) {
02167     int i, key, max_rings;
02168     int n_rings = 0;
02169     int n_back_edges = back_edge_src.num();
02170     SmallRing *ring;
02171     inthash_t *used_edges = new inthash_t; // back edges which have been dealt with 
02172     inthash_t *used_atoms = new inthash_t; // atoms (other than the first) which are used in the current path (i.e. possible loop)
02173     inthash_init(used_edges,n_back_edges);
02174     inthash_init(used_atoms,maxringsize);
02175    
02176     // cap the peak number of rings to find based on the size of the
02177     // input structure.  This should help prevent unusual structures 
02178     // with very high connectivity, such as silicon nanodevices from 
02179     // blowing up the ring search code
02180     max_rings = 2000 + (int) (100.0*sqrt((double) nAtoms));
02181  
02182     for(i=0;i<n_back_edges;i++) {
02183       ring = new SmallRing();
02184       ring->append(back_edge_src[i]);
02185       ring->append(back_edge_dest[i]);
02186       
02187       // first atom is not marked used, since we're allowed to re-use it.
02188       inthash_insert(used_atoms,back_edge_dest[i],1);
02189       
02190       n_rings += find_small_rings_from_partial(ring,maxringsize,used_edges,used_atoms);
02191       delete ring;
02192       
02193       // abort if there are too many rings
02194       if (n_rings > max_rings) {
02195         msgWarn << "Maximum number of rings (" << max_rings << ") exceed."
02196                 << " Stopped looking for rings after " << n_rings << " rings found." << sendmsg;
02197         break;
02198       }
02199       
02200       key = get_edge_key(back_edge_src[i],back_edge_dest[i]);
02201       inthash_insert(used_edges,key,1);
02202       
02203       // remove last atom from used_atoms
02204       inthash_delete(used_atoms,back_edge_dest[i]);
02205     }
02206     
02207     inthash_destroy(used_edges);
02208     delete used_edges;
02209     inthash_destroy(used_atoms);
02210     delete used_atoms;
02211 
02212     return n_rings;
02213 }
02214 
02215 
02216 // find rings smaller than maxringsize from the given partial ring (don't reuse used_edges)
02217 int BaseMolecule::find_small_rings_from_partial(SmallRing *ring, int maxringsize, inthash_t *used_edges, inthash_t *used_atoms) {
02218     int i, next_bond_pos, cur_atom_id, child_atom_id, bond_key, barred, closes, do_pop;
02219     int n_rings = 0;
02220     MolAtom *curatom;
02221 
02222     IntStackHandle atom_id_stack = intstack_create(maxringsize);
02223     IntStackHandle bond_pos_stack = intstack_create(maxringsize);
02224     intstack_push(atom_id_stack,ring->last_atom());
02225     intstack_push(bond_pos_stack,0);
02226 
02227     while (!intstack_pop(atom_id_stack,&cur_atom_id)) {
02228       intstack_pop(bond_pos_stack,&next_bond_pos);
02229       curatom = atom(cur_atom_id);
02230       do_pop = 0; // whether we need to pop the current atom
02231 
02232       // if the ring is already maxringsize, just pop the current atom off.
02233       if (ring->num() > maxringsize)
02234          do_pop = 1;
02235 
02236       if (next_bond_pos == 0 && !do_pop) {
02237         // ignore barred rings (by checking for links back to earlier parts of the ring
02238         // before exploring further)
02239         barred = closes = 0;
02240         for(i=0;i<curatom->bonds;i++) {
02241           child_atom_id = curatom->bondTo[i];
02242 
02243           // check that this is not an edge immediately back to the previous atom
02244           if (child_atom_id == ring->atoms[ring->atoms.num()-2]) continue;
02245 
02246           // check that this is not an atom we've included
02247           // (an exception is the first atom, which we're allowed to try add, obviously :)
02248           if (inthash_lookup(used_atoms, child_atom_id) != HASH_FAIL) {
02249             barred = 1;
02250             continue;
02251           }
02252 
02253           // check is this not a back edge which has already been used
02254           bond_key = get_edge_key(cur_atom_id,child_atom_id);
02255           if (inthash_lookup(used_edges, bond_key) != HASH_FAIL) {
02256             if (child_atom_id == ring->first_atom())
02257               barred = 1; // used back-edge which closes the ring counts as barred
02258             continue;
02259           }
02260 
02261           // check whether ring closes
02262           if (child_atom_id == ring->first_atom()) {
02263             closes = 1;
02264             continue;
02265           }
02266         }
02267 
02268         if (closes && !barred) {
02269           smallringList.append(ring->copy());
02270           n_rings += 1;
02271         }
02272         if (closes || barred) {
02273           // adding this atom would create a barred ring, so skip to next atom on stack
02274           do_pop = 1;
02275         }
02276       }
02277 
02278       if (!do_pop) {
02279         for(i=next_bond_pos;i<curatom->bonds;i++) {
02280           child_atom_id = curatom->bondTo[i];
02281 
02282           // check that this is not an edge immediately back to the previous atom
02283           if (child_atom_id == ring->atoms[ring->atoms.num()-2]) continue;
02284                   
02285           // check is this not a back edge which has already been used
02286           bond_key = get_edge_key(cur_atom_id,child_atom_id);
02287           if (inthash_lookup(used_edges, bond_key) != HASH_FAIL) continue;
02288           
02289           // Append child atom and go deeper
02290           ring->append(child_atom_id);
02291           inthash_insert(used_atoms,child_atom_id,1);
02292           
02293           // push current state and new state onto stack and recurse
02294           intstack_push(atom_id_stack,cur_atom_id);
02295           intstack_push(bond_pos_stack,i+1);
02296           intstack_push(atom_id_stack,child_atom_id);
02297           intstack_push(bond_pos_stack,0);
02298           break;     
02299         }
02300 
02301         // we finished the children, pop one parent
02302         if(i>=curatom->bonds)
02303             do_pop = 1;        
02304       }
02305 
02306       if (do_pop && !intstack_empty(atom_id_stack)) {
02307           // finished with cur_atom_id and all its children      
02308           // clean up before returning from recurse
02309           ring->remove_last();
02310           inthash_delete(used_atoms,cur_atom_id);      
02311       }
02312     }
02313     
02314     intstack_destroy(atom_id_stack);
02315     intstack_destroy(bond_pos_stack);
02316     return n_rings;
02317 }
02318 
02319 
02320 // construct edge key
02321 int BaseMolecule::get_edge_key(int edge_src, int edge_dest) {
02322     int t;
02323     if (edge_dest > edge_src) {
02324        t = edge_src;
02325        edge_src = edge_dest;
02326        edge_dest = t;
02327     }
02328     return edge_src * nAtoms + edge_dest;
02329 }
02330 
02331 // Routines for orientating rings
02332 // contributed by Simon Cross and Michelle Kuttel
02333 
02334 void BaseMolecule::orientate_small_rings(int maxringsize) {
02335   for (int i=0;i<smallringList.num();i++)
02336     orientate_small_ring(*smallringList[i],maxringsize);
02337 }
02338 
02339 void BaseMolecule::orientate_small_ring(SmallRing &ring,int maxringsize) {
02340   short int oxygen = -1;
02341   int oxygen_typeindex, c1_nameindex, c1p_nameindex, cu1_nameindex;
02342   int c2_nameindex, c2p_nameindex, cu2_nameindex;
02343   MolAtom *curatom, *atom_before_O, *atom_after_O;
02344 
02345   // lookup atom name indices in advance
02346   oxygen_typeindex = atomTypes.typecode("O");
02347   c1_nameindex = atomNames.typecode("C1");
02348   c1p_nameindex = atomNames.typecode("C1'");
02349   cu1_nameindex = atomNames.typecode("C_1");
02350   c2_nameindex = atomNames.typecode("C2");
02351   c2p_nameindex = atomNames.typecode("C2'");
02352   cu2_nameindex = atomNames.typecode("C_2");
02353     
02354   // Find an oxygen (or something with two bonds)
02355   for (int i=0; i<ring.num(); i++) {
02356     curatom = atom(ring[i]);
02357     if (curatom->atomicnumber == 8 || curatom->typeindex == oxygen_typeindex || curatom->bonds == 2) {
02358       oxygen = i;
02359       break;
02360     }
02361   }
02362 
02363   // leave ring unorientated if no oxygen found
02364   if (oxygen == -1) return;
02365 
02366   // find atoms before and after oxygen (taking into account wrapping)
02367   if (oxygen == 0) atom_before_O = atom(ring.last_atom());
02368   else atom_before_O = atom(ring[oxygen-1]);
02369 
02370   if (oxygen == ring.num()-1) atom_after_O = atom(ring.first_atom());
02371   else atom_after_O = atom(ring[oxygen+1]);
02372     
02373   // ensure C1 carbon is after the oxygen
02374   // leave unorientated if the C1 carbon can't be found
02375   if (atom_before_O->nameindex == c1_nameindex || atom_before_O->nameindex == c1p_nameindex
02376       || atom_before_O->nameindex == cu1_nameindex
02377       || atom_before_O->nameindex == c2_nameindex || atom_before_O->nameindex == c2p_nameindex
02378       || atom_before_O->nameindex == cu2_nameindex) {
02379     ring.reverse();
02380     ring.orientated = 1;
02381   }
02382   else if (atom_after_O->nameindex == c1_nameindex || atom_after_O->nameindex == c1p_nameindex
02383            || atom_after_O->nameindex == cu1_nameindex
02384            || atom_after_O->nameindex == c2_nameindex || atom_after_O->nameindex == c2p_nameindex
02385            || atom_after_O->nameindex == cu2_nameindex) {
02386     ring.orientated = 1;
02387   }
02388 }
02389 
02390 
02391 // Routines for finding links between rings
02392 // contributed by Simon Cross and Michelle Kuttel
02393 
02394 // find all paths shorter than a given length between the small rings
02395 // listed in smallringList
02396 int BaseMolecule::find_orientated_small_ring_linkages(int maxpathlength, int maxringsize) {
02397   SmallRing *sr;
02398   LinkagePath *lp;
02399   int i, j, atom_id;
02400   int n_paths = 0;
02401   inthash_t *atom_to_ring = new inthash_t;
02402   inthash_t *multi_ring_atoms = new inthash_t;
02403   inthash_t *used_atoms = new inthash_t;
02404   inthash_init(atom_to_ring,smallringList.num()*maxringsize/2);
02405   inthash_init(multi_ring_atoms,smallringList.num());
02406   inthash_init(used_atoms,maxpathlength);
02407    
02408   // create lookup from atom id to ring id
02409   for (i=0;i<smallringList.num();i++) {
02410     sr = smallringList[i];
02411     // XXX: Uncomment this if we want to include non-orientated rings
02412     //      in linkage paths
02413     // if (!sr->orientated) continue;
02414     for (j=0;j<sr->num();j++) {
02415       atom_id = (*sr)[j];
02416       if (inthash_lookup(atom_to_ring,atom_id) == HASH_FAIL)
02417         inthash_insert(atom_to_ring,atom_id,i);
02418       else
02419         inthash_insert(multi_ring_atoms,atom_id,1);
02420     }
02421   }
02422 
02423   // look for ring linkage paths starting from each atom
02424   // of each orientated ring
02425   for (i=0;i<smallringList.num();i++) {
02426     sr = smallringList[i];
02427     if (!sr->orientated) continue;
02428     for (j=0;j<sr->num();j++) {
02429       if (inthash_lookup(multi_ring_atoms,(*sr)[j]) != HASH_FAIL) continue;
02430       lp = new LinkagePath();
02431       lp->start_ring = i;
02432       lp->path.append((*sr)[j]);    
02433       n_paths += find_linkages_for_ring_from_partial(*lp,maxpathlength,atom_to_ring,multi_ring_atoms,used_atoms);
02434       delete lp;
02435     }
02436   }
02437   
02438   inthash_destroy(atom_to_ring);
02439   delete atom_to_ring;
02440   inthash_destroy(multi_ring_atoms);
02441   delete multi_ring_atoms;
02442   inthash_destroy(used_atoms);
02443   delete used_atoms;
02444 
02445 #if 0
02446   msgInfo << smallringLinkages << sendmsg;
02447 #endif
02448   
02449   return n_paths; // number of paths found.
02450 }
02451 
02452 int BaseMolecule::find_linkages_for_ring_from_partial(LinkagePath &lp, int maxpathlength, inthash_t *atom_to_ring, inthash_t *multi_ring_atoms, inthash_t *used_atoms) {
02453   int i, cur_atom_id, next_bond_pos, child_atom_id, ringidx;
02454   int n_paths = 0;
02455   MolAtom *curatom;
02456 
02457   IntStackHandle atom_id_stack = intstack_create(maxpathlength);
02458   IntStackHandle bond_pos_stack = intstack_create(maxpathlength);
02459   intstack_push(atom_id_stack,lp.path.last_atom());
02460   intstack_push(bond_pos_stack,0);
02461 
02462   while (!intstack_pop(atom_id_stack,&cur_atom_id)) {
02463     intstack_pop(bond_pos_stack,&next_bond_pos);
02464     curatom = atom(cur_atom_id);
02465 
02466     if (next_bond_pos == 0)
02467       inthash_insert(used_atoms, cur_atom_id, 1);
02468 
02469     for(i=next_bond_pos;i<curatom->bonds;i++) {
02470       child_atom_id = curatom->bondTo[i];
02471 
02472       // check that this isn't an atom that belongs to multiple rings
02473       if (inthash_lookup(multi_ring_atoms,child_atom_id) != HASH_FAIL) continue;
02474 
02475       // check that this is not an edge immediately back to the previous atom
02476       // (when there is only one atom in the path, it can't be a link back)
02477       if (lp.num() > 1 && child_atom_id == lp[lp.num()-2]) continue;
02478       
02479       // check that we haven't arrived at a non-orientated ring
02480       ringidx = inthash_lookup(atom_to_ring, child_atom_id);
02481       if (ringidx != HASH_FAIL && !smallringList[ringidx]->orientated) continue;
02482 
02483       // only store paths from smaller ringidx to larger ringidx (to avoid getting a copy of each orientation of the path)
02484       // ignore paths which return to the same ring
02485       // check that we're leaving the starting ring
02486       if (ringidx != HASH_FAIL && ringidx <= lp.start_ring) continue;
02487       
02488       // see if this takes us to another ring
02489       if (ringidx != HASH_FAIL && (ringidx > lp.start_ring)) {
02490           lp.append(child_atom_id);
02491           lp.end_ring = ringidx;
02492           smallringLinkages.addLinkagePath(*lp.copy());
02493           n_paths++;
02494           lp.end_ring = -1;
02495           lp.remove_last();
02496           continue;
02497       }
02498       
02499       // check that this is not an atom we've included
02500       // (an exception is the first atom, which we're allowed to try add, obviously :)
02501       if (inthash_lookup(used_atoms, child_atom_id) != HASH_FAIL) continue;
02502               
02503       if (lp.num() < maxpathlength) {
02504          lp.append(child_atom_id);
02505 
02506          // push current state and new state onto stack and recurse
02507          intstack_push(atom_id_stack,cur_atom_id);
02508          intstack_push(bond_pos_stack,i+1);
02509          intstack_push(atom_id_stack,child_atom_id);
02510          intstack_push(bond_pos_stack,0);
02511          break;
02512       }
02513     }
02514 
02515     if ((i>=curatom->bonds)&&(!intstack_empty(atom_id_stack))) {      
02516       // clean up before returning from recurse
02517       lp.remove_last();
02518       inthash_delete(used_atoms,cur_atom_id);
02519     }
02520   }
02521   
02522   intstack_destroy(atom_id_stack);
02523   intstack_destroy(bond_pos_stack);
02524   return n_paths;
02525 }
02526 
02527 #endif  // end of carbohydrate related stuff
02528 
02529 
02530 
02531 void BaseMolecule::add_volume_data(const char *name, const float *o,
02532     const float *xa, const float *ya, const float *za, int x, int y, int z,
02533     float *scalar, float *grad, float *variance) {
02534   msgInfo << "Analyzing Volume..." << sendmsg;
02535 
02536   VolumetricData *vdata = new VolumetricData(name, o, xa, ya, za,
02537                                              x, y, z, scalar);
02538   
02539   // Print out grid size along with memory use for the grid itself plus
02540   // the memory required for the volume gradients (4x the scalar grid memory)
02541   // Color texture maps require another 0.75x the original scalar grid size.
02542   msgInfo << "   Grid size: " << x << "x" << y << "x" << z << "  (" 
02543           << (int) (1.0 * (vdata->gridsize() * sizeof(float)) / (1024.0 * 1024.0)) << " MB)" 
02544           << sendmsg;
02545 
02546   msgInfo << "   Total voxels: " << vdata->gridsize() << sendmsg;
02547 
02548   float datamin, datamax;
02549   vdata->datarange(datamin, datamax);
02550 #if 1
02551   char minstr[1024];
02552   char maxstr[1024];
02553   char rangestr[1024];
02554   sprintf(minstr, "%g", datamin);
02555   sprintf(maxstr, "%g", datamax);
02556   sprintf(rangestr, "%g", (datamax - datamin));
02557   msgInfo << "   Min: " << minstr << "  Max: " << maxstr 
02558           << "  Range: " << rangestr << sendmsg;
02559 #else
02560   msgInfo << "   Min: " << datamin << "  Max: " << datamax 
02561           << "  Range: " << (datamax - datamin) << sendmsg;
02562 #endif
02563 
02564   int gmapmb = int((3.0 * (vdata->gridsize() * sizeof(float)) / (1024.0 * 1024.0)));
02565   if (grad) {
02566     // set gradients for smooth normals
02567     msgInfo << "   Assigning volume gradient map" 
02568             << "  (" << gmapmb << " MB)" << sendmsg;
02569     vdata->set_volume_gradient(grad);
02570   } else {
02571     if (gmapmb <= 3072) {
02572       // calc gradients for smooth normals
02573       msgInfo << "   Computing volume gradient map " 
02574               << "  (" << gmapmb << " MB)" << sendmsg;
02575       vdata->compute_volume_gradient();
02576     } else {
02577       msgInfo << "   Volume gradient map calculations deferred to on-demand" 
02578               << sendmsg;
02579     }
02580   }
02581 
02582   volumeList.append(vdata);
02583 
02584   msgInfo << "Added volume data, name=" << vdata->name << sendmsg;
02585 }
02586 
02587 
02588 void BaseMolecule::add_volume_data(const char *name, const double *o,
02589     const double *xa, const double *ya, const double *za, int x, int y, int z,
02590     float *scalar) {
02591   msgInfo << "Analyzing Volume..." << sendmsg;
02592 
02593   VolumetricData *vdata = new VolumetricData(name, o, xa, ya, za,
02594                                              x, y, z, scalar);
02595   
02596   // Print out grid size along with memory use for the grid itself plus
02597   // the memory required for the volume gradients (4x the scalar grid memory)
02598   // Color texture maps require another 0.75x the original scalar grid size.
02599   msgInfo << "   Grid size: " << x << "x" << y << "x" << z << "  (" 
02600           << (int) (4.0 * (vdata->gridsize() * sizeof(float)) / (1024.0 * 1024.0)) << " MB)" 
02601           << sendmsg;
02602 
02603   msgInfo << "   Total voxels: " << vdata->gridsize() << sendmsg;
02604 
02605   float datamin, datamax;
02606   vdata->datarange(datamin, datamax);
02607   msgInfo << "   Min: " << datamin << "  Max: " << datamax 
02608           << "  Range: " << (datamax - datamin) << sendmsg;
02609 
02610   msgInfo << "   Computing volume gradient map for smooth shading" << sendmsg;
02611   vdata->compute_volume_gradient(); // calc gradients for smooth vertex normals
02612 
02613   volumeList.append(vdata);
02614 
02615   msgInfo << "Added volume data, name=" << vdata->name << sendmsg;
02616 }
02617 
02618 
02619 void BaseMolecule::remove_volume_data(int idx) { 
02620   if (idx >= 0 && idx < volumeList.num()) {
02621     delete volumeList[idx];    
02622     volumeList.remove(idx);
02623   }
02624 }
02625 
02626 
02627 int BaseMolecule::num_volume_data() {
02628   return volumeList.num();
02629 }
02630 
02631 VolumetricData *BaseMolecule::modify_volume_data(int id) {
02632   if (id >= 0 && id < volumeList.num())
02633     return volumeList[id];
02634   return NULL;
02635 }
02636 
02637 const VolumetricData *BaseMolecule::get_volume_data(int id) {
02638   return modify_volume_data(id);
02639 }
02640 

Generated on Thu Apr 25 02:41:59 2024 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002