Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members

atom_algorithms.hpp

Go to the documentation of this file.
00001 // -*- mode: c++; -*-
00002 //
00003 //The Biomolecule Toolkit (BTK) is a C++ library for use in the
00004 //modeling, analysis, and design of biological macromolecules.
00005 //Copyright (C) 2001, Tim Robertson <kid50@users.sourceforge.net>
00006 //
00007 //This program is free software; you can redistribute it and/or modify
00008 //it under the terms of the GNU Lesser General Public License as published
00009 //by the Free Software Foundation; either version 2.1 of the License, or (at
00010 //your option) any later version.
00011 //
00012 //This program is distributed in the hope that it will be useful,  but
00013 //WITHOUT ANY WARRANTY; without even the implied warranty of
00014 //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015 //Lesser General Public License for more details. 
00016 //
00017 //You should have received a copy of the GNU Lesser General Public License 
00018 //along with this program; if not, write to the Free Software Foundation, 
00019 //Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00020 
00024 
00025 
00026 #include <cmath>
00027 
00028 #include <iterator>
00029 
00030 namespace BTK{
00031   namespace Internal{
00032 
00033     template <typename AtomIterator1,typename AtomIterator2>
00034     void
00035     setup_rmsd(AtomIterator1 first1,
00036                AtomIterator1 last1,
00037                AtomIterator2 first2,
00038                AtomIterator2 last2,
00039                BTKVector& T1,
00040                BTKVector& T2,
00041                BTKMatrix& R,
00042                double& Eo,
00043                unsigned& N);
00044   }
00045 }
00046 
00047 
00048 template <typename AtomIterator>
00049 BTK::BTKVector 
00050 BTK::
00051 center_of_mass(AtomIterator first,
00052                AtomIterator last)
00053 {
00054   typedef typename std::iterator_traits<AtomIterator>::value_type InputT;
00055   boost::function_requires<boost::ConvertibleConcept<InputT,Atom> >();
00056 
00057   BTKVector COM = new_vector();
00058   for(AtomIterator i=first;i!=last;++i){ COM += i->position(); }
00059   
00060   return COM/std::distance(first,last);
00061 }
00062 
00063 
00064 template <typename AtomIterator>
00065 void 
00066 BTK::
00067 center(AtomIterator first,
00068        AtomIterator last) 
00069 {
00070   BTKVector COM(center_of_mass(first,last));
00071 
00072   translate(first,last,-COM);
00073 }
00074 
00075 
00076 template <typename AtomIterator1,typename AtomIterator2>
00077 void 
00078 BTK::
00079 align_centers(AtomIterator1 fixed_first,
00080               AtomIterator1 fixed_last,
00081               AtomIterator2 align_first,
00082               AtomIterator2 align_last)
00083 {
00084   BTKVector fixedCOM(center_of_mass(fixed_first,fixed_last));
00085   BTKVector COM(center_of_mass(align_first,align_last));
00086   translate(align_first,align_last,fixedCOM-COM);
00087 }
00088 
00089 
00090 
00091 template <typename AtomIterator>
00092 void 
00093 BTK::
00094 translate(AtomIterator first,
00095           AtomIterator last,
00096           const BTKVector& T) 
00097 {
00098   typedef typename std::iterator_traits<AtomIterator>::value_type InputT;
00099   boost::function_requires<boost::ConvertibleConcept<InputT,Atom> >();
00100 
00101   for (;first!=last;++first) {
00102     first->set_position(first->position()+T);
00103   }
00104 }
00105 
00106 
00107 template <typename AtomIterator>
00108 void 
00109 BTK::
00110 rotate(AtomIterator first,
00111        AtomIterator last,
00112        double phi,
00113        double theta,
00114        double psi,
00115        const BTKVector& fixed_point)
00116 {
00117   BTKMatrix R(create_rotation_matrix(phi,theta,psi));
00118   BTKVector T(prec_prod(R,-fixed_point)+fixed_point);  
00119 
00120   for(; first != last; ++first) {
00121     first->set_position(prec_prod(R,first->position())+T);
00122   }
00123 }
00124 
00125 
00126 template <typename AtomIterator>
00127 void 
00128 BTK::
00129 rotate(AtomIterator first,
00130        AtomIterator last,
00131        const BTKVector& axis,
00132        double theta,
00133        const BTKVector& fixed_point)
00134 {
00135   BTKMatrix R(create_rotation_matrix(axis,theta));
00136   BTKVector T(prec_prod(R,-fixed_point)+fixed_point);  
00137 
00138   for(; first != last; ++first) {
00139     first->set_position(prec_prod(R,first->position())+T);
00140   }
00141 }
00142 
00143 
00144 template <typename AtomIterator>
00145 void 
00146 BTK::
00147 rotate(AtomIterator first,
00148        AtomIterator last,
00149        const BTKMatrix& R)
00150 {
00151   for (; first != last; ++first) {
00152     first->set_position(prec_prod(R,first->position()));
00153   }
00154 }
00155 
00156 
00157 template <typename AtomIterator>
00158 void
00159 BTK::
00160 transform(AtomIterator first,
00161           AtomIterator last,
00162           const BTKMatrix& R,
00163           const BTKVector& T)
00164 {
00165   for(;first!=last;++first){
00166     first->set_position(prec_prod(R,first->position())+T);
00167   }
00168 }
00169 
00170 
00171 
00172 template <typename AtomIterator>
00173 void 
00174 BTK::
00175 principal_axes(AtomIterator first,
00176                AtomIterator last,
00177                std::vector<EigenState>& axes,
00178                BTKVector& COM)
00179 {
00180   COM = center_of_mass(first,last);
00181 
00182   BTKMatrix covar(3,3);
00183   covar.clear();
00184 
00185   for(AtomIterator i = first; i != last; ++i){
00186     BTKVector tmp(i->position() - COM);
00187     covar += outer_prod(tmp,tmp);
00188   }
00189 
00190   axes = diagonalize_symmetric(covar);
00191 }
00192 
00193 
00194 template <typename AtomIterator1, typename AtomIterator2>
00195 void 
00196 BTK::
00197 Internal::
00198 setup_rmsd(AtomIterator1 first1,
00199            AtomIterator1 last1,
00200            AtomIterator2 first2,
00201            AtomIterator2 last2,
00202            BTKVector& T1,
00203            BTKVector& T2,
00204            BTKMatrix& R,
00205            double& Eo,
00206            unsigned& N) 
00207 {
00208   // Get T1, the transform from the moved set COM to the origin
00209   //   & T2, the transform from the origin to the fixed set COM
00210   T1 *= 0;
00211   T2 *= 0;
00212   AtomIterator1 i1(first1);
00213   AtomIterator2 i2(first2);
00214 
00215   for (N = 0;
00216        i1 != last1 && i2 != last2;
00217        ++i1,++i2) {
00218     T1 += i2->position();
00219     T2 += i1->position();
00220     N++;
00221   }
00222 
00223   if (i2 != last2 || i1 != last1) {
00224     std::cerr << "Warning: attempting to compute RMSD between "
00225         << "structures of different lengths! " << std::endl
00226         << "Assuming atom length of smaller structure (" << N << ")...." 
00227         << std::endl;
00228   }
00229 
00230   T1 /= -1.*static_cast<double>(N);
00231   T2 /= static_cast<double>(N);
00232 
00234   // Get Eo and R
00235   int i = 0;
00236   BTKMatrix tmp1(3,N),tmp2(3,N);
00237   for (i1 = first1, i2 = first2;
00238        i1 != last1 && i2 != last2;
00239        ++i1,++i2) {
00240     column(tmp1,i) = i1->position() - T2; 
00241     column(tmp2,i) = i2->position() + T1;
00242     i++;
00243   }
00244 
00245   Eo = 0.;
00246   for (unsigned j = 0; j < 3; ++j) {
00247     Eo += inner_prod(row(tmp1,j),row(tmp1,j)) + inner_prod(row(tmp2,j),row(tmp2,j));
00248     for (unsigned k = 0; k < 3; ++k) {
00249       R(j,k) = inner_prod(row(tmp1,j),row(tmp2,k));
00250     }
00251   }
00252   Eo /= 2.;
00253 }
00254 
00255 
00256 
00257 // Fast calculation of rmsd w/o calculating a rotation matrix.
00258 //
00259 //   Chris Saunders 11/2002 - Fast rmsd calculation by the method of 
00260 // Kabsch 1978, where the required eigenvalues are found by an 
00261 // analytical, rather than iterative, method to save time. 
00262 // The cubic factorization used to accomplish this only produces 
00263 // stable eigenvalues for the transpose(R)*R matrix of a typical 
00264 // protein after the whole matrix has been normalized. Note that 
00265 // the normalization process used here is completely empirical 
00266 // and that, at the present time, there are **no checks** or 
00267 // warnings on the quality of the (potentially unstable) cubic 
00268 // factorization. 
00269 //
00270 template <typename AtomIterator1, typename AtomIterator2>
00271 double 
00272 BTK::
00273 get_rmsd(AtomIterator1 a1_first,
00274          AtomIterator1 a1_last,
00275          AtomIterator2 a2_first,
00276          AtomIterator2 a2_last)
00277 {
00278   double Eo;
00279   BTKMatrix R(3,3);
00280   unsigned N;
00281   BTKVector T1(3),T2(3);
00282   Internal::setup_rmsd(a1_first,a1_last,a2_first,a2_last,T1,T2,R,Eo,N);
00283 
00284   // check if the determinant is greater than 0
00285   double omega = (inner_prod(row(R,0),cross(row(R,1),row(R,2))) > 0 ? 1 : -1);
00286 
00287   //  get elements we need from trans(R)*R 
00288   //   (funky matrix naming relic of first attempt using pivots)
00289   //          matrix = d0 e0 f0
00290   //                      d1 e1
00291   //                         d2
00292   double d0,d1,d2,e0,e1,f0;
00293 
00294   // divide matrix by d0, so that cubic root algorithm can handle it
00295   d0 = R(0,0)*R(0,0) + R(1,0)*R(1,0) + R(2,0)*R(2,0);
00296   d1 = (R(0,1)*R(0,1) + R(1,1)*R(1,1) + R(2,1)*R(2,1))/d0;
00297   d2 = (R(0,2)*R(0,2) + R(1,2)*R(1,2) + R(2,2)*R(2,2))/d0;
00298 
00299   e0 = (R(0,0)*R(0,1) + R(1,0)*R(1,1) + R(2,0)*R(2,1))/d0;
00300   e1 = (R(0,1)*R(0,2) + R(1,1)*R(1,2) + R(2,1)*R(2,2))/d0;
00301 
00302   f0 = (R(0,0)*R(0,2) + R(1,0)*R(1,2) + R(2,0)*R(2,2))/d0;
00303 
00304   // cubic roots
00305   double r1,r2,r3;
00306   {
00307     // solving for eigenvalues as det(A-I*lambda) = 0
00308     // yeilds the values below corresponding to:
00309     // lambda**3 + B*lambda**2 + C*lambda + D = 0
00310     //   (given that d0=1.)
00311     //
00312     double B = -1-d1-d2;
00313     double C = d1+d2+d1*d2-e0*e0-f0*f0-e1*e1;
00314     double D = e0*e0*d2+e1*e1+f0*f0*d1-d1*d2-2*e0*f0*e1;
00315 
00316     // cubic root method of Viete w/ all the safety belts off
00317     double q = (B*B-3.0*C)/9.0;
00318     double q3 = q*q*q;
00319     double r = (2.0*B*B*B-9.0*B*C+27.0*D)/54.0;
00320     double theta = acos(r/sqrt(q3));
00321     r1 = r2 = r3 = -2.0*sqrt(q);
00322     r1 *= cos(theta/3.0);
00323     r2 *= cos((theta+2*PI)/3.0);
00324     r3 *= cos((theta-2*PI)/3.0);
00325     r1 -= B/3.0;
00326     r2 -= B/3.0;
00327     r3 -= B/3.0;
00328   }
00329 
00330   // undo the d0 norm to get eigenvalues
00331   r1 = r1*d0;
00332   r2 = r2*d0;
00333   r3 = r3*d0;
00334 
00335   // set rlow to lowest eigenval; set other two to r1,r2 
00336   double rlow;
00337   if(r3<r1 && r3<r2){
00338     rlow = r3;
00339   } else if(r2<r1 && r2<r3){
00340     rlow = r2; r2=r3;
00341   } else { 
00342     rlow = r1; r1=r3;
00343   }
00344   double rmsd = (2./static_cast<double>(N))*
00345                 (Eo - std::sqrt(r1) - std::sqrt(r2) - omega*std::sqrt(rlow));
00346 
00347   double const sqrt_min = 1e-8;
00348   if(rmsd > sqrt_min) { rmsd = std::sqrt(rmsd); }
00349   else                { rmsd = 0; }
00350 
00351   return rmsd;
00352 }
00353 
00354 
00355 template <typename AtomIterator1, typename AtomIterator2>
00356 double 
00357 BTK::
00358 get_trivial_rmsd(AtomIterator1 a1_first,
00359                  AtomIterator1 a1_last,
00360                  AtomIterator2 a2_first,
00361                  AtomIterator2 a2_last)
00362 {
00363   double rmsd2 = 0.;
00364   unsigned N = 0;
00365     
00366   AtomIterator1 a1 = a1_first;
00367   AtomIterator2 a2 = a2_first;
00368   for(;a1!=a1_last && a2!=a2_last;++a1,++a2){
00369     BTKVector v(a1->position()-a2->position());
00370     rmsd2 += inner_prod(v,v);
00371     N++;
00372   }
00373   if( a1!=a1_last || a2!=a2_last){
00374     std::cerr << "Warning: attempting to compute RMSD between "
00375               << "structures of different lengths! " << std::endl
00376               << "Assuming atom length of smaller structure (" << N << ")...."
00377               << std::endl;
00378   }
00379 
00380   if(N) rmsd2 /= static_cast<double>(N);
00381 
00382   double const sqrt_min = 1e-8;
00383   if(rmsd2 > sqrt_min) { rmsd2 = std::sqrt(rmsd2); }
00384   else                 { rmsd2 = 0; }
00385 
00386   return rmsd2;
00387 }
00388 
00389 
00390 
00391 
00392 template <typename AtomIterator1, typename AtomIterator2>
00393 void 
00394 BTK::
00395 leastsquares_superposition(AtomIterator1 fixed_first,
00396                            AtomIterator1 fixed_last, 
00397                            AtomIterator2 superimpose_first,
00398                            AtomIterator2 superimpose_last,
00399                            BTKMatrix& U,
00400                            BTKVector& T,
00401                            double& rmsd)
00402 {
00403   double Eo;
00404   BTKMatrix R(3,3);
00405   BTKVector T1(3);
00406   BTKVector T2(3);
00407   unsigned N;
00408   Internal::setup_rmsd(fixed_first,fixed_last,superimpose_first,superimpose_last,T1,T2,R,Eo,N);
00409   
00411   // 1) construct the rotation matrix
00412   
00413   // Diagonalize transpose(R)*R
00414   std::vector<EigenState> ES = diagonalize_symmetric(prec_prod(trans(R),R));
00415 
00416   // sort from highest to lowest eigenvalue
00417   std::sort(ES.rbegin(),ES.rend());
00418   
00419 
00420   // Set eigenvector 2 = 0 x 1 to guarantee a r.h. system
00421   ES[2].eigenvector = cross(ES[0].eigenvector,ES[1].eigenvector);
00422 
00423   double mu0,mu1,mu2;
00424 
00425   mu0 = std::sqrt(ES[0].eigenvalue);
00426   mu1 = std::sqrt(ES[1].eigenvalue);
00427   mu2 = std::sqrt(ES[2].eigenvalue);
00428 
00429   BTKVector b0(3),b1(3),b2(3);
00430 
00431   b0 = prec_prod(R,ES[0].eigenvector/mu0);
00432   b1 = prec_prod(R,ES[1].eigenvector/mu1);
00433   b2 = cross(b0,b1);
00434 
00435   // store omega to distinguish reflection from rotation for rmsd calc
00436   double omega = (inner_prod(b2,(prec_prod(R,ES[2].eigenvector))) < 0 ? -1 : 1);
00437 
00438   U.clear();
00439   for (unsigned i = 0; i < 3; ++i) {
00440     for (unsigned j = 0; j < 3; ++j) {
00441       U(i,j) = (b0[i]*ES[0].eigenvector[j]) + 
00442                (b1[i]*ES[1].eigenvector[j]) +
00443                (b2[i]*ES[2].eigenvector[j]);
00444     }
00445   }
00446 
00447   T = prec_prod(U,T1)+T2;
00448     
00449   // Eo-sum(muX) is 1/2 the sum of squared differences between atoms. 
00450   // To get rmsd, multiply this by 2 and divide by the number of points, 
00451   // then take the square root.
00452   rmsd = (2./static_cast<double>(N))*(Eo - mu0 - mu1 - omega*mu2);
00453   // added a check for the smallest value that can safely be sqrt'ed
00454   double const sqrt_min = 1.e-8;
00455   if(rmsd > sqrt_min) { rmsd = std::sqrt(rmsd); }
00456   else                { rmsd = 0; }
00457 
00458 
00459 #ifndef NDEBUG
00460 
00461   // crap code for verification below here
00462   
00463   // check rot and trans
00464   //
00465   double rmsd2 = 0.;
00466   AtomIterator1 fi;
00467   AtomIterator2 mi;
00468   for(fi = fixed_first,mi = superimpose_first;
00469       fi != fixed_last && mi != superimpose_last;
00470       fi++,mi++){
00471     BTKVector foo(3);
00472     foo = fi->position()-(prec_prod(U,mi->position())+T);
00473     rmsd2 += inner_prod(foo,foo);  // = length of vector squared
00474   }
00475   rmsd2 = std::sqrt(rmsd2/static_cast<double>(N));
00476 
00477   std::cerr << "fastrmsd: " << rmsd 
00478             << " superimposed directrmsd: " << rmsd2 << std::endl;
00479 #endif
00480 
00481 }
00482 
00483 
00484 template <typename AtomIterator1, typename AtomIterator2, typename AtomIterator3>
00485 void
00486 BTK::
00487 leastsquares_superposition(AtomIterator1 fixed_first,
00488                            AtomIterator1 fixed_last, 
00489                            AtomIterator2 superimpose_first,
00490                            AtomIterator2 superimpose_last,
00491                            AtomIterator3 transform_first,
00492                            AtomIterator3 transform_last,
00493                            double& rmsd) 
00494 {
00495   BTKVector T(3);
00496   BTKMatrix U(3,3);
00497   leastsquares_superposition(fixed_first,fixed_last,
00498                               superimpose_first,superimpose_last,
00499                               U,T,rmsd);
00500 
00501   for(AtomIterator3 ai=transform_first; ai != transform_last; ++ai){
00502     ai->set_position(prec_prod(U,ai->position())+T);
00503   }
00504 }
00505 
00506 
00507 
00508 

Generated on Wed Apr 14 00:43:16 2004 for BTK by doxygen 1.3.6