00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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
00209
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
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
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
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
00285 double omega = (inner_prod(row(R,0),cross(row(R,1),row(R,2))) > 0 ? 1 : -1);
00286
00287
00288
00289
00290
00291
00292 double d0,d1,d2,e0,e1,f0;
00293
00294
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
00305 double r1,r2,r3;
00306 {
00307
00308
00309
00310
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
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
00331 r1 = r1*d0;
00332 r2 = r2*d0;
00333 r3 = r3*d0;
00334
00335
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
00412
00413
00414 std::vector<EigenState> ES = diagonalize_symmetric(prec_prod(trans(R),R));
00415
00416
00417 std::sort(ES.rbegin(),ES.rend());
00418
00419
00420
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
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
00450
00451
00452 rmsd = (2./static_cast<double>(N))*(Eo - mu0 - mu1 - omega*mu2);
00453
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
00462
00463
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);
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