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

Measure.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr
00003  *cr            (C) Copyright 1995-2011 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: Measure.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.136 $       $Date: 2013/02/11 17:31:09 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *   Code to measure atom distances, angles, dihedrals, etc.
00019  ***************************************************************************/
00020 
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <math.h>
00024 #include "Measure.h"
00025 #include "AtomSel.h"
00026 #include "Matrix4.h"
00027 #include "utilities.h"
00028 #include "fitrms.h"
00029 #include "ResizeArray.h"
00030 #include "SpatialSearch.h"  // for find_within()
00031 #include "MoleculeList.h"
00032 #include "Inform.h"
00033 #include "Timestep.h"
00034 #include "VMDApp.h"
00035 #include "WKFThreads.h"
00036 #include "WKFUtils.h"
00037 
00038 // Standard functions available to everyone
00039 static const char *measure_error_messages[] = {
00040   "no atom selection",                                  // -1
00041   "no atoms in selection",                              // -2
00042   "incorrect number of weights for selection",          // -3
00043   "internal error: NULL pointer given",                 // -4
00044   "bad weight sum, would cause divide by zero",         // -5
00045   "molecule was deleted(?)",                            // -6
00046   "cannot understand weight parameter",                 // -7
00047   "non-number given as parameter",                      // -8
00048   "two selections don't have the same number of atoms", // -9
00049   "internal error: out of range",                       // -10
00050   "no coordinates in selection",                        // -11
00051   "couldn't compute eigenvalue/vectors",                // -12
00052   "unknown Tcl problem",                                // -13
00053   "no atom radii",                                      // -14
00054   "order parameter contains out-of-range atom index",   // -15
00055   "order parameter not supported in old RMS fit mode",  // -16
00056   "specified frames are out of range",                  // -17
00057   "both selections must reference the same molecule",   // -18
00058   "one atom appears twice in list",                     // -19
00059   "molecule contains no frames",                        // -20
00060   "invalid atom id",                                    // -21
00061   "cutoff must be smaller than cell dimension",         // -22
00062   "Zero volmap gridsize"                                // -23
00063 };
00064   
00065 const char *measure_error(int errnum) {
00066   if (errnum >= 0 || errnum < -23) 
00067     return "bad error number";
00068   return measure_error_messages[-errnum - 1];
00069 }
00070 
00071 int measure_move(const AtomSel *sel, float *framepos, const Matrix4 &mat) {
00072   if (!sel)       return MEASURE_ERR_NOSEL;
00073   if (!framepos)  return MEASURE_ERR_NOFRAMEPOS;
00074 
00075   // and apply it to the coordinates
00076   int i;
00077   float *pos = framepos;
00078   if (mat.mat[3]==0 && mat.mat[7]==0 && mat.mat[11]==0 && mat.mat[15]==1) {
00079     // then this is just a rotation followed by a translation.  Do it
00080     // the fast way.
00081     const float dx = mat.mat[12];
00082     const float dy = mat.mat[13];
00083     const float dz = mat.mat[14];
00084     const float *m = mat.mat;
00085     if (sel->selected == sel->num_atoms) {
00086       for (i=0; i<sel->num_atoms; i++) {
00087         float x = pos[0]*m[0] + pos[1]*m[4] + pos[2]*m[8] + dx;
00088         float y = pos[0]*m[1] + pos[1]*m[5] + pos[2]*m[9] + dy;
00089         float z = pos[0]*m[2] + pos[1]*m[6] + pos[2]*m[10] + dz;
00090         pos[0] = x;
00091         pos[1] = y;
00092         pos[2] = z;
00093         pos += 3;
00094       }
00095     } else {
00096       pos += sel->firstsel*3;
00097       for (i=sel->firstsel; i<=sel->lastsel; i++) {
00098         if (sel->on[i]) {
00099           float x = pos[0]*m[0] + pos[1]*m[4] + pos[2]*m[8] + dx;
00100           float y = pos[0]*m[1] + pos[1]*m[5] + pos[2]*m[9] + dy;
00101           float z = pos[0]*m[2] + pos[1]*m[6] + pos[2]*m[10] + dz;
00102           pos[0] = x;
00103           pos[1] = y;
00104           pos[2] = z;
00105         }
00106         pos += 3;
00107       }
00108     }
00109   } else {
00110     pos += sel->firstsel*3;
00111     for (i=sel->firstsel; i<=sel->lastsel; i++) {
00112       if (sel->on[i]) {
00113         mat.multpoint3d(pos, pos);
00114       }
00115       pos += 3;
00116     }
00117   }
00118   return MEASURE_NOERR;
00119 }
00120 
00121 // compute the sum of a set of weights which correspond either to
00122 // all atoms, or to selected atoms
00123 int measure_sumweights(const AtomSel *sel, int numweights, 
00124                        const float *weights, float *weightsum) {
00125   if (!sel)       return MEASURE_ERR_NOSEL;
00126   if (!weights)   return MEASURE_ERR_NOWEIGHT;
00127   if (!weightsum) return MEASURE_ERR_GENERAL;
00128 
00129   double sum = 0;
00130 
00131   if (numweights == sel->num_atoms) {
00132     int i;
00133     for (i=sel->firstsel; i<=sel->lastsel; i++) {
00134       if (sel->on[i]) {
00135         sum += weights[i];
00136       }
00137     }
00138   } else if (numweights == sel->selected) {
00139     int i, j;
00140     for (j=0,i=sel->firstsel; i<=sel->lastsel; i++) {
00141       if (sel->on[i]) {
00142         sum += weights[j];
00143         j++;
00144       }
00145     }
00146   } else {
00147     return MEASURE_ERR_BADWEIGHTPARM;  
00148   }
00149 
00150   *weightsum = (float) sum;
00151   return MEASURE_NOERR;
00152 }
00153 
00154 
00155 // compute the center of mass
00156 // return -5 if total weight == 0, otherwise 0 for success.
00157 int measure_center(const AtomSel *sel, const float *framepos,
00158                    const float *weight, float *com) {
00159   if (!sel)      return MEASURE_ERR_NOSEL;
00160   if (!framepos) return MEASURE_ERR_NOFRAMEPOS;
00161   if (!weight)   return MEASURE_ERR_NOWEIGHT;
00162   if (!com)      return MEASURE_ERR_NOCOM;   
00163 
00164   int i, j;
00165   float x, y, z, w;
00166   j=0;
00167   w=x=y=z=0;
00168   for (i=sel->firstsel; i<=sel->lastsel; i++) {
00169     if (sel->on[i]) {
00170       float tw = weight[j];
00171       w += tw;
00172       x += tw * framepos[3*i  ];
00173       y += tw * framepos[3*i+1];
00174       z += tw * framepos[3*i+2];
00175       j++;
00176     }
00177   }
00178 
00179   if (w == 0) {
00180     return MEASURE_ERR_BADWEIGHTSUM;
00181   }
00182 
00183   com[0] = x / w;
00184   com[1] = y / w;
00185   com[2] = z / w;
00186 
00187   return MEASURE_NOERR;
00188 }
00189 
00190 
00191 // compute the axis aligned aligned bounding box for the selected atoms
00192 int measure_minmax(int num, const int *on, const float *framepos, 
00193                    const float *radii, float *min_coord, float *max_coord) {
00194   int i;
00195   float minx, miny, minz;
00196   float maxx, maxy, maxz;
00197 
00198   if (!on)                      return MEASURE_ERR_NOSEL;
00199   if (num == 0)                 return MEASURE_ERR_NOATOMS;
00200   if (!min_coord || !max_coord) return MEASURE_ERR_NOMINMAXCOORDS;
00201 
00202   vec_zero(min_coord);
00203   vec_zero(max_coord);
00204 
00205   // find first selected atom
00206   int firstsel = 0;
00207   int lastsel = -1;
00208   if (find_first_selection_aligned(num, on, &firstsel))
00209     return MEASURE_NOERR; // no atoms selected
00210 
00211   if (find_last_selection_aligned(num, on, &lastsel))
00212     return MEASURE_NOERR; // no atoms selected or internal inconsistency
00213 
00214   // initialize minmax coords to the first selected atom
00215   i=firstsel;
00216   if (radii == NULL) {
00217     // calculate bounding box of atom centers
00218     minx = maxx = framepos[i*3  ];
00219     miny = maxy = framepos[i*3+1];
00220     minz = maxz = framepos[i*3+2];
00221   } else {
00222     // calculate bounding box for atoms /w given radii 
00223     minx = framepos[i*3  ] - radii[i];
00224     maxx = framepos[i*3  ] + radii[i];
00225     miny = framepos[i*3+1] - radii[i];
00226     maxy = framepos[i*3+1] + radii[i];
00227     minz = framepos[i*3+2] - radii[i];
00228     maxz = framepos[i*3+2] + radii[i];
00229   }
00230 
00231   // continue looping from there until we finish
00232   if (radii == NULL) {
00233     // calculate bounding box of atom centers
00234 #if 0
00235     minmax_selected_3fv_aligned(framepos, on, atomSel->num_atoms,
00236                                 firstsel, lastsel, fmin, fmax);
00237 #else
00238     for (i++; i<=lastsel; i++) {
00239       if (on[i]) {
00240         int ind = i * 3;
00241         float tmpx = framepos[ind  ];
00242         if (tmpx < minx) minx = tmpx; 
00243         if (tmpx > maxx) maxx = tmpx;
00244   
00245         float tmpy = framepos[ind+1];
00246         if (tmpy < miny) miny = tmpy; 
00247         if (tmpy > maxy) maxy = tmpy;
00248   
00249         float tmpz = framepos[ind+2];
00250         if (tmpz < minz) minz = tmpz; 
00251         if (tmpz > maxz) maxz = tmpz;
00252       }
00253     }
00254 #endif
00255   } else {
00256     // calculate bounding box for atoms /w given radii 
00257     for (i++; i<=lastsel; i++) {
00258       if (on[i]) {
00259         int ind = i * 3;
00260         float mintmpx = framepos[ind  ] - radii[i];
00261         float maxtmpx = framepos[ind  ] + radii[i];
00262         if (mintmpx < minx) minx = mintmpx;
00263         if (maxtmpx > maxx) maxx = maxtmpx;
00264   
00265         float mintmpy = framepos[ind+1] - radii[i];
00266         float maxtmpy = framepos[ind+1] + radii[i];
00267         if (mintmpy < miny) miny = mintmpy; 
00268         if (maxtmpy > maxy) maxy = maxtmpy;
00269   
00270         float mintmpz = framepos[ind+2] - radii[i];
00271         float maxtmpz = framepos[ind+2] + radii[i];
00272         if (mintmpz < minz) minz = mintmpz; 
00273         if (maxtmpz > maxz) maxz = maxtmpz;
00274       }
00275     }
00276   }
00277 
00278   // set the final min/max output values
00279   min_coord[0]=minx;
00280   min_coord[1]=miny;
00281   min_coord[2]=minz;
00282   max_coord[0]=maxx;
00283   max_coord[1]=maxy;
00284   max_coord[2]=maxz;
00285 
00286   return MEASURE_NOERR;
00287 }
00288 
00289 
00290 // Calculate average position of selected atoms over selected frames
00291 extern int measure_avpos(const AtomSel *sel, MoleculeList *mlist, 
00292                          int start, int end, int step, float *avpos) {
00293   if (!sel)                     return MEASURE_ERR_NOSEL;
00294   if (sel->num_atoms == 0)      return MEASURE_ERR_NOATOMS;
00295 
00296   Molecule *mymol = mlist->mol_from_id(sel->molid());
00297   int maxframes = mymol->numframes();
00298   
00299   // accept value of -1 meaning "all" frames
00300   if (end == -1)
00301     end = maxframes-1;
00302 
00303   if (maxframes == 0 || start < 0 || start > end || 
00304       end >= maxframes || step <= 0)
00305     return MEASURE_ERR_BADFRAMERANGE;
00306 
00307   int i;
00308   for (i=0; i<(3*sel->selected); i++)
00309     avpos[i] = 0.0f;
00310 
00311   int frame, avcount, j;
00312   for (avcount=0,frame=start; frame<=end; avcount++,frame+=step) {
00313     const float *framepos = (mymol->get_frame(frame))->pos;
00314     for (j=0,i=sel->firstsel; i<=sel->lastsel; i++) {
00315       if (sel->on[i]) {
00316         avpos[j*3    ] += framepos[i*3    ];
00317         avpos[j*3 + 1] += framepos[i*3 + 1];
00318         avpos[j*3 + 2] += framepos[i*3 + 2];
00319         j++;
00320       }
00321     } 
00322   }
00323 
00324   float avinv = 1.0f / (float) avcount;
00325   for (j=0; j<(3*sel->selected); j++) {
00326     avpos[j] *= avinv;
00327   } 
00328 
00329   return MEASURE_NOERR;
00330 }
00331 
00332 
00333 // Calculate dipole moment for selected atoms
00334 extern int measure_dipole(const AtomSel *sel, MoleculeList *mlist, 
00335                           float *dipole, int unitsdebye, int usecenter) {
00336   if (!sel)                     return MEASURE_ERR_NOSEL;
00337   if (sel->num_atoms == 0)      return MEASURE_ERR_NOATOMS;
00338 
00339   Molecule *mymol = mlist->mol_from_id(sel->molid());
00340   double  rvec[3] = {0, 0, 0};
00341   double qrvec[3] = {0, 0, 0};
00342   double mrvec[3] = {0, 0, 0};
00343   double totalq = 0.0;
00344   double totalm = 0.0;
00345   int i;
00346 
00347   // get atom coordinates
00348   const float *framepos = sel->coordinates(mlist);
00349 
00350   // get atom charges
00351   const float *q = mymol->charge();
00352   const float *m = mymol->mass();
00353 
00354   for (i=sel->firstsel; i<=sel->lastsel; i++) {
00355     if (sel->on[i]) {
00356       int ind = i * 3;
00357       rvec[0] += framepos[ind    ];
00358       rvec[1] += framepos[ind + 1];
00359       rvec[2] += framepos[ind + 2];
00360 
00361       qrvec[0] += framepos[ind    ] * q[i];
00362       qrvec[1] += framepos[ind + 1] * q[i];
00363       qrvec[2] += framepos[ind + 2] * q[i];
00364 
00365       mrvec[0] += framepos[ind    ] * m[i];
00366       mrvec[1] += framepos[ind + 1] * m[i];
00367       mrvec[2] += framepos[ind + 2] * m[i];
00368 
00369       totalq += q[i];
00370       totalm += m[i];
00371     }
00372   }
00373 
00374   // fall back to geometrical center when bad or no masses
00375   if (totalm < 0.0001)
00376     usecenter=1;
00377   
00378   switch (usecenter) {
00379     case 1:
00380     {
00381         double rscale = totalq / sel->selected; 
00382         dipole[0] = (float) (qrvec[0] - (rvec[0] * rscale)); 
00383         dipole[1] = (float) (qrvec[1] - (rvec[1] * rscale)); 
00384         dipole[2] = (float) (qrvec[2] - (rvec[2] * rscale)); 
00385         break;
00386     }
00387 
00388     case -1: 
00389     {
00390         double mscale = totalq / totalm; 
00391         dipole[0] = (float) (qrvec[0] - (mrvec[0] * mscale)); 
00392         dipole[1] = (float) (qrvec[1] - (mrvec[1] * mscale)); 
00393         dipole[2] = (float) (qrvec[2] - (mrvec[2] * mscale)); 
00394         break;
00395     }
00396     
00397     case 0: // fallthrough
00398     default: 
00399     {
00400         dipole[0] = (float) qrvec[0];
00401         dipole[1] = (float) qrvec[1];
00402         dipole[2] = (float) qrvec[2];
00403         break;
00404     }
00405   }
00406 
00407   // According to the values in
00408   // http://www.physics.nist.gov/cuu/Constants/index.html
00409   // 1 e*A = 4.80320440079 D
00410   // 1 D = 1E-18 Fr*cm = 0.208194346224 e*A
00411   // 1 e*A = 1.60217653 E-29 C*m
00412   // 1 C*m = 6.24150947961 E+28 e*A
00413   // 1 e*A = 1.88972613458 e*a0
00414   // 1 e*a0 = 0.529177208115 e*A
00415 
00416   if (unitsdebye) {
00417     // 1 e*A = 4.80320440079 D 
00418     // latest CODATA (2006) gives:
00419     //         4.80320425132073913031
00420     dipole[0] *= 4.80320425132f;
00421     dipole[1] *= 4.80320425132f;
00422     dipole[2] *= 4.80320425132f;
00423   }
00424  
00425   return MEASURE_NOERR;
00426 }
00427 
00428 
00429 // Calculate RMS fluctuation of selected atoms over selected frames
00430 extern int measure_rmsf(const AtomSel *sel, MoleculeList *mlist, 
00431                         int start, int end, int step, float *rmsf) {
00432   if (!sel)                     return MEASURE_ERR_NOSEL;
00433   if (sel->num_atoms == 0)      return MEASURE_ERR_NOATOMS;
00434 
00435   Molecule *mymol = mlist->mol_from_id(sel->molid());
00436   int maxframes = mymol->numframes();
00437 
00438   // accept value of -1 meaning "all" frames
00439   if (end == -1)
00440     end = maxframes-1;
00441 
00442   if (maxframes == 0 || start < 0 || start > end ||
00443       end >= maxframes || step <= 0)
00444     return MEASURE_ERR_BADFRAMERANGE;
00445 
00446   int i;
00447   for (i=0; i<sel->selected; i++)
00448     rmsf[i] = 0.0f;
00449 
00450   int rc; 
00451   float *avpos = new float[3*sel->selected];
00452   rc = measure_avpos(sel, mlist, start, end, step, avpos);
00453 
00454   if (rc != MEASURE_NOERR) {
00455     delete [] avpos;
00456     return rc;
00457   }
00458 
00459   // calculate per-atom variance here
00460   int frame, avcount, j;
00461   for (avcount=0,frame=start; frame<=end; avcount++,frame+=step) {
00462     const float *framepos = (mymol->get_frame(frame))->pos;
00463     for (j=0,i=sel->firstsel; i<=sel->lastsel; i++) {
00464       if (sel->on[i]) {
00465         rmsf[j] += distance2(&avpos[3*j], &framepos[3*i]);
00466         j++;
00467       }
00468     }
00469   }
00470 
00471   float avinv = 1.0f / (float) avcount;
00472   for (j=0; j<sel->selected; j++) {
00473     rmsf[j] = sqrtf(rmsf[j] * avinv);
00474   }
00475 
00476   delete [] avpos;
00477   return MEASURE_NOERR;
00478 }
00479 
00480 
00482 //  rgyr := sqrt(sum (mass(n) ( r(n) - r(com) )^2)/sum(mass(n)))
00483 //  The return value, a float, is put in 'float *rgyr'
00484 //  The function return value is 0 if ok, <0 if not
00485 int measure_rgyr(const AtomSel *sel, MoleculeList *mlist, const float *weight, 
00486                  float *rgyr) {
00487   if (!rgyr) return MEASURE_ERR_NORGYR;
00488   if (!sel) return MEASURE_ERR_NOSEL;
00489   if (!mlist) return MEASURE_ERR_GENERAL;
00490 
00491   const float *framepos = sel->coordinates(mlist);
00492 
00493   // compute the center of mass with the current weights
00494   float com[3];
00495   int ret_val = measure_center(sel, framepos, weight, com);
00496   if (ret_val < 0) 
00497     return ret_val;
00498   
00499   // measure center of gyration
00500   int i, j;
00501   float total_w, sum;
00502 
00503   total_w=sum=0;
00504   for (j=0,i=sel->firstsel; i<=sel->lastsel; i++) {
00505     if (sel->on[i]) {
00506       float w = weight[j];
00507       total_w += w;
00508       sum += w * distance2(framepos + 3*i, com);
00509       j++;
00510     } 
00511   }
00512 
00513   if (total_w == 0) {
00514     return MEASURE_ERR_BADWEIGHTSUM;
00515   }
00516 
00517   // and finalize the computation
00518   *rgyr = sqrtf(sum/total_w);
00519 
00520   return MEASURE_NOERR;
00521 }
00522 
00523 
00525 //  1) if num == sel.selected ; assumes there is one weight per 
00526 //           selected atom
00527 //  2) if num == sel.num_atoms; assumes weight[i] is for atom[i]
00528 //  returns 0 and value in rmsd if good
00529 //   return < 0 if invalid
00530 //  Function is::=  rmsd = 
00531 //    sqrt(sum(weight(n) * sqr(r1(i(n))-r2(i(n))))/sum(weight(n)) / N
00532 int measure_rmsd(const AtomSel *sel1, const AtomSel *sel2,
00533                  int num, const float *framepos1, const float *framepos2,
00534                  float *weight, float *rmsd) {
00535   if (!sel1 || !sel2)   return MEASURE_ERR_NOSEL;
00536   if (sel1->selected < 1 || sel2->selected < 1) return MEASURE_ERR_NOSEL;
00537   if (!weight || !rmsd) return MEASURE_ERR_NOWEIGHT;
00538 
00539   // the number of selected atoms must be the same
00540   if (sel1->selected != sel2->selected) return MEASURE_ERR_MISMATCHEDCNT;
00541 
00542   // need to know how to traverse the list of weights
00543   // there could be 1 weight per atom (sel_flg == 1) or 
00544   // 1 weight per selected atom (sel_flg == 0)
00545   int sel_flg;
00546   if (num == sel1->num_atoms) {
00547     sel_flg = 1; // using all elements
00548   } else {
00549     sel_flg = 0; // using elements from selection
00550   }
00551 
00552   // temporary variables
00553   float tmp_w;
00554   int w_index = 0;              // the term in the weight field to use
00555   int sel1ind = sel1->firstsel; // start from the first selected atom
00556   int sel2ind = sel2->firstsel; // start from the first selected atom
00557   float wsum = 0;
00558   float rmsdsum = 0;
00559 
00560   *rmsd = 10000000; // if we bail out, return a huge value
00561 
00562   // compute the rmsd
00563   int count = sel1->selected;
00564   while (count-- > 0) {
00565     // find next 'on' atom in sel1 and sel2
00566     // loop is safe since we already stop the on count > 0 above
00567     while (!sel1->on[sel1ind])
00568       sel1ind++;
00569     while (!sel2->on[sel2ind])
00570       sel2ind++;
00571 
00572     // the weight offset to use depends on how many terms there are
00573     if (sel_flg == 0) {
00574       tmp_w = weight[w_index++];
00575     } else {
00576       tmp_w = weight[sel1ind]; // use the first selection for the weights
00577     }
00578 
00579     // sum the calculated rmsd and weight values
00580     rmsdsum += tmp_w * distance2(framepos1 + 3*sel1ind, framepos2 + 3*sel2ind);
00581     wsum += tmp_w;
00582 
00583     // and advance to the next atom pair
00584     sel1ind++;
00585     sel2ind++;
00586   }
00587 
00588   // check weight sum
00589   if (wsum == 0) {
00590     return MEASURE_ERR_BADWEIGHTSUM;
00591   }
00592 
00593   // finish the rmsd calcs
00594   *rmsd = sqrtf(rmsdsum / wsum);
00595 
00596   return MEASURE_NOERR; // and say rmsd is OK
00597 }
00598 
00599 /* jacobi.C, taken from Numerical Recipes and specialized to 3x3 case */
00600 
00601 #define ROTATE(a,i,j,k,l) g=a[i][j];h=a[k][l];a[i][j]=g-s*(h+g*tau);\
00602         a[k][l]=h+s*(g-h*tau);
00603 
00604 static int jacobi(float a[4][4], float d[3], float v[3][3])
00605 {
00606   int n=3;
00607   int j,iq,ip,i;
00608   float tresh,theta,tau,t,sm,s,h,g,c,*b,*z;
00609   
00610   b=new float[n];
00611   z=new float[n];
00612   for (ip=0;ip<n;ip++) {
00613     for (iq=0;iq<n;iq++) v[ip][iq]=0.0;
00614     v[ip][ip]=1.0;
00615   }
00616   for (ip=0;ip<n;ip++) {
00617     b[ip]=d[ip]=a[ip][ip];
00618     z[ip]=0.0;
00619   }
00620   for (i=1;i<=50;i++) {
00621     sm=0.0;
00622     for (ip=0;ip<n-1;ip++) {
00623       for (iq=ip+1;iq<n;iq++)
00624         sm += (float) fabs(a[ip][iq]);
00625     }
00626     if (sm == 0.0) {
00627       delete [] z;
00628       delete [] b;
00629       return 0; // Exit normally
00630     }
00631     if (i < 4)
00632       tresh=0.2f*sm/(n*n);
00633     else
00634       tresh=0.0f;
00635     for (ip=0;ip<n-1;ip++) {
00636       for (iq=ip+1;iq<n;iq++) {
00637         g=100.0f * fabsf(a[ip][iq]);
00638         if (i > 4 && (float)(fabs(d[ip])+g) == (float)fabs(d[ip])
00639             && (float)(fabs(d[iq])+g) == (float)fabs(d[iq]))
00640           a[ip][iq]=0.0;
00641         else if (fabs(a[ip][iq]) > tresh) {
00642           h=d[iq]-d[ip];
00643           if ((float)(fabs(h)+g) == (float)fabs(h))
00644             t=(a[ip][iq])/h;
00645           else {
00646             theta=0.5f*h/(a[ip][iq]);
00647             t=1.0f/(fabsf(theta)+sqrtf(1.0f+theta*theta));
00648             if (theta < 0.0f) t = -t;
00649           }
00650           c=1.0f/sqrtf(1+t*t);
00651           s=t*c;
00652           tau=s/(1.0f+c);
00653           h=t*a[ip][iq];
00654           z[ip] -= h;
00655           z[iq] += h;
00656           d[ip] -= h;
00657           d[iq] += h;
00658           a[ip][iq]=0.0;
00659           for (j=0;j<=ip-1;j++) {
00660             ROTATE(a,j,ip,j,iq)
00661               }
00662           for (j=ip+1;j<=iq-1;j++) {
00663             ROTATE(a,ip,j,j,iq)
00664               }
00665           for (j=iq+1;j<n;j++) {
00666             ROTATE(a,ip,j,iq,j)
00667               }
00668           for (j=0;j<n;j++) {
00669             ROTATE(v,j,ip,j,iq)
00670               }
00671         }
00672       }
00673     }
00674     for (ip=0;ip<n;ip++) {
00675       b[ip] += z[ip];
00676       d[ip]=b[ip];
00677       z[ip]=0.0;
00678     }
00679   }
00680   delete [] b;
00681   delete [] z;
00682   return 1; // Failed to converge
00683 }
00684 #undef ROTATE
00685 
00686 static int transvecinv(const double *v, Matrix4 &res) {
00687   double x, y, z;
00688   x=v[0];
00689   y=v[1];
00690   z=v[2];
00691   if (x == 0.0 && y == 0.0) {
00692     if (z == 0.0) {
00693       return -1;
00694     }
00695     if (z > 0)
00696       res.rot(90, 'y');
00697     else
00698       res.rot(-90, 'y');
00699     return 0;
00700   }
00701   double theta = atan2(y,x);
00702   double length = sqrt(x*x + y*y);
00703   double phi = atan2(z,length);
00704   res.rot((float) RADTODEG(phi),  'y');
00705   res.rot((float) RADTODEG(-theta), 'z');
00706   return 0;
00707 }
00708  
00709 static int transvec(const double *v, Matrix4 &res) {
00710   double x, y, z;
00711   x=v[0];
00712   y=v[1];
00713   z=v[2];
00714   if (x == 0.0 && y == 0.0) {
00715     if (z == 0.0) {
00716       return -1;
00717     }
00718     if (z > 0)
00719       res.rot(-90, 'y');
00720     else
00721       res.rot(90, 'y');
00722     return 0;
00723   }
00724   double theta = atan2(y,x);
00725   double length = sqrt(x*x + y*y);
00726   double phi = atan2(z,length);
00727   res.rot((float) RADTODEG(theta), 'z');
00728   res.rot((float) RADTODEG(-phi),  'y');
00729   return 0;
00730 }
00731 
00732 static Matrix4 myfit2(const float *x, const float *y, 
00733                const float *comx, const float *comy) {
00734 
00735   Matrix4 res;
00736   double dx[3], dy[3];
00737   dx[0] = x[0] - comx[0];
00738   dx[1] = x[1] - comx[1];
00739   dx[2] = x[2] - comx[2];
00740   dy[0] = y[0] - comy[0];
00741   dy[1] = y[1] - comy[1];
00742   dy[2] = y[2] - comy[2];
00743   
00744   res.translate(comy[0], comy[1], comy[2]);
00745   transvec(dy, res);
00746   transvecinv(dx, res);
00747   res.translate(-comx[0], -comx[1], -comx[2]);
00748   return res;
00749 }
00750 
00751 static Matrix4 myfit3(const float *x1, const float *x2, 
00752                       const float *y1, const float *y2,
00753                       const float *comx, const float *comy) {
00754    
00755   Matrix4 mx, my, rx1, ry1;
00756   double dx1[3], dy1[3], angle;
00757   float dx2[3], dy2[3], x2t[3], y2t[3];
00758 
00759   for (int i=0; i<3; i++) {
00760     dx1[i] = x1[i] - comx[i];
00761     dx2[i] = x2[i] - comx[i];
00762     dy1[i] = y1[i] - comy[i];
00763     dy2[i] = y2[i] - comy[i];
00764   }
00765 
00766   // At some point, multmatrix went from pre-multiplying, as the code of 
00767   // Matrix.C itself suggests, to post multiplying, which is what the 
00768   // users must have expected. Thus my.multmatrix(mx) is the same as 
00769   // my = my * mx, not mx * my.  This means that you use the matrix 
00770   // conventions of openGL (first matrix in the code is the last 
00771   // matrix applied)
00772   transvecinv(dx1, rx1);
00773   rx1.multpoint3d(dx2, x2t);
00774   angle = atan2(x2t[2], x2t[1]);
00775   mx.rot((float) RADTODEG(angle), 'x'); 
00776   mx.multmatrix(rx1);
00777   mx.translate(-comx[0], -comx[1], -comx[2]);
00778   
00779   transvecinv(dy1, ry1);
00780   ry1.multpoint3d(dy2, y2t);
00781   angle = atan2(y2t[2], y2t[1]);
00782   my.rot((float) RADTODEG(angle), 'x');
00783   my.multmatrix(ry1);
00784   my.translate(-comy[0], -comy[1], -comy[2]);
00785   my.inverse();
00786   my.multmatrix(mx);
00787   return my;
00788 }
00789 
00790 // find the best fit alignment to take the first structure into the second
00791 // Put the result in the matrix 'mat'
00792 //  This algorithm comes from Kabsch, Acta Cryst. (1978) A34, 827-828.
00793 // Need the 2nd weight for the com calculation
00794 int measure_fit(const AtomSel *sel1, const AtomSel *sel2, const float *x, 
00795                 const float *y, const float *weight, 
00796                 const int *order, Matrix4 *mat) {
00797   float comx[3];
00798   float comy[3];
00799   int num = sel1->selected;
00800   int ret_val;
00801   ret_val = measure_center(sel1, x, weight, comx);
00802   if (ret_val < 0) {
00803     return ret_val;
00804   }
00805   ret_val = measure_center(sel2, y, weight, comy);
00806   if (ret_val < 0) {
00807     return ret_val;
00808   }
00809 
00810   // the Kabsch method won't work of the number of atoms is less than 4
00811   // (and won't work in some cases of n > 4; I think it works so long as
00812   // three or more planes are needed to intersect all the data points
00813   switch (sel1->selected) {
00814   case 1: { // simple center of mass alignment
00815     Matrix4 tmp;
00816     tmp.translate(-comx[0], -comx[1], -comx[2]);
00817     tmp.translate(comy[0], comy[1], comy[2]);
00818     memcpy(mat->mat, tmp.mat, 16*sizeof(float));
00819     return MEASURE_NOERR;
00820   }
00821   case 3:
00822   case 2: { 
00823     // find the first (n-1) points (from each molecule)
00824     int pts[6], count = 0;
00825     int n;
00826     for (n=sel1->firstsel; n<=sel1->lastsel; n++) {
00827       if (sel1->on[n]) {
00828         pts[count++] = n;
00829         if (sel1->selected == 2) {
00830           count++;                   // will put y data in pts[3]
00831           break;
00832         }
00833         if (count == 2) break;
00834       }
00835     }
00836     for (n=sel2->firstsel; n<=sel2->lastsel; n++) {
00837       if (sel2->on[n]) {
00838         pts[count++] = n;
00839         if (sel1->selected == 2) {
00840           count++;
00841           break;
00842         }
00843         if (count == 4) break;
00844       }
00845     }
00846     if (count != 4) {
00847       return MEASURE_ERR_MISMATCHEDCNT;
00848     }
00849     
00850     // reorder the sel2 atoms according to the order parameter
00851     if (order != NULL) {
00852       int i; 
00853       int tmp[6];
00854       memcpy(tmp, pts, sizeof(pts));
00855       for (i=0; i<num; i++) {
00856         pts[i + num] = tmp[num + order[i]]; // order indices are 0-based
00857       } 
00858     }
00859 
00860     if (sel1->selected == 2) {
00861       *mat = myfit2(x+3*pts[0],y+3*pts[2], comx, comy);
00862       ret_val = 0;
00863     } else {
00864       *mat = myfit3(x+3*pts[0],x+3*pts[1],y+3*pts[2],y+3*pts[3],comx,comy);
00865       ret_val = 0;
00866     }  
00867     if (ret_val != 0) {
00868       return MEASURE_ERR_GENERAL;
00869     }
00870 
00871     return 0;
00872   }
00873   default:
00874     break;
00875   }
00876   // at this point I know all the data values are good
00877  
00878 
00879   // use the new RMS fit implementation by default unless told otherwise 
00880   char *opt = getenv("VMDRMSFITMETHOD");
00881   if (!opt || strcmp(opt, "oldvmd")) {
00882     int i, k;
00883     float *v1, *v2;
00884     v1 = new float[3*num];
00885     v2 = new float[3*num];
00886     for (k=0, i=sel1->firstsel; i<=sel1->lastsel; i++) {
00887       if (sel1->on[i]) {
00888         int ind = 3 * i;
00889         v1[k++] = x[ind    ];
00890         v1[k++] = x[ind + 1];
00891         v1[k++] = x[ind + 2];
00892       }
00893     }
00894     for (k=0, i=sel2->firstsel; i<=sel2->lastsel; i++) {
00895       if (sel2->on[i]) {
00896         int ind = 3 * i;
00897         v2[k++] = y[ind    ];
00898         v2[k++] = y[ind + 1];
00899         v2[k++] = y[ind + 2];
00900       }
00901     }
00902 
00903     // reorder the sel2 atoms according to the order parameter
00904     if (order != NULL) {
00905       int i; 
00906       float *tmp = new float[3*num];
00907       memcpy(tmp, v2, 3*num*sizeof(float));
00908       for (i=0; i<num; i++) {
00909         int ind = 3 * i;
00910         int idx = 3 * order[i]; // order indices are 0-based
00911         v2[ind    ] = tmp[idx    ];
00912         v2[ind + 1] = tmp[idx + 1];
00913         v2[ind + 2] = tmp[idx + 2];
00914       } 
00915       delete[] tmp;
00916     }
00917 
00918     float tmp[16];
00919     // MatrixFitRMS returns RMS distance of fitted molecule.  Would be nice
00920     // to return this information to the user since it would make computing
00921     // the fitted RMSD much faster (no need to get the matrix, apply the
00922     // transformation, recompute RMS).
00923     MatrixFitRMS(num, v1, v2, weight, tmp);
00924 
00925     delete [] v1;
00926     delete [] v2;
00927     //fprintf(stderr, "got err %f\n", err);
00928     // output from FitRMS is a 3x3 matrix, plus a pre-translation stored in
00929     // row 3, and a post-translation stored in column 3.
00930     float pre[3], post[3];
00931     for (i=0; i<3; i++) {
00932       post[i] = tmp[4*i+3];
00933       tmp[4*i+3] = 0;
00934     }
00935     for (i=0; i<3; i++) {
00936       pre[i] = tmp[12+i];
00937       tmp[12+i] = 0;
00938     }
00939     Matrix4 result;
00940     result.translate(pre);
00941     result.multmatrix(Matrix4(tmp));
00942     result.translate(post);
00943     memcpy(mat->mat, result.mat, 16*sizeof(float));
00944     return 0;
00945   }
00946 
00947   // the old RMS fit code doesn't support reordering of sel2 currently
00948   if (order != NULL) {
00949     return MEASURE_ERR_NOTSUP;
00950   }
00951 
00952   // a) compute R = r(i,j) = sum( w(n) * (y(n,i)-comy(i)) * (x(n,j)-comx(j)))
00953   Matrix4 R;
00954   int i,j;
00955   float scale = (float) num * num;
00956   for (i=0; i<3; i++) {
00957     for (j=0; j<3; j++) {
00958       float tmp = 0;
00959       int nx = 0, ny = 0, k = 0; 
00960       while (nx < sel1->num_atoms && ny < sel2->num_atoms) { 
00961         if (!sel1->on[nx]) {
00962           nx++;
00963           continue;
00964         }
00965         if (!sel2->on[ny]) {
00966           ny++;
00967           continue;
00968         }
00969 
00970         // found both, so get data
00971         
00972         tmp += weight[k] * (y[3*ny+i] - comy[i]) * (x[3*nx+j] - comx[j]) /
00973           scale;
00974         nx++;
00975         ny++;
00976         k++;
00977       }
00978       R.mat[4*i+j] = tmp;
00979     }
00980   }
00981 
00982   // b) 1) form R~R
00983   Matrix4 Rt;
00984   for (i=0; i<3; i++) {
00985     for (j=0; j<3; j++) {
00986       Rt.mat[4*i+j] = R.mat[4*j+i];
00987     }
00988   }
00989   Matrix4 RtR(R);
00990   RtR.multmatrix(Rt);
00991 
00992   // b) 2) find the eigenvalues and eigenvectors
00993   float evalue[3];
00994   float evector[3][3];
00995   float tmpmat[4][4];
00996   for (i=0; i<4; i++)
00997     for (j=0; j<4; j++)
00998       tmpmat[i][j]=RtR.mat[4*i+j];
00999 
01000   if(jacobi(tmpmat,evalue,evector) != 0) return MEASURE_ERR_NONZEROJACOBI;
01001   // transposition the evector matrix to put the vectors in rows
01002   float vectmp;
01003   vectmp=evector[0][1]; evector[0][1]=evector[1][0]; evector[1][0]=vectmp;
01004   vectmp=evector[0][2]; evector[0][2]=evector[2][0]; evector[2][0]=vectmp;
01005   vectmp=evector[2][1]; evector[2][1]=evector[1][2]; evector[1][2]=vectmp;
01006 
01007 
01008   // b) 4) sort so that the eigenvalues are from largest to smallest
01009   //      (or rather so a[0] is eigenvector with largest eigenvalue, ...)
01010   float *a[3];
01011   a[0] = evector[0];
01012   a[1] = evector[1];
01013   a[2] = evector[2];
01014 #define SWAP(qq,ww) {                                           \
01015     float v; float *v1;                                         \
01016     v = evalue[qq]; evalue[qq] = evalue[ww]; evalue[ww] = v;    \
01017     v1 = a[qq]; a[qq] = a[ww]; a[ww] = v1;                      \
01018 }
01019   if (evalue[0] < evalue[1]) {
01020     SWAP(0, 1);
01021   }
01022   if (evalue[0] < evalue[2]) {
01023     SWAP(0, 2);
01024   }
01025   if (evalue[1] < evalue[2]) {
01026     SWAP(1, 2);
01027   }
01028 
01029   
01030   // c) determine b(i) = R*a(i)
01031   float b[3][3];
01032 
01033   Rt.multpoint3d(a[0], b[0]);
01034   vec_normalize(b[0]);
01035 
01036   Rt.multpoint3d(a[1], b[1]);
01037   vec_normalize(b[1]);
01038 
01039   Rt.multpoint3d(a[2], b[2]);
01040   vec_normalize(b[2]);
01041 
01042   // d) compute U = u(i,j) = sum(b(k,i) * a(k,j))
01043   Matrix4 U;
01044   for (i=0; i<3; i++) {
01045     for (j=0; j<3; j++) {
01046       float *tmp = &(U.mat[4*j+i]);
01047       *tmp = 0;
01048       for (int k=0; k<3; k++) {
01049         *tmp += b[k][i] * a[k][j];
01050       }
01051     }
01052   }
01053 
01054   // Check the determinant of U.  If it's negative, we need to
01055   // flip the sign of the last row.
01056   float *um = U.mat;
01057   float det = 
01058     um[0]*(um[4+1]*um[8+2] - um[4+2]*um[8+1]) -
01059     um[1]*(um[4+0]*um[8+2] - um[4+2]*um[8+0]) +
01060     um[2]*(um[4+0]*um[8+1] - um[4+1]*um[8+0]);
01061   if (det < 0) {
01062     for (int q=0; q<3; q++) um[8+q] = -um[8+q];
01063   }
01064 
01065   // e) apply the offset for com
01066   Matrix4 tx;
01067   tx.translate(-comx[0], -comx[1], -comx[2]);
01068   Matrix4 ty;
01069   ty.translate(comy[0], comy[1], comy[2]);
01070   //  U.multmatrix(com);
01071   ty.multmatrix(U);
01072   ty.multmatrix(tx);
01073   memcpy(mat->mat, ty.mat, 16*sizeof(float));
01074   return MEASURE_NOERR;
01075 }
01076 
01077 // For different values of the random seed, the computed SASA's of brH.pdb 
01078 // converge to within 1% of each other when the number of points is about
01079 // 500.  We therefore use 500 as the default number.
01080 #define NPTS 500 
01081 extern int measure_sasa(const AtomSel *sel, const float *framepos,
01082     const float *radius, float srad, float *sasa, 
01083     ResizeArray<float> *sasapts, const AtomSel *restrictsel,
01084     const int *nsamples) {
01085 
01086   // check arguments
01087   if (!sel) return MEASURE_ERR_NOSEL;
01088   if (!sel->selected) {
01089     *sasa = 0;
01090     return MEASURE_NOERR;
01091   }
01092   if (!framepos) return MEASURE_ERR_NOFRAMEPOS;
01093   if (!radius)   return MEASURE_ERR_NORADII;
01094   if (restrictsel && restrictsel->num_atoms != sel->num_atoms)
01095     return MEASURE_ERR_MISMATCHEDCNT;
01096 
01097   int i;
01098   int npts = nsamples ? *nsamples : NPTS;
01099   float maxrad = -1;
01100 
01101 
01102   // find biggest atom radius 
01103   for (i=0; i<sel->num_atoms; i++) {
01104     float rad = radius[i];
01105     if (maxrad < rad) maxrad = rad;
01106   }
01107 
01108   // Find atoms within maxrad of each other.  
01109   // build a list of pairs for each atom
01110   ResizeArray<int> *pairlist = new ResizeArray<int>[sel->num_atoms];
01111   {
01112     GridSearchPair *pairs;
01113     pairs = vmd_gridsearch1(framepos, sel->num_atoms, sel->on, 
01114                             2.0f * (maxrad + srad), 0, sel->num_atoms * 1000);
01115 
01116     GridSearchPair *p, *tmp; 
01117     for (p = pairs; p != NULL; p = tmp) {
01118       int ind1=p->ind1;
01119       int ind2=p->ind2;
01120       pairlist[ind1].append(ind2);
01121       pairlist[ind2].append(ind1);
01122       tmp = p->next;
01123       free(p);
01124     }
01125   }
01126 
01127   static const float RAND_MAX_INV = 1.0f/VMD_RAND_MAX;
01128   // Seed the random number generator before each calculation.  This gives
01129   // reproducible results and still allows a more accurate answer to be
01130   // obtained by increasing the samples size.  I don't know if this is a
01131   // "good" seed value or not, I just picked something random-looking.
01132   vmd_srandom(38572111);
01133 
01134   // All the spheres use the same random points.  
01135   float *spherepts = new float[3*npts];
01136   for (i=0; i<npts; i++) {
01137     float u1 = (float) vmd_random();
01138     float u2 = (float) vmd_random();
01139     float z = 2.0f*u1*RAND_MAX_INV -1.0f;
01140     float phi = (float) (2.0f*VMD_PI*u2*RAND_MAX_INV);
01141     float R = sqrtf(1.0f-z*z);
01142     spherepts[3*i  ] = R*cosf(phi);
01143     spherepts[3*i+1] = R*sinf(phi);
01144     spherepts[3*i+2] = z;
01145   }
01146 
01147   const float prefac = (float) (4 * VMD_PI / npts);
01148   float totarea = 0.0f;
01149   // compute area for each atom based on its pairlist
01150   for (i=sel->firstsel; i<=sel->lastsel; i++) {
01151     if (sel->on[i]) {
01152       // only atoms in restrictsel contribute
01153       if (restrictsel && !restrictsel->on[i]) continue;
01154       const float *loc = framepos+3*i;
01155       float rad = radius[i]+srad;
01156       float surfpos[3];
01157       int surfpts = npts;
01158       const ResizeArray<int> &nbrs = pairlist[i];
01159       for (int j=0; j<npts; j++) {
01160         surfpos[0] = loc[0] + rad*spherepts[3*j  ];
01161         surfpos[1] = loc[1] + rad*spherepts[3*j+1];
01162         surfpos[2] = loc[2] + rad*spherepts[3*j+2];
01163         int on = 1;
01164         for (int k=0; k<nbrs.num(); k++) {
01165           int ind = nbrs[k];
01166           const float *nbrloc = framepos+3*ind;
01167           float radsq = radius[ind]+srad; radsq *= radsq;
01168           float dx = surfpos[0]-nbrloc[0];
01169           float dy = surfpos[1]-nbrloc[1];
01170           float dz = surfpos[2]-nbrloc[2];
01171           if (dx*dx + dy*dy + dz*dz <= radsq) {
01172             on = 0;
01173             break;
01174           }
01175         }
01176         if (on) {
01177           if (sasapts) {
01178             sasapts->append(surfpos[0]);
01179             sasapts->append(surfpos[1]);
01180             sasapts->append(surfpos[2]);
01181           }
01182         } else {
01183           surfpts--;
01184         }
01185       }
01186       float atomarea = prefac * rad * rad * surfpts;
01187       totarea += atomarea;
01188     }
01189   }
01190 
01191   delete [] pairlist;
01192   delete [] spherepts;
01193   *sasa = totarea;
01194   return 0;
01195 }
01196 
01197 
01198 //
01199 // Calculate g(r)
01200 //
01201 
01202 // find the minimum image distance for one coordinate component 
01203 // and square the result (orthogonal cells only).
01204 static float fix_pbc_n_sqr(float delta, const float boxby2) {
01205   while (delta >= boxby2) { delta -= 2.0f * boxby2; }
01206   while (delta < -boxby2) { delta += 2.0f * boxby2; }
01207   return delta * delta;
01208 }
01209 
01210 // calculate the minimum distance between two positions with respect 
01211 // to the periodic cell (orthogonal cells only).
01212 static float min_dist_with_pbc(const float *a, const float *b, 
01213                                 const float *boxby2) {
01214   float distsqr;
01215   distsqr  = fix_pbc_n_sqr(a[0] - b[0], boxby2[0]);
01216   distsqr += fix_pbc_n_sqr(a[1] - b[1], boxby2[1]);
01217   distsqr += fix_pbc_n_sqr(a[2] - b[2], boxby2[2]);
01218   return sqrtf(distsqr);
01219 }
01220 
01225 static inline double spherical_cap(const double &radius, const double &boxby2) {
01226   return (VMD_PI / 3.0 * (radius - boxby2) * (radius - boxby2)
01227           * ( 2.0 * radius + boxby2));
01228 }
01229 
01230 
01231 typedef struct {
01232   int threadid;
01233   int threadcount;
01234   int count_o_start;
01235   int count_o_end;
01236   const float *olist;
01237   int count_i;
01238   const float *ilist;
01239   int count_h;
01240   int *hlist;
01241   float delta;
01242   const float *boxby2;
01243   wkfmsgtimer *msgtp;
01244   int curframe;
01245   int maxframe;
01246 } gofrparms_t;
01247     
01248 // calculate the non-normalized pair-distribution function
01249 // for two lists of atom coordinates and store the resulting
01250 // histogram in the hlist array. orthogonal cell version.
01251 //
01252 // NOTE: this is just the workhorse function. special issues related
01253 // to atoms present in both lists have to be dealt with in the uplevel
01254 // functions, that then also can do various post-processing steps.
01255 extern "C" void * measure_gofr_orth(void *voidparms) {
01256   // handle per-thread parameters
01257   gofrparms_t *parms = (gofrparms_t *) voidparms;
01258   const int count_o_start = parms->count_o_start;
01259   const int count_o_end   = parms->count_o_end;
01260   const int count_i       = parms->count_i;
01261   const int count_h       = parms->count_h;
01262   const float *olist      = parms->olist;
01263   const float *ilist      = parms->ilist;
01264   const float *boxby2     = parms->boxby2;
01265   wkfmsgtimer *msgtp         = parms->msgtp;
01266   const int curframe      = parms->curframe;
01267   const int maxframe      = parms->maxframe;
01268   int *hlist              = parms->hlist;
01269   // other local variables
01270   int i, j, idx;
01271   float dist;
01272   const float deltascale = 1.0f / parms->delta;
01273   int msgcnt=0;
01274 
01275   // loop over the chunk of pairs that was associated to this thread.
01276   for (i=count_o_start; i<count_o_end; ++i) {
01277 
01278     // print progress messages only for thread(s) that have
01279     // a timer defined (usually only tid==0).
01280     if (msgtp && wkf_msg_timer_timeout(msgtp)) {
01281       char tmpbuf[1024];
01282       if (msgcnt==0) 
01283         sprintf(tmpbuf, "timestep %d of %d", curframe, maxframe);
01284       else
01285         sprintf(tmpbuf, "timestep %d of %d: (%6.2f %% complete)", curframe, maxframe, 
01286                 (100.0f * i) / (float) (count_o_end - count_o_start + 1));
01287       msgInfo << "vmd_measure_gofr_orth: " << tmpbuf << sendmsg;
01288       msgcnt++;
01289       // XXX we should update the display here...
01290     }
01291 
01292     for (j=0; j<count_i; ++j) {
01293       // calculate distance and add to histogram
01294       dist = min_dist_with_pbc(&olist[i*3], &ilist[j*3], boxby2);
01295       idx = (int) (dist * deltascale);
01296       if ((idx >= 0) && (idx < count_h)) 
01297         ++hlist[idx];
01298     }
01299   }
01300 
01301   return MEASURE_NOERR;
01302 }
01303 
01304 // main entry point for 'measure gofr'.
01305 // tasks:
01306 // - sanity check on arguments.
01307 // - select proper algorithm for PBC treatment.
01308 int measure_gofr(AtomSel *sel1, AtomSel *sel2, MoleculeList *mlist,
01309                  const int count_h, double *gofr, double *numint, double *histog,
01310                  const float delta, int first, int last, int step, int *framecntr,
01311                  int usepbc, int selupdate) {
01312   int i, j, frame;
01313   float a, b, c, alpha, beta, gamma;
01314   int isortho=0;    // orthogonal unit cell not assumed by default.
01315   int duplicates=0; // counter for duplicate atoms in both selections.
01316 
01317   // initialize a/b/c/alpha/beta/gamma to arbitrary defaults to please the compiler.
01318   a=b=c=9999999.0;
01319   alpha=beta=gamma=90.0;
01320 
01321   // reset counter for total, skipped, and _orth processed frames.
01322   framecntr[0]=framecntr[1]=framecntr[2]=0;
01323 
01324   // First round of sanity checks.
01325   // neither list can be undefined
01326   if (!sel1 || !sel2 ) {
01327     return MEASURE_ERR_NOSEL;
01328   }
01329 
01330   // make sure that both selections are from the same molecule
01331   // so that we know that PBC unit cell info is the same for both
01332   if (sel2->molid() != sel1->molid()) {
01333     return MEASURE_ERR_MISMATCHEDMOLS;
01334   }
01335 
01336   Molecule *mymol = mlist->mol_from_id(sel1->molid());
01337   int maxframe = mymol->numframes() - 1;
01338   int nframes = 0;
01339 
01340   if (last == -1)
01341     last = maxframe;
01342 
01343   if ((last < first) || (last < 0) || (step <=0) || (first < -1)
01344       || (maxframe < 0) || (last > maxframe)) {
01345       msgErr << "measure gofr: bad frame range given." 
01346              << " max. allowed frame#: " << maxframe << sendmsg;
01347     return MEASURE_ERR_BADFRAMERANGE;
01348   }
01349 
01350   // test for non-orthogonal PBC cells, zero volume cells, etc.
01351   if (usepbc) { 
01352     for (isortho=1, nframes=0, frame=first; frame <=last; ++nframes, frame += step) {
01353       const Timestep *ts;
01354 
01355       if (first == -1) {
01356         // use current frame only. don't loop.
01357         ts = sel1->timestep(mlist);
01358         frame=last;
01359       } else {
01360         ts = mymol->get_frame(frame);
01361       }
01362       // get periodic cell information for current frame
01363       a = ts->a_length;
01364       b = ts->b_length;
01365       c = ts->c_length;
01366       alpha = ts->alpha;
01367       beta = ts->beta;
01368       gamma = ts->gamma;
01369 
01370       // check validity of PBC cell side lengths
01371       if (fabsf(a*b*c) < 0.0001) {
01372         msgErr << "measure gofr: unit cell volume is zero." << sendmsg;
01373         return MEASURE_ERR_GENERAL;
01374       }
01375 
01376       // check PBC unit cell shape to select proper low level algorithm.
01377       if ((alpha != 90.0) || (beta != 90.0) || (gamma != 90.0)) {
01378         isortho=0;
01379       }
01380     }
01381   } else {
01382     // initialize a/b/c/alpha/beta/gamma to arbitrary defaults
01383     isortho=1;
01384     a=b=c=9999999.0;
01385     alpha=beta=gamma=90.0;
01386   }
01387 
01388   // until we can handle non-orthogonal periodic cells, this is fatal
01389   if (!isortho) {
01390     msgErr << "measure gofr: only orthorhombic cells are supported (for now)." << sendmsg;
01391     return MEASURE_ERR_GENERAL;
01392   }
01393 
01394   // clear the result arrays
01395   for (i=0; i<count_h; ++i) {
01396     gofr[i] = numint[i] = histog[i] = 0.0;
01397   }
01398 
01399   // pre-allocate coordinate buffers of the max size we'll
01400   // ever need, so we don't have to reallocate if/when atom
01401   // selections are updated on-the-fly
01402   float *sel1coords = new float[3*sel1->num_atoms];
01403   float *sel2coords = new float[3*sel2->num_atoms];
01404 
01405   // setup status message timer
01406   wkfmsgtimer *msgt = wkf_msg_timer_create(5);
01407 
01408   // threading setup.
01409   wkf_thread_t *threads;
01410   gofrparms_t  *parms;
01411 #if defined(VMDTHREADS)
01412   int numprocs = wkf_thread_numprocessors();
01413 #else
01414   int numprocs = 1;
01415 #endif
01416 
01417   threads = new wkf_thread_t[numprocs];
01418   memset(threads, 0, numprocs * sizeof(wkf_thread_t));
01419 
01420   // allocate and (partially) initialize array of per-thread parameters
01421   parms = new gofrparms_t[numprocs];
01422   for (i=0; i<numprocs; ++i) {
01423     parms[i].threadid = i;
01424     parms[i].threadcount = numprocs;
01425     parms[i].delta = (float) delta;
01426     parms[i].msgtp = NULL;
01427     parms[i].count_h = count_h;
01428     parms[i].hlist = new int[count_h];
01429   }
01430 
01431   msgInfo << "measure gofr: using multi-threaded implementation with " 
01432           << numprocs << ((numprocs > 1) ? " CPUs" : " CPU") << sendmsg;
01433 
01434   for (nframes=0,frame=first; frame <=last; ++nframes, frame += step) {
01435     const Timestep *ts1, *ts2;
01436 
01437     if (frame  == -1) {
01438       // use current frame only. don't loop.
01439       ts1 = sel1->timestep(mlist);
01440       ts2 = sel2->timestep(mlist);
01441       frame=last;
01442     } else {
01443       sel1->which_frame = frame;
01444       sel2->which_frame = frame;
01445       ts1 = ts2 = mymol->get_frame(frame); // requires sels from same mol
01446     }
01447 
01448     if (usepbc) {
01449       // get periodic cell information for current frame
01450       a     = ts1->a_length;
01451       b     = ts1->b_length;
01452       c     = ts1->c_length;
01453       alpha = ts1->alpha;
01454       beta  = ts1->beta;
01455       gamma = ts1->gamma;
01456     }
01457 
01458     // compute half periodic cell size
01459     float boxby2[3];
01460     boxby2[0] = 0.5f * a;
01461     boxby2[1] = 0.5f * b;
01462     boxby2[2] = 0.5f * c;
01463 
01464     // update the selections if the user desires it
01465     if (selupdate) {
01466       if (sel1->change(NULL, mymol) != AtomSel::PARSE_SUCCESS)
01467         msgErr << "measure gofr: failed to evaluate atom selection update";
01468       if (sel2->change(NULL, mymol) != AtomSel::PARSE_SUCCESS)
01469         msgErr << "measure gofr: failed to evaluate atom selection update";
01470     }
01471 
01472     // check for duplicate atoms in the two lists, as these will have
01473     // to be subtracted back out of the first histogram slot
01474     if (sel2->molid() == sel1->molid()) {
01475       int i;
01476       for (i=0, duplicates=0; i<sel1->num_atoms; ++i) {
01477         if (sel1->on[i] && sel2->on[i])
01478           ++duplicates;
01479       }
01480     }
01481 
01482     // copy selected atoms to the two coordinate lists
01483     // requires that selections come from the same molecule
01484     const float *framepos = ts1->pos;
01485     for (i=0, j=0; i<sel1->num_atoms; ++i) {
01486       if (sel1->on[i]) {
01487         int a = i*3;
01488         sel1coords[j    ] = framepos[a    ];
01489         sel1coords[j + 1] = framepos[a + 1];
01490         sel1coords[j + 2] = framepos[a + 2];
01491         j+=3;
01492       }
01493     }
01494     framepos = ts2->pos;
01495     for (i=0, j=0; i<sel2->num_atoms; ++i) {
01496       if (sel2->on[i]) {
01497         int a = i*3;
01498         sel2coords[j    ] = framepos[a    ];
01499         sel2coords[j + 1] = framepos[a + 1];
01500         sel2coords[j + 2] = framepos[a + 2];
01501         j+=3;
01502       }
01503     }
01504 
01505     // clear the histogram for this frame
01506     // and set up argument structure for threaded execution.
01507     int maxframe = (int) ((last - first + 1) / ((float) step));
01508     for (i=0; i<numprocs; ++i) {
01509       memset(parms[i].hlist, 0, count_h * sizeof(int));
01510       parms[i].boxby2 = boxby2;
01511       parms[i].curframe = frame;
01512       parms[i].maxframe = maxframe;
01513     }
01514     parms[0].msgtp = msgt;
01515     
01516     if (isortho && sel1->selected && sel2->selected) {
01517       int count_o = sel1->selected;
01518       int count_i = sel2->selected;
01519       const float *olist = sel1coords;
01520       const float *ilist = sel2coords;
01521       // make sure the outer loop is the longer one to have 
01522       // better threading efficiency and cache utilization.
01523       if (count_o < count_i) {
01524         count_o = sel2->selected;
01525         count_i = sel1->selected;
01526         olist = sel2coords;
01527         ilist = sel1coords;
01528       }
01529 
01530       // distribute outer loop across threads in fixed size chunks.
01531       // this should work very well for small numbers of threads.
01532       // thrdelta is the chunk size and we need it to be at least 1 
01533       // _and_ numprocs*thrdelta >= count_o.
01534       int thrdelta = (count_o + (numprocs-1)) / numprocs;
01535       int o_min = 0;
01536       int o_max = thrdelta;
01537       for (i=0; i<numprocs; ++i, o_min += thrdelta, o_max += thrdelta) {
01538         if (o_max >  count_o)  o_max = count_o; // curb loop to max
01539         if (o_min >= count_o)  o_max = - 1;     // no work for this thread. too little data.
01540         parms[i].count_o_start = o_min;
01541         parms[i].count_o_end   = o_max;
01542         parms[i].count_i       = count_i;
01543         parms[i].olist         = olist;
01544         parms[i].ilist         = ilist;
01545       }
01546       
01547       // do the gofr calculation for orthogonal boxes.
01548       // XXX. non-orthogonal box not supported yet. detected and handled above.
01549 #if defined(VMDTHREADS)
01550       for (i=0; i<numprocs; ++i) {
01551         wkf_thread_create(&threads[i], measure_gofr_orth, &parms[i]);
01552       }
01553       for (i=0; i<numprocs; ++i) {
01554         wkf_thread_join(threads[i], NULL);
01555       } 
01556 #else
01557       measure_gofr_orth((void *) &parms[0]);
01558 #endif
01559       ++framecntr[2]; // frame processed with _orth algorithm
01560     } else {
01561       ++framecntr[1]; // frame skipped
01562     }
01563     ++framecntr[0];   // total frames.
01564 
01565     // correct the first histogram slot for the number of atoms that are 
01566     // present in both lists. they'll end up in the first histogram bin. 
01567     // we subtract only from the first thread histogram which is always defined.
01568     parms[0].hlist[0] -= duplicates;
01569 
01570     // in case of going 'into the edges', we should cut 
01571     // off the part that is not properly normalized to 
01572     // not confuse people that don't know about this.
01573     int h_max=count_h;
01574     float smallside=a;
01575     if (isortho && usepbc) {
01576       if(b < smallside) {
01577         smallside=b;
01578       }
01579       if(c < smallside) {
01580         smallside=c;
01581       }
01582       h_max=(int) (sqrtf(0.5f)*smallside/delta) +1;
01583       if (h_max > count_h) {
01584         h_max=count_h;
01585       }
01586     }
01587     // compute normalization function.
01588     double all=0.0;
01589     double pair_dens = 0.0;
01590     if (sel1->selected && sel2->selected) {
01591       if (usepbc) {
01592         pair_dens = a * b * c / ((double)sel1->selected * (double)sel2->selected - (double)duplicates);
01593       } else { // assume a particle volume of 30 \AA^3 (~ 1 water).
01594         pair_dens = 30.0 * (double)sel1->selected / 
01595           ((double)sel1->selected * (double)sel2->selected - (double)duplicates);
01596       }
01597     }
01598     
01599     // XXX for orthogonal boxes, we can reduce this to rmax < sqrt(0.5)*smallest side
01600     for (i=0; i<h_max; ++i) {
01601       // radius of inner and outer sphere that form the spherical slice
01602       double r_in  = delta * (double)i;
01603       double r_out = delta * (double)(i+1);
01604       double slice_vol = 4.0 / 3.0 * VMD_PI
01605         * ((r_out * r_out * r_out) - (r_in * r_in * r_in));
01606 
01607       if (isortho && usepbc) {
01608         // add correction for 0.5*box < r <= sqrt(0.5)*box
01609         if (r_out > boxby2[0]) {
01610           slice_vol -= 2.0 * spherical_cap(r_out, boxby2[0]);
01611         }
01612         if (r_out > boxby2[1]) {
01613           slice_vol -= 2.0 * spherical_cap(r_out, boxby2[1]);
01614         }
01615         if (r_out > boxby2[2]) {
01616           slice_vol -= 2.0 * spherical_cap(r_out, boxby2[2]);
01617         }
01618         if (r_in > boxby2[0]) {
01619           slice_vol += 2.0 * spherical_cap(r_in, boxby2[0]);
01620         }
01621         if (r_in > boxby2[1]) {
01622           slice_vol += 2.0 * spherical_cap(r_in, boxby2[1]);
01623         }
01624         if (r_in > boxby2[2]) {
01625           slice_vol += 2.0 * spherical_cap(r_in, boxby2[2]);
01626         }
01627       }
01628 
01629       double normf = pair_dens / slice_vol;
01630       double histv = 0.0;
01631       for (j=0; j<numprocs; ++j) {
01632         histv += (double) parms[j].hlist[i];
01633       }
01634       gofr[i] += normf * histv;
01635       all     += histv;
01636       if (sel1->selected) {
01637         numint[i] += all / (double)(sel1->selected);
01638       }
01639       histog[i] += histv;
01640     }
01641   }
01642   delete [] sel1coords;
01643   delete [] sel2coords;
01644 
01645   double norm = 1.0 / (double) nframes;
01646   for (i=0; i<count_h; ++i) {
01647     gofr[i]   *= norm;
01648     numint[i] *= norm;
01649     histog[i] *= norm;
01650   }
01651   wkf_msg_timer_destroy(msgt);
01652 
01653   // release thread-related storage
01654   for (i=0; i<numprocs; ++i) {
01655     delete [] parms[i].hlist;
01656   }
01657   delete [] threads;
01658   delete [] parms;
01659 
01660   return MEASURE_NOERR;
01661 }
01662 
01663 
01664 int measure_geom(MoleculeList *mlist, int *molid, int *atmid, ResizeArray<float> *gValues,
01665                  int frame, int first, int last, int defmolid, int geomtype) {
01666   int i, ret_val;
01667   for(i=0; i < geomtype; i++) {
01668     // make sure an atom is not repeated in this list
01669     if(i > 0 && molid[i-1]==molid[i] && atmid[i-1]==atmid[i]) {
01670       printf("measure_geom: %i/%i %i/%i\n", molid[i-1],atmid[i-1],molid[i],atmid[i]);
01671       return MEASURE_ERR_REPEATEDATOM;
01672     }
01673   }
01674 
01675   float value;
01676   int max_ts, orig_ts;
01677 
01678   // use the default molecule to determine which frames to cycle through
01679   Molecule *mol = mlist->mol_from_id(defmolid);
01680   if( !mol )
01681     return MEASURE_ERR_NOMOLECULE;
01682   
01683   // get current frame number and make sure there are frames
01684   if((orig_ts = mol->frame()) < 0)
01685     return MEASURE_ERR_NOFRAMES;
01686   
01687   // get the max frame number and determine frame range
01688   max_ts = mol->numframes()-1;
01689   if (frame<0) {
01690     if (first<0 && last<0) first = last = orig_ts; 
01691     if (last<0 || last>max_ts) last = max_ts;
01692     if (first<0) first = 0;
01693   } else {
01694     if (frame>max_ts) frame = max_ts;
01695     first = last = frame; 
01696   }
01697   
01698   // go through all the frames, calculating values
01699   for(i=first; i <= last; i++) {
01700     mol->override_current_frame(i);
01701     switch (geomtype) {
01702     case MEASURE_BOND:
01703       if ((ret_val=calculate_bond(mlist, molid, atmid, &value))<0)
01704         return ret_val;
01705       gValues->append(value);
01706       break;
01707     case MEASURE_ANGLE:
01708       if ((ret_val=calculate_angle(mlist, molid, atmid, &value))<0)
01709         return ret_val;
01710       gValues->append(value);
01711       break;
01712     case MEASURE_DIHED:
01713       if ((ret_val=calculate_dihed(mlist, molid, atmid, &value))<0)
01714         return ret_val;
01715       gValues->append(value);
01716       break;
01717     }
01718   }
01719   
01720   // reset the current frame
01721   mol->override_current_frame(orig_ts);
01722   
01723   return MEASURE_NOERR;
01724 }
01725   
01726   
01727 // calculate the value of this geometry, and return it
01728 int calculate_bond(MoleculeList *mlist, int *molid, int *atmid, float *value) {
01729 
01730   // get coords to calculate distance 
01731   int ret_val;
01732   float pos1[3], pos2[3];
01733   if ((ret_val=normal_atom_coord(mlist->mol_from_id(molid[0]), atmid[0], pos1))<0)
01734     return ret_val;
01735   if ((ret_val=normal_atom_coord(mlist->mol_from_id(molid[1]), atmid[1], pos2))<0)
01736     return ret_val;
01737   
01738   vec_sub(pos2, pos2, pos1);
01739   *value = norm(pos2);
01740 
01741   return MEASURE_NOERR;
01742 }
01743 
01744 // calculate the value of this geometry, and return it
01745 int calculate_angle(MoleculeList *mlist, int *molid, int *atmid, float *value) {
01746 
01747   // get coords to calculate distance 
01748   int ret_val;
01749   float pos1[3], pos2[3], pos3[3], r1[3], r2[3];
01750   if((ret_val=normal_atom_coord(mlist->mol_from_id(molid[0]), atmid[0], pos1))<0)
01751     return ret_val;
01752   if((ret_val=normal_atom_coord(mlist->mol_from_id(molid[1]), atmid[1], pos2))<0)
01753     return ret_val;
01754   if((ret_val=normal_atom_coord(mlist->mol_from_id(molid[2]), atmid[2], pos3))<0)
01755     return ret_val;
01756 
01757   vec_sub(r1, pos1, pos2);
01758   vec_sub(r2, pos3, pos2);
01759   *value = angle(r1, r2);
01760 
01761   return MEASURE_NOERR;
01762 }
01763 
01764 // calculate the value of this geometry, and return it
01765 int calculate_dihed(MoleculeList *mlist, int *molid, int *atmid, float *value) {
01766 
01767   // get coords to calculate distance 
01768   int ret_val;
01769   float pos1[3], pos2[3], pos3[3], pos4[3]; 
01770   if((ret_val=normal_atom_coord(mlist->mol_from_id(molid[0]), atmid[0], pos1))<0)
01771     return ret_val;
01772   if((ret_val=normal_atom_coord(mlist->mol_from_id(molid[1]), atmid[1], pos2))<0)
01773     return ret_val;
01774   if((ret_val=normal_atom_coord(mlist->mol_from_id(molid[2]), atmid[2], pos3))<0)
01775     return ret_val;
01776   if((ret_val=normal_atom_coord(mlist->mol_from_id(molid[3]), atmid[3], pos4))<0)
01777     return ret_val;
01778 
01779   *value = dihedral(pos1, pos2, pos3, pos4);
01780 
01781   return MEASURE_NOERR;
01782 }
01783 
01784 
01785 // for the given Molecule, find the UNTRANSFORMED coords for the given atom
01786 // return Molecule pointer if successful, NULL otherwise.
01787 int normal_atom_coord(Molecule *mol, int a, float *pos) {
01788   Timestep *now;
01789 
01790   int cell[3];
01791   memset(cell, 0, 3*sizeof(int));
01792 
01793   // get the molecule pointer, and get the coords for the current timestep
01794   int ret_val = check_mol(mol, a);
01795   if (ret_val<0) 
01796     return ret_val;
01797 
01798   if ((now = mol->current())) {
01799     memcpy((void *)pos, (void *)(now->pos + 3*a), 3*sizeof(float));
01800     
01801     // Apply periodic image transformation before returning
01802     Matrix4 mat;
01803     now->get_transform_from_cell(cell, mat);
01804     mat.multpoint3d(pos, pos);
01805     
01806     return MEASURE_NOERR;
01807   }
01808   
01809   // if here, error (i.e. molecule contains no frames)
01810   return MEASURE_ERR_NOFRAMES;
01811 }
01812 
01813 
01814 // check whether the given molecule & atom index is OK
01815 // if OK, return Molecule pointer; otherwise, return NULL
01816 int check_mol(Molecule *mol, int a) {
01817 
01818   if (!mol)
01819     return MEASURE_ERR_NOMOLECULE;
01820   if (a < 0 || a >= mol->nAtoms)
01821     return MEASURE_ERR_BADATOMID;
01822   
01823   return MEASURE_NOERR;
01824 }
01825 
01826 
01827 int measure_energy(MoleculeList *mlist, int *molid, int *atmid, int natoms, ResizeArray<float> *gValues,
01828                  int frame, int first, int last, int defmolid, double *params, int geomtype) {
01829   int i, ret_val;
01830   for(i=0; i < natoms; i++) {
01831     // make sure an atom is not repeated in this list
01832     if(i > 0 && molid[i-1]==molid[i] && atmid[i-1]==atmid[i]) {
01833       printf("measure_energy: %i/%i %i/%i\n", molid[i-1],atmid[i-1],molid[i],atmid[i]);
01834       return MEASURE_ERR_REPEATEDATOM;
01835     }
01836   }
01837 
01838   float value;
01839   int max_ts, orig_ts;
01840 
01841   // use the default molecule to determine which frames to cycle through
01842   Molecule *mol = mlist->mol_from_id(defmolid);
01843   if( !mol )
01844     return MEASURE_ERR_NOMOLECULE;
01845   
01846   // get current frame number and make sure there are frames
01847   if((orig_ts = mol->frame()) < 0)
01848     return MEASURE_ERR_NOFRAMES;
01849   
01850   // get the max frame number and determine frame range
01851   max_ts = mol->numframes()-1;
01852   if (frame==-1) {
01853     if (first<0 && last<0) first = last = orig_ts; 
01854     if (last<0 || last>max_ts) last = max_ts;
01855     if (first<0) first = 0;
01856   } else {
01857     if (frame>max_ts || frame==-2) frame = max_ts;
01858     first = last = frame; 
01859   }
01860   
01861   // go through all the frames, calculating values
01862   for(i=first; i <= last; i++) {
01863     mol->override_current_frame(i);
01864     switch (geomtype) {
01865     case MEASURE_BOND:
01866       if ((ret_val=compute_bond_energy(mlist, molid, atmid, &value, (float) params[0], (float) params[1]))<0)
01867         return ret_val;
01868       gValues->append(value);
01869       break;
01870     case MEASURE_ANGLE:
01871       if ((ret_val=compute_angle_energy(mlist, molid, atmid, &value, (float) params[0], (float) params[1], (float) params[2], (float) params[3]))<0)
01872         return ret_val;
01873       gValues->append(value);
01874       break;
01875     case MEASURE_DIHED:
01876       if ((ret_val=compute_dihed_energy(mlist, molid, atmid, &value, (float) params[0], int(params[1]), (float) params[2]))<0)
01877         return ret_val;
01878       gValues->append(value);
01879       break;
01880     case MEASURE_IMPRP:
01881       if ((ret_val=compute_imprp_energy(mlist, molid, atmid, &value, (float) params[0], (float) params[1]))<0)
01882         return ret_val;
01883       gValues->append(value);
01884       break;
01885     case MEASURE_VDW:
01886       if ((ret_val=compute_vdw_energy(mlist, molid, atmid, &value, (float) params[0], (float) params[1], (float) params[2], (float) params[3], (float) params[4], (float) params[5]))<0)
01887         return ret_val;
01888       gValues->append(value);
01889       break;
01890     case MEASURE_ELECT:
01891       if ((ret_val=compute_elect_energy(mlist, molid, atmid, &value, (float) params[0], (float) params[1], (bool) params[2], (bool) params[3], (float) params[4]))<0)
01892         return ret_val;
01893       gValues->append(value);
01894       break;
01895     }
01896   }
01897   
01898   // reset the current frame
01899   mol->override_current_frame(orig_ts);
01900   
01901   return MEASURE_NOERR;
01902 }
01903   
01904 // calculate the energy of this geometry
01905 int compute_bond_energy(MoleculeList *mlist, int *molid, int *atmid, float *energy, float k, float x0) {
01906   int ret_val;
01907   float dist;
01908 
01909   // Get the coordinates
01910   if ((ret_val=calculate_bond(mlist, molid, atmid, &dist))<0)
01911         return ret_val;
01912   float x = dist-x0;
01913   *energy = k*x*x;
01914 
01915   return MEASURE_NOERR;
01916 }
01917 
01918 // calculate the energy of this geometry
01919 int compute_angle_energy(MoleculeList *mlist, int *molid, int *atmid, float *energy,
01920                          float k, float x0, float kub, float s0) {
01921   int ret_val;
01922   float value;
01923 
01924   // Get the coordinates
01925   if ((ret_val=calculate_angle(mlist, molid, atmid, &value))<0)
01926         return ret_val;
01927   float x = (float) DEGTORAD((value-x0));
01928   float s = 0.0f;
01929 
01930   if (kub>0.0f) {
01931     int twoatoms[2];
01932     twoatoms[0] = atmid[0];
01933     twoatoms[1] = atmid[2];
01934     if ((ret_val=calculate_bond(mlist, molid, twoatoms, &value))<0)
01935       return ret_val;
01936     s = value-s0;
01937   }
01938 
01939   *energy = k*x*x + kub*s*s;
01940 
01941   return MEASURE_NOERR;
01942 }
01943 
01944 // calculate the energy of this geometry
01945 int compute_dihed_energy(MoleculeList *mlist, int *molid, int *atmid, float *energy,
01946                          float k, int n, float delta) {
01947   int ret_val;
01948   float value;
01949 
01950   // Get the coordinates
01951   if ((ret_val=calculate_dihed(mlist, molid, atmid, &value))<0)
01952         return ret_val;
01953   *energy = k*(1+cosf((float) (DEGTORAD((n*value-delta)))));
01954 
01955   return MEASURE_NOERR;
01956 }
01957 
01958 // calculate the energy of this geometry
01959 int compute_imprp_energy(MoleculeList *mlist, int *molid, int *atmid, float *energy,
01960                          float k, float x0) {
01961   int ret_val;
01962   float value;
01963 
01964   // Get the coordinates
01965   if ((ret_val=calculate_dihed(mlist, molid, atmid, &value))<0)
01966         return ret_val;
01967   float x = (float) (DEGTORAD((value-x0)));
01968   *energy = k*x*x;
01969 
01970   return MEASURE_NOERR;
01971 }
01972 
01973 // Calculate the VDW energy for specified pair of atoms
01974 // VDW energy:                                               
01975 // Evdw = eps * ((Rmin/dist)**12 - 2*(Rmin/dist)**6)         
01976 // eps = sqrt(eps1*eps2),  Rmin = Rmin1+Rmin2                
01977 int compute_vdw_energy(MoleculeList *mlist, int *molid, int *atmid, float *energy, float eps1, float rmin1,
01978                          float eps2, float rmin2, float cutoff, float switchdist) {
01979   int ret_val;
01980   float dist;
01981 
01982   // Get the coordinates
01983   if ((ret_val=calculate_bond(mlist, molid, atmid, &dist))<0)
01984     return ret_val;
01985 
01986   float sw=1.0;
01987   if (switchdist>0.0 && cutoff>0.0) {
01988     if (dist>=cutoff) {
01989       sw = 0.0;
01990     } else if (dist>=switchdist) {
01991       // This is the CHARMM switching function
01992       float dist2 = dist*dist;
01993       float cut2 = cutoff*cutoff;
01994       float switch2 = switchdist*switchdist;
01995       float s = cut2-dist2;
01996       float range = cut2-switch2;
01997       sw = s*s*(cut2+2*dist2-3*switch2)/(range*range*range);
01998     }
01999   }
02000 
02001   float term6 = (float) powf((rmin1+rmin2)/dist,6);
02002   *energy = sqrtf(eps1*eps2)*(term6*term6 - 2.0f*term6)*sw;
02003 
02004   return MEASURE_NOERR;
02005 }
02006 
02007 int compute_elect_energy(MoleculeList *mlist, int *molid, int *atmid, float *energy, float q1, float q2,
02008                          bool flag1, bool flag2, float cutoff) {
02009   int ret_val;
02010   float dist;
02011 
02012   // Get the coordinates
02013   if ((ret_val=calculate_bond(mlist, molid, atmid, &dist))<0)
02014     return ret_val;
02015 
02016   // Get atom charges
02017   if (!flag1) q1 = mlist->mol_from_id(molid[0])->charge()[atmid[0]];
02018   if (!flag2) q2 = mlist->mol_from_id(molid[0])->charge()[atmid[1]];
02019 
02020   if (cutoff>0.0) {
02021     if (dist<cutoff) {
02022       float efac = 1.0f-dist*dist/(cutoff*cutoff);
02023       *energy = 332.0636f*q1*q2/dist*efac*efac;
02024     } else {
02025       *energy = 0.0f;
02026     }
02027   } else {
02028     *energy = 332.0636f*q1*q2/dist;
02029   }
02030 
02031   return MEASURE_NOERR;
02032 }
02033  
02034 
02035 // Compute the center of mass for a given selection.
02036 // The result is put in rcom which has to have a size of at least 3.
02037 static void center_of_mass(AtomSel *sel, MoleculeList *mlist, float *rcom) {
02038   int i;
02039   float m = 0, mtot = 0;
02040   Molecule *mol = mlist->mol_from_id(sel->molid());
02041 
02042   // get atom masses
02043   const float *mass = mol->mass();
02044 
02045   // get atom coordinates
02046   const float *pos = sel->coordinates(mlist);
02047 
02048   memset(rcom, 0, 3*sizeof(float));
02049 
02050   // center of mass
02051   for (i=sel->firstsel; i<=sel->lastsel; i++) {
02052     if (sel->on[i]) {
02053       int ind = i * 3;
02054 
02055       m = mass[i];
02056 
02057       rcom[0] += m*pos[ind    ];
02058       rcom[1] += m*pos[ind + 1];
02059       rcom[2] += m*pos[ind + 2];
02060 
02061       // total mass
02062       mtot += m;
02063     }
02064   }
02065 
02066   rcom[0] /= mtot;
02067   rcom[1] /= mtot;
02068   rcom[2] /= mtot;
02069 }
02070 
02071 
02072 // Calculate principle axes and moments of inertia for selected atoms.
02073 // The corresponding eigenvalues are also returned, they can be used
02074 // to see if two axes are equivalent. The center of mass will be put
02075 // in parameter rcom.
02076 // The user can provide his own set of coordinates in coor. If this
02077 // parameter is NULL then the coordinates from the selection are used.
02078 extern int measure_inertia(AtomSel *sel, MoleculeList *mlist, const float *coor, float rcom[3],
02079                            float priaxes[3][3], float itensor[4][4], float evalue[3]) {
02080   if (!sel)                     return MEASURE_ERR_NOSEL;
02081   if (sel->num_atoms == 0)      return MEASURE_ERR_NOATOMS;
02082 
02083   Molecule *mol = mlist->mol_from_id(sel->molid());
02084 
02085   float x, y, z, m;
02086   float Ixx=0, Iyy=0, Izz=0, Ixy=0, Ixz=0, Iyz=0;
02087   int i,j=0;
02088 
02089   // need to put 3x3 inertia tensor into 4x4 matrix for jacobi eigensolver
02090   // itensor = {{0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 1}};
02091   memset(itensor, 0, 16*sizeof(float));
02092   itensor[3][3] = 1.0;
02093 
02094   // compute center of mass
02095   center_of_mass(sel, mlist, rcom);
02096 
02097   // get atom coordinates
02098   const float *pos = sel->coordinates(mlist);
02099 
02100   // get atom masses
02101   const float *mass = mol->mass();
02102 
02103 
02104   // moments of inertia tensor
02105   for (i=sel->firstsel; i<=sel->lastsel; i++) {
02106     if (sel->on[i]) {
02107       // position relative to COM
02108       if (coor) {
02109         // use user provided coordinates
02110         x = coor[j*3    ] - rcom[0];
02111         y = coor[j*3 + 1] - rcom[1];
02112         z = coor[j*3 + 2] - rcom[2];
02113         j++;
02114       } else {
02115         // use coordinates from selection
02116         x = pos[i*3    ] - rcom[0];
02117         y = pos[i*3 + 1] - rcom[1];
02118         z = pos[i*3 + 2] - rcom[2];
02119       }
02120 
02121       m = mass[i];
02122 
02123       Ixx += m*(y*y+z*z);
02124       Iyy += m*(x*x+z*z);
02125       Izz += m*(x*x+y*y);
02126       Ixy -= m*x*y;
02127       Ixz -= m*x*z;
02128       Iyz -= m*y*z;
02129     }
02130   }
02131 
02132   itensor[0][0] = Ixx;
02133   itensor[1][1] = Iyy;
02134   itensor[2][2] = Izz;
02135   itensor[0][1] = Ixy;
02136   itensor[1][0] = Ixy;
02137   itensor[0][2] = Ixz;
02138   itensor[2][0] = Ixz;
02139   itensor[1][2] = Iyz;
02140   itensor[2][1] = Iyz;
02141 
02142   // Find the eigenvalues and eigenvectors of moments of inertia tensor.
02143   // The eigenvectors correspond to the principle axes of inertia.
02144   float evector[3][3];
02145   if (jacobi(itensor,evalue,evector) != 0) return MEASURE_ERR_NONZEROJACOBI;
02146 
02147   // transpose the evector matrix to put the vectors in rows
02148   float vectmp;
02149   vectmp=evector[0][1]; evector[0][1]=evector[1][0]; evector[1][0]=vectmp;
02150   vectmp=evector[0][2]; evector[0][2]=evector[2][0]; evector[2][0]=vectmp;
02151   vectmp=evector[2][1]; evector[2][1]=evector[1][2]; evector[1][2]=vectmp;
02152 
02153 
02154   // sort so that the eigenvalues are from largest to smallest
02155   // (or rather so a[0] is eigenvector with largest eigenvalue, ...)
02156   float *a[3];
02157   a[0] = evector[0];
02158   a[1] = evector[1];
02159   a[2] = evector[2];
02160   // The code for SWAP is copied from measure_fit().
02161   // It swaps rows in the eigenvector matrix.
02162 #define SWAP(qq,ww) {                                           \
02163     float v; float *v1;                                         \
02164     v = evalue[qq]; evalue[qq] = evalue[ww]; evalue[ww] = v;    \
02165     v1 = a[qq]; a[qq] = a[ww]; a[ww] = v1;                      \
02166 }
02167   if (evalue[0] < evalue[1]) {
02168     SWAP(0, 1);
02169   }
02170   if (evalue[0] < evalue[2]) {
02171     SWAP(0, 2);
02172   }
02173   if (evalue[1] < evalue[2]) {
02174     SWAP(1, 2);
02175   }
02176 
02177 #if 0
02178   // If the 2nd and 3rd eigenvalues are identical and not close to zero
02179   // then the corresponding axes are not unique. 
02180   if (evalue[1]/evalue[0]>0.1 && fabs(evalue[1]-evalue[2])/evalue[0]<0.05
02181       && fabs(evalue[0]-evalue[1])/evalue[0]>0.05) {
02182     msgInfo << "Principal axes of inertia 2 and 3 are not unique!" << sendmsg;
02183   }
02184 #endif
02185 
02186   for (i=0; i<3; i++) {
02187     for (j=0; j<3; j++) 
02188       priaxes[i][j] = a[i][j];
02189   }
02190 
02191   return MEASURE_NOERR;
02192 }
02193 

Generated on Mon May 20 01:45:49 2013 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002