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

MeasurePBC.C

Go to the documentation of this file.
00001 /***************************************************************************
00002  *cr
00003  *cr            (C) Copyright 1995-2008 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: MeasurePBC.C,v $
00013  *      $Author: johns $        $Locker:  $             $State: Exp $
00014  *      $Revision: 1.7 $       $Date: 2008/04/28 22:47:58 $
00015  *
00016  ***************************************************************************
00017  * DESCRIPTION:
00018  *   Code to measure atom distances, angles, dihedrals, etc, 
00019  *   accounting for periodic boundary conditions
00020  ***************************************************************************/
00021 
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <math.h>
00025 #include "Measure.h"
00026 #include "AtomSel.h"
00027 #include "Matrix4.h"
00028 #include "utilities.h"
00029 #include "fitrms.h"
00030 #include "ResizeArray.h"
00031 #include "SpatialSearch.h"  // for find_within()
00032 #include "MoleculeList.h"
00033 #include "Inform.h"
00034 #include "Timestep.h"
00035 #include "VMDApp.h"
00036 
00037 //
00038 // Find an orthogonal basis R^3 with ob1=b1     
00039 //
00040 
00041 void orthonormal_basis(const float b[9], float e[9]) {
00042   float ob[3*3];
00043   vec_copy(ob+0, b+0);
00044   vec_copy(e+0, ob+0);
00045   vec_normalize(e+0);
00046   vec_triad(ob+3, b+3, -dot_prod(e+0, b+3), e+0);
00047   vec_copy(e+3, ob+3);
00048   vec_normalize(e+3);
00049   vec_triad(ob+6,  b+6, -dot_prod(e+0, b+6), e+0);
00050   vec_triad(ob+6, ob+6, -dot_prod(e+3, b+6), e+3);
00051   vec_copy(e+6, ob+6);
00052   vec_normalize(e+6);
00053 }
00054 
00055 //
00056 // Returns basis vectors in coordinates of an orthonormal 
00057 // basis obase.                                        
00058 //
00059 
00060 void basis_change(const float *base, const float *obase, float *newcoor, int n) {
00061   int i, j;
00062   for (i=0; i<n; i++) {
00063     for (j=0; j<n; j++) {
00064       newcoor[n*i+j] = dot_prod(&base[n*j], &obase[n*i]);
00065     }
00066   }
00067 }
00068 
00069 // Compute matrix that transforms coordinates from an arbitrary PBC cell 
00070 // into an orthonormal unitcell. Since the cell origin is not stored by VMD
00071 // you have to specify it.
00072 int measure_pbc2onc(MoleculeList *mlist, int molid, int frame, const float origin[3], Matrix4 &transform) {
00073   int orig_ts, max_ts;
00074 
00075   Molecule *mol = mlist->mol_from_id(molid);
00076   if( !mol )
00077     return MEASURE_ERR_NOMOLECULE;
00078  
00079   // get current frame number and make sure there are frames
00080   if((orig_ts = mol->frame()) < 0)
00081     return MEASURE_ERR_NOFRAMES;
00082   
00083   // get the max frame number and determine frame range
00084   max_ts = mol->numframes()-1;
00085   if (frame==-2)  frame = orig_ts;
00086   else if (frame>max_ts || frame==-1) frame = max_ts;
00087 
00088   Timestep *ts = mol->get_frame(frame);
00089 
00090   Matrix4 AA, BB, CC;
00091   ts->get_transforms(AA, BB, CC);
00092 
00093   // Construct the cell spanning vectors
00094   float cell[9];
00095   cell[0] = AA.mat[12];
00096   cell[1] = AA.mat[13];
00097   cell[2] = AA.mat[14];
00098   cell[3] = BB.mat[12];
00099   cell[4] = BB.mat[13];
00100   cell[5] = BB.mat[14];
00101   cell[6] = CC.mat[12];
00102   cell[7] = CC.mat[13];
00103   cell[8] = CC.mat[14];
00104 
00105   get_transform_to_orthonormal_cell(cell, origin, transform);
00106 
00107   return MEASURE_NOERR;
00108 }
00109 
00110 // Compute matrix that transforms coordinates from an arbitrary cell 
00111 // into an orthonormal unitcell. Since the origin is not stored by VMD
00112 // you have to specify it.
00113 // This is the lowlevel backend of measure_pbc2onc().
00114 
00115 // Here is a 2D example:
00116 // A and B are the are the displacement vectors which are needed to create 
00117 // the neighboring images. The parallelogram denotes the PBC cell with the origin O at its center.
00118 // The sqare to the right indicates the orthonormal unit cell i.e. the area into which the atoms 
00119 // will be wrapped by transformation T.
00120 //
00121 //                  + B                                        
00122 //                 /                              ^ B'         
00123 //       _________/________                       |            
00124 //      /        /        /                   +---|---+        
00125 //     /        /        /              T     |   |   |        
00126 //    /        O--------/-------> A   ====>   |   O---|--> A'  
00127 //   /                 /                      |       |        
00128 //  /_________________/                       +-------+        
00129   
00130 //  A  = displacement vector along X-axis with length a
00131 //  B  = displacement vector in XY-plane with length b
00132 //  A' = displacement vector along X-axis with length 1
00133 //  B' = displacement vector along Y-axis with length 1
00134 //  O = origin of the unit cell 
00135 
00136 void get_transform_to_orthonormal_cell(const float *cell, const float *center, Matrix4 &transform) {
00137   // Orthogonalize system:
00138   // Find an orthonormal basis of the cell (in cartesian coords).
00139   // If the cell vectors from VMD/NAMD are used this should actually always
00140   // return the identity matrix due to the way the cell vectors A, B and C
00141   // are defined (i.e. A || x; B lies in the x,y-plane; A, B, C form a right
00142   // hand system).
00143   float obase[3*3];
00144   orthonormal_basis(cell, obase);
00145 
00146   // Get orthonormal base in cartesian coordinates (it is the inverse of the
00147   // obase->cartesian transformation):
00148   float identity[3*3] = {1, 0, 0, 0, 1, 0, 0, 0, 1};
00149   float obase_cartcoor[3*3];
00150   basis_change(obase, identity, obase_cartcoor, 3);
00151 
00152 
00153   // Transform 3x3 into 4x4 matrix:
00154   Matrix4 obase2cartinv;
00155   trans_from_rotate(obase_cartcoor, &obase2cartinv);
00156 
00157   // This is the matrix for the obase->cartesian transformation:
00158   Matrix4 obase2cart = obase2cartinv;
00159   obase2cart.inverse();
00160 
00161   // Get coordinates of cell in terms of obase
00162   float m[3*3]; 
00163   basis_change(cell, obase, m, 3);
00164   Matrix4 rotmat;
00165   trans_from_rotate(m, &rotmat);
00166   rotmat.inverse();
00167 
00168   
00169   // Actually we have:
00170   // transform = translation * obase2cart * obase2cartinv * rotmat * obase2cart
00171   //                           `------------v------------'
00172   //                                       = 1
00173   transform = obase2cart;
00174   transform.multmatrix(rotmat); // pre-multiplication
00175 
00176   // Finally we need to apply the translation of the origin
00177   float origin[3];
00178   vec_copy(origin, center);
00179   vec_scaled_add(origin, -0.5, &cell[0]);
00180   vec_scaled_add(origin, -0.5, &cell[3]);
00181   vec_scaled_add(origin, -0.5, &cell[6]);
00182   vec_negate(origin, origin);
00183   //printf("origin={%3.f %3.f %3.f}\n", origin[0], origin[1], origin[2]);
00184   transform.translate(origin);
00185 }
00186 
00187 
00188 // Get the an array of coordinates of pbc image atoms for the specified selection.
00189 // The cutoff vector defines the region surrounding the pbc cell for which image 
00190 // atoms shall be constructed ({6 8 0} means 6 Angstrom for the direction of A,
00191 // 8 for B and no images in the C direction). The indexmap_array relates each
00192 // image atom to its corresponding main atom.
00193 // In case the molecule was aligned you can supply the alignment matrix which
00194 // is then used to correct for the rotation and shift of the pbc cell.
00195 // Since the pbc cell center is not stored in Timestep it must be provided.
00196 
00197 // The algorithm transforms the unitcell so that the unitcell minus the cutoff fits
00198 // into an orthonormal cell. Now the atoms in the rim can be easily identified and
00199 // wrapped into the neigboring cells. This works only if the largest cutoff
00200 // dimension is smaller than half of the smallest cell dimension. Otherwise a
00201 // slower algorithm is used that wraps each atom into all 26 neighboring cells
00202 // and checks if the image lies within cutoff.
00203 //
00204 //       ________________          
00205 //      / ____________  /          +---------+
00206 //     / /           / /           | +-----+ |
00207 //    / /   core    / /   ---->    | |     |_|___orthonormal_cell
00208 //   / /___________/ /    <----    | |     | |
00209 //  /_______________/              | +-----+ |___rim
00210 //                                 +---------+
00211 //
00212 // Alternatively one can specify a rectangular bounding box into which the atoms
00213 // shall be wrapped. It is specified in form of minmax coordinates through
00214 // parameter *box.
00215 
00216 // TODO:
00217 // * Allow requesting specific neighbor cells.
00218 
00219 int measure_pbc_neighbors(MoleculeList *mlist, AtomSel *sel, int molid,
00220                           int frame, const Matrix4 *alignment,
00221                           const float *center, const float *cutoff, const float *box,
00222                           ResizeArray<float> *extcoord_array,
00223                           ResizeArray<int> *indexmap_array) {
00224   int orig_ts, max_ts;
00225   if (!box && !cutoff[0] && !cutoff[1] && !cutoff[2]) return MEASURE_NOERR;
00226 
00227   Molecule *mol = mlist->mol_from_id(molid);
00228   if( !mol )
00229     return MEASURE_ERR_NOMOLECULE;
00230  
00231   // get current frame number and make sure there are frames
00232   if((orig_ts = mol->frame()) < 0)
00233     return MEASURE_ERR_NOFRAMES;
00234   
00235   // get the max frame number and determine current frame
00236   max_ts = mol->numframes()-1;
00237   if (frame==-2)  frame = orig_ts;
00238   else if (frame>max_ts || frame==-1) frame = max_ts;
00239 
00240   Timestep *ts = mol->get_frame(frame);
00241   if (!ts) return MEASURE_ERR_NOMOLECULE;
00242 
00243   // Get the displacement vectors (in form of translation matrices)
00244   Matrix4 Tpbc[3][2];
00245   ts->get_transforms(Tpbc[0][1], Tpbc[1][1], Tpbc[2][1]);
00246 
00247   // Assign the negative cell translation vectors
00248   Tpbc[0][0] = Tpbc[0][1];
00249   Tpbc[1][0] = Tpbc[1][1];
00250   Tpbc[2][0] = Tpbc[2][1];
00251   Tpbc[0][0].inverse();
00252   Tpbc[1][0].inverse();
00253   Tpbc[2][0].inverse();
00254 
00255   // Construct the cell spanning vectors
00256   float cell[9];
00257   cell[0] = Tpbc[0][1].mat[12];
00258   cell[1] = Tpbc[0][1].mat[13];
00259   cell[2] = Tpbc[0][1].mat[14];
00260   cell[3] = Tpbc[1][1].mat[12];
00261   cell[4] = Tpbc[1][1].mat[13];
00262   cell[5] = Tpbc[1][1].mat[14];
00263   cell[6] = Tpbc[2][1].mat[12];
00264   cell[7] = Tpbc[2][1].mat[13];
00265   cell[8] = Tpbc[2][1].mat[14];
00266 
00267   float len[3];
00268   len[0] = sqrtf(dot_prod(&cell[0], &cell[0]));
00269   len[1] = sqrtf(dot_prod(&cell[3], &cell[3]));
00270   len[2] = sqrtf(dot_prod(&cell[6], &cell[6]));
00271   //printf("len={%.3f %.3f %.3f}\n", len[0], len[1], len[2]);
00272 
00273   int i;
00274   float minlen = len[0];
00275   if (len[1] && len[1]<minlen) minlen = len[1];
00276   if (len[2] && len[2]<minlen) minlen = len[2];
00277   minlen--;
00278 
00279   // The algorithm works only for atoms in adjacent neighbor cells.
00280   if (!box && (cutoff[0]>=len[0] || cutoff[1]>=len[1] || cutoff[2]>=len[2])) {
00281     return MEASURE_ERR_BADCUTOFF;
00282   }
00283 
00284   bool bigrim = 1;
00285   float corecell[9];
00286   float diag[3];
00287   float origin[3];
00288   memset(origin, 0, 3*sizeof(float));
00289   Matrix4 M_norm;
00290 
00291   if (box) {
00292     // Get the matrix M_norm that transforms all atoms inside the 
00293     // unit cell into the normalized unitcell spanned by 
00294     // {1/len[0] 0 0} {0 1/len[1] 0} {0 0 1/len[2]}.
00295     bigrim = 1;
00296 
00297     float vtmp[3];
00298     vec_add(vtmp, &cell[0], &cell[3]);
00299     vec_add(diag, &cell[6], vtmp);
00300     //printf("diag={%.3f %.3f %.3f}\n", diag[0], diag[1], diag[2]);
00301 
00302     // Finally we need to apply the translation of the cell origin
00303     vec_copy(origin, center);
00304     vec_scaled_add(origin, -0.5, &cell[0]);
00305     vec_scaled_add(origin, -0.5, &cell[3]);
00306     vec_scaled_add(origin, -0.5, &cell[6]);
00307     vec_negate(origin, origin);
00308     //printf("origin={%.3f %.3f %.3f}\n", origin[0], origin[1], origin[2]);
00309 
00310   } else if (2.0f*cutoff[0]<minlen && 2.0f*cutoff[1]<minlen && 2.0f*cutoff[2]<minlen) {
00311     // The cutoff must not be larger than half of the smallest cell dimension
00312     // otherwise we would have to use a less efficient algorithm.
00313 
00314     // Get the matrix M_norm that transforms all atoms inside the 
00315     // corecell into the orthonormal unitcell spanned by {1 0 0} {0 1 0} {0 0 1}.
00316     // The corecell ist the pbc cell minus cutoffs for each dimension.
00317     vec_scale(&corecell[0], (len[0]-cutoff[0])/len[0], &cell[0]);
00318     vec_scale(&corecell[3], (len[1]-cutoff[1])/len[1], &cell[3]);
00319     vec_scale(&corecell[6], (len[2]-cutoff[2])/len[2], &cell[6]);
00320     get_transform_to_orthonormal_cell(corecell, center, M_norm);
00321     //printf("Using algorithm for small PBC environment.\n");
00322 
00323   } else {
00324     // Get the matrix M_norm that transforms all atoms inside the 
00325     // unit cell into the orthonormal unitcell spanned by {1 0 0} {0 1 0} {0 0 1}.
00326     get_transform_to_orthonormal_cell(cell, center, M_norm);
00327 
00328     bigrim = 1;
00329     //printf("Using algorithm for large PBC environment.\n");
00330   }
00331 
00332   // In case the molecule was aligned our pbc cell is rotated and shifted.
00333   // In corder to transform a point P into the orthonormal cell (P') it 
00334   // first has to be unaligned (the inverse of the alignment):
00335   // P' = M_norm * (alignment^-1) * P
00336   Matrix4 alignmentinv(*alignment);
00337   alignmentinv.inverse();
00338   Matrix4 M_coretransform(M_norm);
00339   M_coretransform.multmatrix(alignmentinv);
00340 
00341   //printf("alignment = \n");
00342   //print_Matrix4(alignment);
00343 
00344   // Similarly if we want to transform a point P into its image P' we
00345   // first have to unalign it, then apply the PBC translation and 
00346   // finally realign:
00347   // P' = alignment * Tpbc * (alignment^-1) * P
00348   //      `-------------v--------------'
00349   //                transform
00350   int j, u;
00351   Matrix4 Tpbc_aligned[3][2];
00352   if (!box) {
00353     for (i=0; i<3; i++) {
00354       for (j=0; j<2; j++) {
00355         Tpbc_aligned[i][j].loadmatrix(*alignment);
00356         Tpbc_aligned[i][j].multmatrix(Tpbc[i][j]);
00357         Tpbc_aligned[i][j].multmatrix(alignmentinv);
00358       }
00359     }
00360   }
00361 
00362   Matrix4 M[3];
00363   float *coords = ts->pos;
00364   float *coor;
00365   float orthcoor[3], wrapcoor[3];
00366 
00367   //printf("cutoff={%.3f %.3f %.3f}\n", cutoff[0], cutoff[1], cutoff[2]);
00368 
00369   if (box) {
00370     float min_coord[3], max_coord[3];
00371     // Increase box by cutoff
00372     vec_sub(min_coord, box,   cutoff);
00373     vec_add(max_coord, box+3, cutoff);
00374     //printf("Wrapping atoms into rectangular bounding box.\n");
00375     //printf("min_coord={%.3f %.3f %.3f}\n", min_coord[0], min_coord[1], min_coord[2]);
00376     //printf("max_coord={%.3f %.3f %.3f}\n", max_coord[0], max_coord[1], max_coord[2]);
00377     vec_add(min_coord, min_coord, origin);
00378     vec_add(max_coord, max_coord, origin);
00379 
00380     float testcoor[9];
00381     int idx, k;
00382     // Loop over all atoms
00383     for (idx=0; idx<ts->num; idx++) { 
00384       coor = coords+3*idx;
00385 
00386       // Apply the inverse alignment transformation
00387       // to the current test point.
00388       M_coretransform.multpoint3d(coor, orthcoor);
00389 
00390       // Loop over all 26 neighbor cells
00391       // x
00392       for (i=-1; i<=1; i++) {
00393         // Choose the direction of translation
00394         if      (i>0) M[0].loadmatrix(Tpbc[0][1]);
00395         else if (i<0) M[0].loadmatrix(Tpbc[0][0]);
00396         else          M[0].identity();
00397         // Translate the unaligned atom
00398         M[0].multpoint3d(orthcoor, testcoor);
00399 
00400         // y
00401         for (j=-1; j<=1; j++) {
00402           // Choose the direction of translation
00403           if      (j>0) M[1].loadmatrix(Tpbc[1][1]);
00404           else if (j<0) M[1].loadmatrix(Tpbc[1][0]);
00405           else          M[1].identity();
00406           // Translate the unaligned atom
00407           M[1].multpoint3d(testcoor, testcoor+3);
00408 
00409           // z
00410           for (k=-1; k<=1; k++) {
00411             if(i==0 && j==0 && k==0) continue;
00412 
00413             // Choose the direction of translation
00414             if      (k>0) M[2].loadmatrix(Tpbc[2][1]);
00415             else if (k<0) M[2].loadmatrix(Tpbc[2][0]);
00416             else          M[2].identity();
00417             // Translate the unaligned atom
00418             M[2].multpoint3d(testcoor+3, testcoor+6);
00419 
00420             // Realign atom
00421             alignment->multpoint3d(testcoor+6, wrapcoor);
00422 
00423             vec_add(testcoor+6, wrapcoor, origin);
00424             if (testcoor[6]<min_coord[0] || testcoor[6]>max_coord[0]) continue;
00425             if (testcoor[7]<min_coord[1] || testcoor[7]>max_coord[1]) continue;
00426             if (testcoor[8]<min_coord[2] || testcoor[8]>max_coord[2]) continue;
00427 
00428             // Atom is inside cutoff, add it to the list            
00429             for (int n=0; n<3; n++) extcoord_array->append(wrapcoor[n]);
00430             indexmap_array->append(idx);
00431           }
00432         }
00433       }
00434     }
00435 
00436   } else if (bigrim) {
00437     // This is the more general but slower algorithm.
00438     // We loop over all atoms, move each atom to all 26 neighbor cells
00439     // and check if it lies inside cutoff
00440     float min_coord[3], max_coord[3];
00441     min_coord[0] = -cutoff[0]/len[0];
00442     min_coord[1] = -cutoff[1]/len[1];
00443     min_coord[2] = -cutoff[2]/len[2];
00444     max_coord[0] = 1.0f + cutoff[0]/len[0];
00445     max_coord[1] = 1.0f + cutoff[1]/len[1];
00446     max_coord[2] = 1.0f + cutoff[2]/len[2];
00447 
00448     float testcoor[3];
00449     int idx, k;
00450     // Loop over all atoms
00451     for (idx=0; idx<ts->num; idx++) { 
00452       coor = coords+3*idx;
00453 
00454       // Apply the PBC --> orthonormal unitcell transformation
00455       // to the current test point.
00456       M_coretransform.multpoint3d(coor, orthcoor);
00457 
00458       // Loop over all 26 neighbor cells
00459       // x
00460       for (i=-1; i<=1; i++) {
00461         testcoor[0] = orthcoor[0]+(float)(i);
00462         if (testcoor[0]<min_coord[0] || testcoor[0]>max_coord[0]) continue;
00463 
00464         // Choose the direction of translation
00465         if      (i>0) M[0].loadmatrix(Tpbc_aligned[0][1]);
00466         else if (i<0) M[0].loadmatrix(Tpbc_aligned[0][0]);
00467         else          M[0].identity();
00468 
00469         // y
00470         for (j=-1; j<=1; j++) {
00471           testcoor[1] = orthcoor[1]+(float)(j);
00472           if (testcoor[1]<min_coord[1] || testcoor[1]>max_coord[1]) continue;
00473 
00474           // Choose the direction of translation
00475           if      (j>0) M[1].loadmatrix(Tpbc_aligned[1][1]);
00476           else if (j<0) M[1].loadmatrix(Tpbc_aligned[1][0]);
00477           else          M[1].identity();
00478 
00479           // z
00480           for (k=-1; k<=1; k++) {
00481             testcoor[2] = orthcoor[2]+(float)(k);
00482             if (testcoor[2]<min_coord[2] || testcoor[2]>max_coord[2]) continue;
00483 
00484             if(i==0 && j==0 && k==0) continue;
00485 
00486             // Choose the direction of translation
00487             if      (k>0) M[2].loadmatrix(Tpbc_aligned[2][1]);
00488             else if (k<0) M[2].loadmatrix(Tpbc_aligned[2][0]);
00489             else          M[2].identity();
00490 
00491             M[0].multpoint3d(coor, wrapcoor);
00492             M[1].multpoint3d(wrapcoor, wrapcoor);
00493             M[2].multpoint3d(wrapcoor, wrapcoor);
00494 
00495             // Atom is inside cutoff, add it to the list            
00496             for (int n=0; n<3; n++) extcoord_array->append(wrapcoor[n]);
00497             indexmap_array->append(idx);
00498           }
00499         }
00500       }
00501     }
00502   
00503   } else {
00504     Matrix4 Mtmp;
00505 
00506     for (i=0; i < ts->num; i++) { 
00507       // Apply the PBC --> orthonormal unitcell transformation
00508       // to the current test point.
00509       M_coretransform.multpoint3d(coords+3*i, orthcoor);
00510 
00511       // Determine in which cell we are.
00512       int cellindex[3];    
00513       if      (orthcoor[0]<0) cellindex[0] = -1;
00514       else if (orthcoor[0]>1) cellindex[0] =  1;
00515       else                    cellindex[0] =  0;
00516       if      (orthcoor[1]<0) cellindex[1] = -1;
00517       else if (orthcoor[1]>1) cellindex[1] =  1;
00518       else                    cellindex[1] =  0;
00519       if      (orthcoor[2]<0) cellindex[2] = -1;
00520       else if (orthcoor[2]>1) cellindex[2] =  1;
00521       else                    cellindex[2] =  0;
00522 
00523       // All zero means we're inside the core --> no image.
00524       if (!cellindex[0] && !cellindex[1] && !cellindex[2]) continue;
00525 
00526       // Choose the direction of translation
00527       if      (orthcoor[0]<0) M[0].loadmatrix(Tpbc_aligned[0][1]);
00528       else if (orthcoor[0]>1) M[0].loadmatrix(Tpbc_aligned[0][0]);
00529       if      (orthcoor[1]<0) M[1].loadmatrix(Tpbc_aligned[1][1]);
00530       else if (orthcoor[1]>1) M[1].loadmatrix(Tpbc_aligned[1][0]);
00531       if      (orthcoor[2]<0) M[2].loadmatrix(Tpbc_aligned[2][1]);
00532       else if (orthcoor[2]>1) M[2].loadmatrix(Tpbc_aligned[2][0]);
00533 
00534       // Create wrapped copies of the atom:
00535       // x, y, z planes
00536       coor = coords+3*i;
00537       for (u=0; u<3; u++) {
00538         if (cellindex[u] && cutoff[u]) {
00539           M[u].multpoint3d(coor, wrapcoor);
00540           for (j=0; j<3; j++) extcoord_array->append(wrapcoor[j]);
00541           indexmap_array->append(i);
00542         }
00543       }
00544 
00545       Mtmp = M[0];
00546 
00547       // xy edge
00548       if (cellindex[0] && cellindex[1] && cutoff[0] && cutoff[1]) {
00549         M[0].multmatrix(M[1]);
00550         M[0].multpoint3d(coor, wrapcoor);
00551         for (j=0; j<3; j++) extcoord_array->append(wrapcoor[j]);
00552         indexmap_array->append(i);
00553       }
00554 
00555       // yz edge
00556       if (cellindex[1] && cellindex[2] && cutoff[1] && cutoff[2]) {
00557         M[1].multmatrix(M[2]);
00558         M[1].multpoint3d(coor, wrapcoor);
00559         for (j=0; j<3; j++) extcoord_array->append(wrapcoor[j]);
00560         indexmap_array->append(i);
00561       }
00562 
00563       // zx edge
00564       if (cellindex[0] && cellindex[2] && cutoff[0] && cutoff[2]) {
00565         M[2].multmatrix(Mtmp);
00566         M[2].multpoint3d(coor, wrapcoor);
00567         for (j=0; j<3; j++) extcoord_array->append(wrapcoor[j]);
00568         indexmap_array->append(i);
00569       }
00570 
00571       // xyz corner
00572       if (cellindex[0] && cellindex[1] && cellindex[2]) {
00573         M[1].multmatrix(Mtmp);
00574         M[1].multpoint3d(coor, wrapcoor);
00575         for (j=0; j<3; j++) extcoord_array->append(wrapcoor[j]);
00576         indexmap_array->append(i);
00577       }
00578 
00579     }
00580 
00581   } // endif
00582 
00583   // If a selection was provided we select extcoords
00584   // within cutoff of the original selection:
00585   if (sel) {
00586     int numext = sel->selected+indexmap_array->num();
00587     float *extcoords = new float[3*numext];
00588     int   *indexmap  = new int[numext];
00589     int   *others    = new int[numext];
00590     memset(others, 0, numext);
00591 
00592     // Use the largest given cutoff
00593     float maxcutoff = cutoff[0];
00594     for (i=1; i<3; i++) {
00595       if (cutoff[i]>maxcutoff) maxcutoff = cutoff[i];
00596     }
00597 
00598     // Prepare C-array of coordinates for find_within()
00599     j=0;
00600     for (i=0; i < sel->num_atoms; i++) { 
00601       if (!sel->on[i]) continue; //atom is not selected
00602       extcoords[3*j]   = coords[3*i];
00603       extcoords[3*j+1] = coords[3*i+1];
00604       extcoords[3*j+2] = coords[3*i+2];
00605       indexmap[j] = i;
00606       others[j++] = 1;
00607     }
00608     for (i=0; i<indexmap_array->num(); i++) {
00609       extcoords[3*j]   = (*extcoord_array)[3*i];
00610       extcoords[3*j+1] = (*extcoord_array)[3*i+1];
00611       extcoords[3*j+2] = (*extcoord_array)[3*i+2];
00612       indexmap[j] = (*indexmap_array)[i];
00613       others[j++] = 0;
00614     }
00615 
00616     // Initialize flags array to true, find_within() results are AND'd/OR'd in.
00617     int *flgs   = new int[numext];
00618     for (i=0; i<numext; i++) {
00619       flgs[i] = 1;
00620     }
00621 
00622     // Find coordinates from extcoords that are within cutoff of the ones
00623     // with flagged in 'others' and set the flgs accordingly:
00624     find_within(extcoords, flgs, others, numext, maxcutoff);
00625 
00626     extcoord_array->clear();
00627     indexmap_array->clear();
00628     for (i=sel->selected; i<numext; i++) {
00629       if (!flgs[i]) continue;
00630 
00631       extcoord_array->append(extcoords[3*i]);
00632       extcoord_array->append(extcoords[3*i+1]);
00633       extcoord_array->append(extcoords[3*i+2]);
00634       indexmap_array->append(indexmap[i]);
00635     }
00636 
00637   }
00638 
00639   return MEASURE_NOERR;
00640 }  
00641 
00642 // Computes the rectangular bounding box for the PBC cell.
00643 // If the molecule was rotated/moved you can supply the transformation
00644 // matrix and you'll get the bounding box of the transformed cell.
00645 int compute_pbcminmax(MoleculeList *mlist, int molid, const float *center,
00646                       const Matrix4 *transform, float *min, float *max) {
00647   Molecule *mol = mlist->mol_from_id(molid);
00648   if( !mol )
00649     return MEASURE_ERR_NOMOLECULE;
00650 
00651   Timestep *ts = mol->get_frame(0);
00652   if (!ts) return MEASURE_ERR_NOFRAMES;
00653 
00654   // Get the displacement vectors (in form of translation matrices)
00655   Matrix4 Tpbc[3];
00656   ts->get_transforms(Tpbc[0], Tpbc[1], Tpbc[2]);
00657 
00658   // Construct the cell spanning vectors
00659   float cell[9];
00660   cell[0] = Tpbc[0].mat[12];
00661   cell[1] = Tpbc[0].mat[13];
00662   cell[2] = Tpbc[0].mat[14];
00663   cell[3] = Tpbc[1].mat[12];
00664   cell[4] = Tpbc[1].mat[13];
00665   cell[5] = Tpbc[1].mat[14];
00666   cell[6] = Tpbc[2].mat[12];
00667   cell[7] = Tpbc[2].mat[13];
00668   cell[8] = Tpbc[2].mat[14];
00669 
00670   float len[3];
00671   len[0] = sqrtf(dot_prod(&cell[0], &cell[0]));
00672   len[1] = sqrtf(dot_prod(&cell[3], &cell[3]));
00673   len[2] = sqrtf(dot_prod(&cell[6], &cell[6]));
00674 
00675   // Finally we need to apply the translation of the origin
00676   float node[8*3];
00677   int n=0;
00678   float i, j, k;
00679   for (i=-0.5; i<1; i+=1.0) {
00680     for (j=-0.5; j<1; j+=1.0) {
00681       for (k=-0.5; k<1; k+=1.0) {
00682         vec_copy(node+3*n, center);
00683         vec_scaled_add(node+3*n, i, &cell[0]);
00684         vec_scaled_add(node+3*n, j, &cell[3]);
00685         vec_scaled_add(node+3*n, k, &cell[6]);
00686 
00687         // Apply global alignment transformation
00688         transform->multpoint3d(node+3*n, node+3*n);
00689         n++;
00690       }
00691     }
00692   }
00693 
00694   for (n=0; n<8; n++) {
00695     if (!n || node[3*n]   < min[0]) min[0] = node[3*n];
00696     if (!n || node[3*n+1] < min[1]) min[1] = node[3*n+1];
00697     if (!n || node[3*n+2] < min[2]) min[2] = node[3*n+2];
00698     if (!n || node[3*n]   > max[0]) max[0] = node[3*n];
00699     if (!n || node[3*n+1] > max[1]) max[1] = node[3*n+1];
00700     if (!n || node[3*n+2] > max[2]) max[2] = node[3*n+2];
00701    }
00702 
00703   return MEASURE_NOERR;
00704 }

Generated on Thu Sep 4 01:27:07 2008 for VMD (current) by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002