⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rmsd.c

📁 RMSD: Root Mean Square Deviation 是一种在分子模拟及预测中很常见的评价标准
💻 C
字号:
/* *  **************************************************************************
 *
 *  rmsd.c  *  (c) 2005 Bosco K Ho *  *  Implementation of the Kabsch algorithm to find the least-squares *  rotation matrix for a superposition between two sets of vectors. *  The jacobi transform of the diagonalization of a symmetric matrix *  is taken from Numerical Recipes. This piece of code is  *  self-contained and *does not require* another library. * *  **************************************************************************
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published
 *  by the Free Software Foundation; either version 2.1 of the License, or (at
 *  your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,  but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details. 
 *  
 *  You should have received a copy of the GNU Lesser General Public License 
 *  along with this program; if not, write to the Free Software Foundation, 
 *  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *  **************************************************************************
 *  */ #include <stdio.h>#include <math.h>#include "rmsd.h"/* vector functions using c arrays */void normalize(double a[3]){  double  b;  b = sqrt((double)(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]));  a[0] /= b;  a[1] /= b;  a[2] /= b;}double dot(double a[3], double b[3]){  return (a[0] * b[0] + a[1] * b[1] + a[2] * b[2]);}static void cross(double a[3], double b[3], double c[3]){  a[0] = b[1]*c[2] - b[2]*c[1];  a[1] = b[2]*c[0] - b[0]*c[2];  a[2] = b[0]*c[1] - b[1]*c[0];}/* * setup_rotation()  * *      given two lists of x,y,z coordinates, constructs * the correlation R matrix and the E value needed to calculate the * least-squares rotation matrix. */void setup_rotation(double ref_xlist[][3],                    double mov_xlist[][3],                     int n_list,                    double mov_com[3],                    double mov_to_ref[3],                    double R[3][3],                    double* E0){  int i, j, n;  double ref_com[3];  /* calculate the centre of mass */  for (i=0; i<3; i++)  {     mov_com[i] = 0.0;    ref_com[i] = 0.0;  }    for (n=0; n<n_list; n++)     for (i=0; i<3; i++)    {       mov_com[i] += mov_xlist[n][i];      ref_com[i] += ref_xlist[n][i];    }      for (i=0; i<3; i++)  {    mov_com[i] /= n_list;    ref_com[i] /= n_list;    mov_to_ref[i] = ref_com[i] - mov_com[i];  }  /* shift mov_xlist and ref_xlist to centre of mass */  for (n=0; n<n_list; n++)     for (i=0; i<3; i++)    {       mov_xlist[n][i] -= mov_com[i];      ref_xlist[n][i] -= ref_com[i];    }  /* initialize */  for (i=0; i<3; i++)    for (j=0; j<3; j++)       R[i][j] = 0.0;  *E0 = 0.0;  for (n=0; n<n_list; n++)   {    /*      * E0 = 1/2 * sum(over n): y(n)*y(n) + x(n)*x(n)      */    for (i=0; i<3; i++)      *E0 +=  mov_xlist[n][i] * mov_xlist[n][i]              + ref_xlist[n][i] * ref_xlist[n][i];        /*     * correlation matrix R:        *   R[i,j) = sum(over n): y(n,i) * x(n,j)       *   where x(n) and y(n) are two vector sets        */    for (i=0; i<3; i++)    {      for (j=0; j<3; j++)        R[i][j] += mov_xlist[n][i] * ref_xlist[n][j];    }  }  *E0 *= 0.5;  }#define ROTATE(a,i,j,k,l) { g = a[i][j]; \                            h = a[k][l]; \                            a[i][j] = g-s*(h+g*tau); \
                            a[k][l] = h+s*(g-h*tau); }/*    * jacobi3 * *    computes eigenval and eigen_vec of a real 3x3 * symmetric matrix. On output, elements of a that are above  * the diagonal are destroyed. d[1..3] returns the  * eigenval of a. v[1..3][1..3] is a matrix whose  * columns contain, on output, the normalized eigen_vec of
 * a. n_rot returns the number of Jacobi rotations that were required. */int jacobi3(double a[3][3], double d[3], double v[3][3], int* n_rot){  int count, k, i, j;  double tresh, theta, tau, t, sum, s, h, g, c, b[3], z[3];  /*Initialize v to the identity matrix.*/
  for (i=0; i<3; i++)   {     for (j=0; j<3; j++)       v[i][j] = 0.0;
    v[i][i] = 1.0;
  }
  /* Initialize b and d to the diagonal of a */  for (i=0; i<3; i++)     b[i] = d[i] = a[i][i];
  /* z will accumulate terms */  for (i=0; i<3; i++)     z[i] = 0.0;     *n_rot = 0;  /* 50 tries */  for (count=0; count<50; count++)       {    /* sum off-diagonal elements */    sum = 0.0;    for (i=0; i<2; i++)     {      for (j=i+1; j<3; j++)         sum += fabs(a[i][j]);    }    /* if converged to machine underflow */    if (sum == 0.0)       return(1);    /* on 1st three sweeps... */    if (count < 3)       tresh = sum * 0.2 / 9.0;        else             tresh = 0.0;          for (i=0; i<2; i++)     {      for (j=i+1; j<3; j++)       {        g = 100.0 * fabs(a[i][j]);        /*  after four sweeps, skip the rotation if         *   the off-diagonal element is small          */        if ( count > 3  &&  fabs(d[i])+g == fabs(d[i])              &&  fabs(d[j])+g == fabs(d[j]) )         {          a[i][j] = 0.0;        }         else if (fabs(a[i][j]) > tresh)         {          h = d[j] - d[i];                    if (fabs(h)+g == fabs(h))          {            t = a[i][j] / h;          }          else           {            theta = 0.5 * h / (a[i][j]);            t = 1.0 / ( fabs(theta) +                        (double)sqrt(1.0 + theta*theta) );            if (theta < 0.0)               t = -t;          }                    c = 1.0 / (double) sqrt(1 + t*t);          s = t * c;          tau = s / (1.0 + c);          h = t * a[i][j];          z[i] -= h;          z[j] += h;          d[i] -= h;          d[j] += h;          a[i][j] = 0.0;          for (k=0; k<=i-1; k++)             ROTATE(a, k, i, k, j)          for (k=i+1; k<=j-1; k++)             ROTATE(a, i, k, k, j)          for (k=j+1; k<3; k++)             ROTATE(a, i, k, j, k)          for (k=0; k<3; k++)             ROTATE(v, k, i, k, j)          ++(*n_rot);        }      }    }    for (i=0; i<3; i++)     {      b[i] += z[i];      d[i] = b[i];      z[i] = 0.0;    }  }  printf("Too many iterations in jacobi3\n");  return (0);}  /*  * diagonalize_symmetric  * *    Diagonalize a 3x3 matrix & sort eigenval by size */int diagonalize_symmetric(double matrix[3][3],                           double eigen_vec[3][3],                           double eigenval[3]){  int n_rot, i, j, k;  double vec[3][3];  double val;     if (!jacobi3(matrix, eigenval, vec, &n_rot))   {    printf("convergence failed\n");    return (0);  }  /* sort solutions by eigenval */  for (i=0; i<3; i++)   {
    k = i;    val = eigenval[i];        for (j=i+1; j<3; j++)
      if (eigenval[j] >= val)      {         k = j;        val = eigenval[k];
      }           if (k != i)     {
      eigenval[k] = eigenval[i];
      eigenval[i] = val;
      for (j=0; j<3; j++)       {
        val = vec[j][i];
        vec[j][i] = vec[j][k];
        vec[j][k] = val;
      }
    }
  }
  /* transpose such that first index refers to solution index */  for (i=0; i<3; i++)    for (j=0; j<3; j++)      eigen_vec[i][j] = vec[j][i];  return (1);}/* * calculate_rotation_matrix()  * *   calculates the rotation matrix U and the * rmsd from the R matrix and E0: */int calculate_rotation_matrix(double R[3][3],                              double U[3][3],                               double E0,                              double* residual){  int i, j, k;  double Rt[3][3], RtR[3][3];  double left_eigenvec[3][3], right_eigenvec[3][3], eigenval[3];  double v[3];  double sigma;  /* build Rt, transpose of R  */  for (i=0; i<3; i++)    for (j=0; j<3; j++)      Rt[i][j] = R[j][i];  /* make symmetric RtR = Rt X R */  for (i=0; i<3; i++)     for (j=0; j<3; j++)    {      RtR[i][j] = 0.0;      for (k = 0; k<3; k++)        RtR[i][j] += Rt[k][i] * R[j][k];    }  if (!diagonalize_symmetric(RtR, right_eigenvec, eigenval))    return(0);  /* right_eigenvec's should be an orthogonal system but could be left   * or right-handed. Let's force into right-handed system.   */  cross(&right_eigenvec[2][0], &right_eigenvec[0][0], &right_eigenvec[1][0]);  /* From the Kabsch algorithm, the eigenvec's of RtR   * are identical to the right_eigenvec's of R.   * This means that left_eigenvec = R x right_eigenvec    */  for (i=0; i<3; i++)     for (j=0; j<3; j++)       left_eigenvec[i][j] = dot(&right_eigenvec[i][0], &Rt[j][0]);  for (i=0; i<3; i++)     normalize(&left_eigenvec[i][0]);  /*    * Force left_eigenvec[2] to be orthogonal to the other vectors.   * First check if the rotational matrices generated from the    * orthogonal eigenvectors are in a right-handed or left-handed   * co-ordinate system - given by sigma. Sigma is needed to   * resolve this ambiguity in calculating the RMSD.   */  cross(v, &left_eigenvec[0][0], &left_eigenvec[1][0]);  if (dot(v, &left_eigenvec[2][0]) < 0.0)    sigma = -1.0;  else     sigma = 1.0;  for (i=0; i<3; i++)    left_eigenvec[2][i] = v[i];   /* calc optimal rotation matrix U that minimises residual */  for (i=0;i<3; i++)    for (j=0; j<3; j++)     {      U[i][j] = 0.0;      for (k=0; k<3; k++)        U[i][j] += left_eigenvec[k][i] * right_eigenvec[k][j];    }      *residual = E0 - (double) sqrt(fabs(eigenval[0]))                  - (double) sqrt(fabs(eigenval[1]))                 - sigma * (double) sqrt(fabs(eigenval[2]));  return (1);}void calculate_rotation_rmsd(double ref_xlist[][3],                             double mov_xlist[][3],                              int n_list,                             double mov_com[3],                             double mov_to_ref[3],                             double U[3][3],                             double* rmsd){  double Eo, residual;  double R[3][3];    setup_rotation(ref_xlist, mov_xlist, n_list,                  mov_com, mov_to_ref, R, &Eo);  calculate_rotation_matrix(R, U, Eo, &residual);    residual = fabs(residual); /* avoids the awkward case of -0.0 */  *rmsd = sqrt( fabs((double) (residual)*2.0/((double)n_list)) ); }  /* * Fast calculation of rmsd w/o calculating a rotation matrix. * *   Chris Saunders 11/2002 - Fast rmsd calculation by the method of  * Kabsch 1978, where the required eigenvalues are found by an  * analytical, rather than iterative, method to save time.  * The cubic factorization used to accomplish this only produces  * stable eigenvalues for the transpose(R]*R matrix of a typical  * protein after the whole matrix has been normalized. Note that  * the normalization process used here is completely empirical  * and that, at the present time, there are **no checks** or  * warnings on the quality of the (potentially unstable) cubic  * factorization.  * */#define PI 3.14159265358979323846void fast_rmsd(double ref_xlist[][3],               double mov_xlist[][3],                int n_list,               double* rmsd){   double R[3][3];  double d0,d1,d2,e0,e1,f0;  double omega;  double mov_com[3];  double mov_to_ref[3];  /* cubic roots */  double r1,r2,r3;  double rlow;    double v[3];  double Eo, residual;      setup_rotation(ref_xlist, mov_xlist, n_list,                  mov_com, mov_to_ref, R, &Eo);    /*    * check if the determinant is greater than 0 to   * see if R produces a right-handed or left-handed   * co-ordinate system.   */  cross(v, &R[1][0], &R[2][0]);  if (dot(&R[0][0], v) > 0.0)    omega = 1.0;  else    omega = -1.0;  /*   * get elements we need from tran(R) x R    *  (funky matrix naming relic of first attempt using pivots)   *          matrix = d0 e0 f0   *                      d1 e1   *                         d2   * divide matrix by d0, so that cubic root algorithm can handle it    */     d0 =  R[0][0]*R[0][0] + R[1][0]*R[1][0] + R[2][0]*R[2][0];  d1 = (R[0][1]*R[0][1] + R[1][1]*R[1][1] + R[2][1]*R[2][1])/d0;  d2 = (R[0][2]*R[0][2] + R[1][2]*R[1][2] + R[2][2]*R[2][2])/d0;  e0 = (R[0][0]*R[0][1] + R[1][0]*R[1][1] + R[2][0]*R[2][1])/d0;  e1 = (R[0][1]*R[0][2] + R[1][1]*R[1][2] + R[2][1]*R[2][2])/d0;  f0 = (R[0][0]*R[0][2] + R[1][0]*R[1][2] + R[2][0]*R[2][2])/d0;  /* cubic roots */  {    double B, C, D, q, q3, r, theta;    /*     * solving for eigenvalues as det(A-I*lambda) = 0     * yeilds the values below corresponding to:     * lambda**3 + B*lambda**2 + C*lambda + D = 0     *   (given that d0=1.)     */    B = -1.0 - d1 - d2;    C = d1 + d2 + d1*d2 - e0*e0 - f0*f0 - e1*e1;    D = e0*e0*d2 + e1*e1 + f0*f0*d1 - d1*d2 - 2*e0*f0*e1;    /* cubic root method of Viete with all safety belts off */    q = (B*B - 3.0*C) / 9.0;    q3 = q*q*q;    r = (2.0*B*B*B - 9.0*B*C + 27.0*D) / 54.0;    theta = acos(r/sqrt(q3));    r1 = r2 = r3 = -2.0*sqrt(q);    r1 *= cos(theta/3.0);    r2 *= cos((theta + 2.0*PI) / 3.0);    r3 *= cos((theta - 2.0*PI) / 3.0);    r1 -= B / 3.0;    r2 -= B / 3.0;    r3 -= B / 3.0;  }  /* undo the d0 norm to get eigenvalues */  r1 = r1*d0;  r2 = r2*d0;  r3 = r3*d0;  /* set rlow to lowest eigenval; set other two to r1,r2 */  if (r3<r1 && r3<r2)  {    rlow = r3;  }  else if (r2<r1 && r2<r3)  {    rlow = r2;     r2 = r3;  }   else   {     rlow = r1;     r1 = r3;  }  residual = Eo - sqrt(r1) - sqrt(r2) - omega*sqrt(rlow);  *rmsd = sqrt( (double) residual*2.0 / ((double) n_list) ); }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -