LCOV - code coverage report
Current view: top level - colvar - RMSD.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 49 50 98.0 %
Date: 2026-11-05 10:19:04 Functions: 3 4 75.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #include "Colvar.h"
      23             : #include "core/PlumedMain.h"
      24             : #include "core/ActionRegister.h"
      25             : #include "tools/RMSD.h"
      26             : #include "tools/PDB.h"
      27             : 
      28             : namespace PLMD {
      29             : namespace colvar {
      30             : 
      31             : class RMSD : public Colvar {
      32             : 
      33             :   bool squared;
      34             :   bool nopbc;
      35             :   PLMD::RMSD myrmsd;
      36             :   std::vector<Vector> der;
      37             : public:
      38             :   explicit RMSD(const ActionOptions&);
      39             :   void calculate() override;
      40             :   static void registerKeywords(Keywords& keys);
      41             : };
      42             : 
      43             : //+PLUMEDOC DCOLVAR RMSD
      44             : /*
      45             : Calculate the RMSD with respect to a reference structure.
      46             : 
      47             : One colvar that has been shown to be very successful in studying protein folding is the distance between the instantaneous configuration
      48             : and a reference configuration - often the structure of the folded state.  When the free energy of a protein is shown as a function
      49             : of this collective variable there is a minima for low values of the CV, which is due to the folded state of the protein.  There is
      50             : then a second minima at higher values of the CV, which is the minima corresponding to the unfolded state.  The aim of this colvar is
      51             : thus to calculate something like:
      52             : 
      53             : $$
      54             : d(X,X') = \vert X-X' \vert
      55             : $$
      56             : 
      57             : where $X$ is the instantaneous position of all the atoms in the system and
      58             : $X'$ is the positions of the atoms in some reference structure that is provided as input.
      59             : $d(X,X')$ thus measures the distance all the atoms have moved away from this reference configuration.
      60             : Oftentimes, it is only the internal motions of the structure - i.e. not the translations of the center of
      61             : mass or the rotations of the reference frame - that are interesting.  Hence, when calculating
      62             : the root-mean-square deviation between the atoms in two configurations
      63             : you must first superimpose the two structures in some way. At present PLUMED provides two distinct ways
      64             : of performing this superposition.  The first method is applied when you use TYPE=SIMPLE in the input
      65             : line.  This instruction tells PLUMED that the root mean square deviation is to be calculated after the
      66             : positions of the geometric centers in the reference and instantaneous configurations are aligned.  In
      67             : other words $d(X,x')$ is to be calculated using:
      68             : 
      69             : $$
      70             : d(X,X') = \sqrt{ \sum_i \sum_\alpha^{x,y,z}  \frac{w_i}{\sum_j w_j} ( X _{i,\alpha}-com _\alpha(X)-{X'} _{i,\alpha}+com _\alpha(X') )^2 }
      71             : $$
      72             : 
      73             : with
      74             : 
      75             : $$
      76             : com_\alpha (X) = \sum_i \frac{w'_{i}}{\sum_j w'_j} X _{i,\alpha}
      77             : $$
      78             : 
      79             : and
      80             : 
      81             : $$
      82             : com_\alpha(X')= \sum_i  \frac{w'_{i}}{\sum_j w'_j}X' _{i,\alpha}
      83             : $$
      84             : 
      85             : Obviously, $com_\alpha(X)$ and  $com_\alpha(X')$  represent the positions of the center of mass in the reference
      86             : and instantaneous configurations if the weights $w'$ are set equal to the atomic masses.  If the weights are all set equal to
      87             : one, however, $com_\alpha(X)$ and  $com_\alpha(X')$ are the positions of the geometric centers.
      88             : 
      89             : An example input that can be used to calculate and print this RMSD distance is shown below:
      90             : 
      91             : ```plumed
      92             : #SETTINGS INPUTFILES=regtest/basic/rt19/test0.pdb
      93             : rmsd0: RMSD TYPE=SIMPLE REFERENCE=regtest/basic/rt19/test0.pdb
      94             : PRINT ARG=rmsd0 FILE=colvar
      95             : ```
      96             : 
      97             : Notice that there are sets of weights: $w'$ and $w$ in the formulas above. The first of these weights is used to calculate the position of the center of mass
      98             : (so it determines how the atoms are _aligned_).  The second set of weights, $w$ is used when calculating how far the atoms have been
      99             : _displaced_. These weights are assigned in the reference configuration that you provide as input (i.e. the appear in the input file
     100             : to this action that you set using REFERENCE=whatever.pdb). As you can see in the input above, this input consists of a simple pdb file
     101             : containing the set of atoms for which you want to calculate the RMSD displacement and their positions in the reference configuration.
     102             : It is important to note that the indices in this pdb need to be set correctly.  The indices in this file determine the indices of the
     103             : instantaneous atomic positions that are used by PLUMED when calculating this colvar.  As such if you want to calculate the RMSD distance
     104             : moved by the first, fourth, sixth and twenty eighth atoms in the MD codes input file then the indices of the corresponding reference positions in this pdb
     105             : file should be set equal to 1, 4, 6 and 28.
     106             : 
     107             : The pdb input file should also contain the weights $w$ and $w'$. These second of these sets of weights, $w'$, appears in the OCCUPANCY column (the first column after the coordinates).
     108             : These are the weights that are used to calculate the position of the center of mass.  The BETA column (the second column
     109             : after the Cartesian coordinates) is used to provide the $w$ values which are used in the calculation of the displacement.
     110             : Please note that it is possible to use fractional values for beta and for the occupancy. However, we recommend you only do this when
     111             : you really know what you are doing however as the results can be rather strange.  A more common practise is to use different sets of atoms
     112             : for the alignment and the displacement.  You can do this by setting the $w$ and $w'$ values for all the atoms you wish to use for alignment only equal to zero and
     113             : one respectively and by setting the $w$ and $w'$ values for all the atoms you wish to use for displacement only to one and zero respectively.
     114             : 
     115             : In the PDB input files that you use for RMSD the atomic coordinates and box lengths should be in Angstroms unless
     116             : you are working with natural units.  If you are working with natural units then the coordinates
     117             : should be in your natural length unit.  You can find more details on the PDB file format [here](http://www.wwpdb.org/docs.html).
     118             : Please make sure your PDB file is correctly formatted.  More detail on the format for PDB files can be found in the documentation for the [PDB2CONSTANT](PDB2CONSTANT.md) action.
     119             : 
     120             : The following input uses a different method to calculate the RMSD distance as you can tell from the TYPE=OPTIMAL on the input line.  In addition, because we have added the
     121             : SQURED flag on the input line, we are calculating $d(X,X')^2$ rather than $d(X,X')$.  Calculating $d(X,X')^2$ is slightly less computationally expensive than computing $d(X,X')$ as
     122             : you avoid a square root operation.
     123             : 
     124             : ```plumed
     125             : #SETTINGS INPUTFILES=regtest/basic/rt19/test0.pdb
     126             : rmsd0: RMSD TYPE=OPTIMAL SQUARED REFERENCE=regtest/basic/rt19/test0.pdb
     127             : PRINT ARG=rmsd0 FILE=colvar
     128             : ```
     129             : 
     130             : In this case  the root mean square deviation is calculated after the positions of geometric centers in the reference and instantaneous configurations are aligned AND after
     131             : an optimal alignment of the two frames is performed so that motion due to rotation of the reference frame between the two structures is
     132             : removed.  The equation for $d(X,X')$ in this case is:
     133             : 
     134             : $$
     135             : d(X,X') = \sqrt{ \sum_i \sum_\alpha^{x,y,z}  \frac{w_i}{\sum_j w_j}[ X_{i,\alpha}-com_\alpha(X)- \sum_\beta M(X,X',w')_{\alpha,\beta}({X'}_{i,\beta}-com_\beta(X')) ]^2 }
     136             : $$
     137             : 
     138             : where $M(X,X',w')$ is the optimal alignment matrix which is calculated using the Kearsley algorithm that is described in the paper from the bibliography below.  Again different sets of
     139             : weights are used for the alignment ($w'$) and for the displacement calculations ($w$).
     140             : This gives a great deal of flexibility as it allows you to use a different sets of atoms (which may or may not overlap) for the alignment and displacement
     141             : parts of the calculation. This may be very useful when you want to calculate how a ligand moves about in a protein cavity as you can use the protein as a reference
     142             : system and do no alignment of the ligand.
     143             : 
     144             : (Note: when this form of RMSD is used to calculate the secondary structure variables ([ALPHARMSD](ALPHARMSD.md), [ANTIBETARMSD](ANTIBETARMSD.md) and [PARABETARMSD](PARABETARMSD.md)
     145             : all the atoms in the segment are assumed to be part of both the alignment and displacement sets and all weights are set equal to one)
     146             : 
     147             : ## Numerical derivatives
     148             : 
     149             : If you are calculating the RMSD distance from a single RMSD reference frame you calculate numerical derivatives of the RMSD distance as shown below for the simple metric:
     150             : 
     151             : ```plumed
     152             : #SETTINGS INPUTFILES=regtest/basic/rt19/test0.pdb
     153             : rmsd0: RMSD TYPE=SIMPLE REFERENCE=regtest/basic/rt19/test0.pdb
     154             : rmsd0n: RMSD TYPE=SIMPLE NUMERICAL_DERIVATIVES REFERENCE=regtest/basic/rt19/test0.pdb
     155             : DUMPDERIVATIVES ARG=rmsd0,rmsd0n FILE=deriv
     156             : ```
     157             : 
     158             : and as shown below for the OPTIMAL metric:
     159             : 
     160             : ```plumed
     161             : #SETTINGS INPUTFILES=regtest/basic/rt19/test0.pdb
     162             : rmsd0: RMSD TYPE=OPTIMAL REFERENCE=regtest/basic/rt19/test0.pdb
     163             : rmsd0n: RMSD TYPE=OPTIMAL NUMERICAL_DERIVATIVES REFERENCE=regtest/basic/rt19/test0.pdb
     164             : DUMPDERIVATIVES ARG=rmsd0,rmsd0n FILE=deriv
     165             : ```
     166             : 
     167             : In practice there is no reason to use the NUMERICAL_DERIVATIVES option unless you are testing the RMSD implementation in the way the above two inputs are doing by printing
     168             : a file that allows us to compare the analytic and numerical derivatives.  Evaluating the derivtives numerically is __much__ more expensive than calculating them analytically.
     169             : 
     170             : ## Computing RMSD displacements
     171             : 
     172             : The $d(X,X')$ values that are calculated when you use the TYPE=SIMPLE and TYPE=OPTIMAL variants of RMSD are scalars. These scalars tell you the length of the vector of displacements,
     173             : $X - X'$, between the instantaneous and reference positions.  If you would like to access this vector of displacements instead of its length you can by using the DISPLACEMENT keyword
     174             : as shown below for TYPE=SIMPLE
     175             : 
     176             : ```plumed
     177             : #SETTINGS INPUTFILES=regtest/basic/rt19/test0.pdb
     178             : rmsd0: RMSD TYPE=SIMPLE DISPLACEMENT REFERENCE=regtest/basic/rt19/test0.pdb
     179             : PRINT ARG=rmsd0.disp,rmsd0.dist FILE=colvar
     180             : ```
     181             : 
     182             : or as shown below for TYPE=OPTIMAL
     183             : 
     184             : ```plumed
     185             : #SETTINGS INPUTFILES=regtest/basic/rt19/test0.pdb
     186             : rmsd0: RMSD TYPE=OPTIMAL DISPLACEMENT REFERENCE=regtest/basic/rt19/test0.pdb
     187             : PRINT ARG=rmsd0.disp,rmsd0.dist FILE=colvar
     188             : ```
     189             : 
     190             : The RMSD command for these inputs output two components
     191             : 
     192             : - `dist` - the length of displacement vector that is output when you don't use the DISPLACEMENT keyword
     193             : - `disp` - the 3N dimensional vector of atomic dispacements, where N is the number of atoms.
     194             : 
     195             : These vectors of displacements are used if you use the [PCAVARS](PCAVARS.md) action to compute the projection of the displacement on a particular reference vector.
     196             : 
     197             : ## Computing multiple RMSD values
     198             : 
     199             : You can also define multiple reference configurations in the reference input as is done in the following example:
     200             : 
     201             : ```plumed
     202             : #SETTINGS INPUTFILES=regtest/mapping/rt39/all1.pdb
     203             : rmsd: RMSD TYPE=OPTIMAL REFERENCE=regtest/mapping/rt39/all1.pdb
     204             : PRINT ARG=rmsd FILE=colvar
     205             : ```
     206             : 
     207             : The output from RMSD in this case is a vector that contains the RMSD distances from each of the reference configurations in your input file.  This feature is used in the
     208             : [PATH](PATH.md) shortcut.  Furthermore, you can use the DISPLACEMENT keyword when there are multiple reference configurations in the input file as shown below:
     209             : 
     210             : ```plumed
     211             : #SETTINGS INPUTFILES=regtest/mapping/rt39/all1.pdb
     212             : rmsd: RMSD TYPE=OPTIMAL DISPLACEMENT REFERENCE=regtest/mapping/rt39/all1.pdb
     213             : PRINT ARG=rmsd.disp,rmsd.dist FILE=colvar
     214             : ```
     215             : 
     216             : The RMSD command here still outputs the `dist` and `disp` components but now `dist` is a vector and `disp` is a matrix.  This type of command is used to implement
     217             : the [GEOMETRIC_PATH](GEOMETRIC_PATH.md) shortcut.  For this command you need information on the distances from a set of reference configurations that can be found
     218             : in the `dist` component as well as the information on the displacement vectors between the instantaneous position and each of the reference configurations that is contained in the `dist` matrix.
     219             : 
     220             : Please note that there are a number of other methods for calculating the distance between the instantaneous configuration and a reference configuration that are available in plumed.
     221             : 
     222             : ## A note on periodic boundary conditions
     223             : 
     224             : When periodic boundary conditions are used, the atoms should be
     225             : in the proper periodic image. This has been done automatically since PLUMED 2.5,
     226             : by considering the ordered list of atoms and rebuilding molecules using a procedure
     227             : that is equivalent to that done in [WHOLEMOLECULES](WHOLEMOLECULES.md). Notice that
     228             : rebuilding is local to this action. This is different from [WHOLEMOLECULES](WHOLEMOLECULES.md)
     229             : which actually modifies the coordinates stored in PLUMED.
     230             : 
     231             : In case you want to recover the old behavior you should use the NOPBC flag as shown below:
     232             : 
     233             : ```plumed
     234             : #SETTINGS INPUTFILES=regtest/basic/rt19/test0.pdb
     235             : rmsd0: RMSD TYPE=SIMPLE NOPBC REFERENCE=regtest/basic/rt19/test0.pdb
     236             : PRINT ARG=rmsd0 FILE=colvar
     237             : ```
     238             : 
     239             : In that case you need to take care that atoms are in the correct
     240             : periodic image.
     241             : 
     242             : ## A final thought
     243             : 
     244             : Notice that there are many other ways of calculating the distance from a particular reference structure. These normally work by computing the square root of the sum of the squares
     245             : of the differences in collective variable values. To compute these distances you use the functionality in the [refdist](module_refdist.md) module.
     246             : 
     247             : */
     248             : //+ENDPLUMEDOC
     249             : 
     250             : PLUMED_REGISTER_ACTION(RMSD,"RMSD_SCALAR")
     251             : 
     252         169 : void RMSD::registerKeywords(Keywords& keys) {
     253         169 :   Colvar::registerKeywords(keys);
     254         338 :   keys.setDisplayName("RMSD");
     255         169 :   keys.add("compulsory","REFERENCE","a file in pdb format containing the reference structure and the atoms involved in the CV.");
     256         169 :   keys.add("compulsory","TYPE","SIMPLE","the manner in which RMSD alignment is performed.  Should be OPTIMAL or SIMPLE.");
     257         169 :   keys.addFlag("SQUARED",false," This should be set if you want mean squared displacement instead of RMSD ");
     258         338 :   keys.setValueDescription("scalar","the RMSD between the instantaneous structure and the reference structure that was input");
     259         169 : }
     260             : 
     261          83 : RMSD::RMSD(const ActionOptions&ao):
     262             :   PLUMED_COLVAR_INIT(ao),
     263          83 :   squared(false),
     264          83 :   nopbc(false) {
     265             :   std::string reference;
     266         167 :   parse("REFERENCE",reference);
     267             :   std::string type;
     268          83 :   type.assign("SIMPLE");
     269          83 :   parse("TYPE",type);
     270          83 :   parseFlag("SQUARED",squared);
     271          83 :   parseFlag("NOPBC",nopbc);
     272          83 :   checkRead();
     273             : 
     274          84 :   addValueWithDerivatives();
     275          83 :   setNotPeriodic();
     276          83 :   PDB pdb;
     277             : 
     278             :   // read everything in ang and transform to nm if we are not in natural units
     279          83 :   if( !pdb.read(reference,usingNaturalUnits(),0.1/getUnits().getLength()) ) {
     280           0 :     error("missing input file " + reference );
     281             :   }
     282          83 :   myrmsd.set( pdb, type, true, true );
     283             : 
     284          83 :   std::vector<AtomNumber> atoms( pdb.getAtomNumbers() );
     285          83 :   requestAtoms( atoms );
     286          82 :   der.resize( atoms.size() );
     287             : 
     288          82 :   log.printf("  reference from file %s\n",reference.c_str());
     289          82 :   log.printf("  which contains %d atoms\n",getNumberOfAtoms());
     290          82 :   log.printf("  with indices : ");
     291        3996 :   for(unsigned i=0; i<atoms.size(); ++i) {
     292        3914 :     if(i%25==0) {
     293         189 :       log<<"\n";
     294             :     }
     295        3914 :     log.printf("%d ",atoms[i].serial());
     296             :   }
     297          82 :   log.printf("\n");
     298          82 :   log.printf("  method for alignment : %s \n",type.c_str() );
     299          82 :   if(squared) {
     300          11 :     log.printf("  chosen to use SQUARED option for MSD instead of RMSD\n");
     301             :   }
     302          82 :   if(nopbc) {
     303          61 :     log.printf("  without periodic boundary conditions\n");
     304             :   } else {
     305          21 :     log.printf("  using periodic boundary conditions\n");
     306             :   }
     307         170 : }
     308             : 
     309             : 
     310             : // calculator
     311       40521 : void RMSD::calculate() {
     312       40521 :   if(!nopbc) {
     313        3959 :     makeWhole();
     314             :   }
     315       40521 :   double r=myrmsd.calculate( getPositions(), der, squared );
     316             : 
     317       40521 :   setValue(r);
     318    14618160 :   for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     319    14577639 :     setAtomsDerivatives( i, der[i] );
     320             :   }
     321       40521 :   setBoxDerivativesNoPbc();
     322       40521 : }
     323             : 
     324             : }
     325             : }
     326             : 
     327             : 
     328             : 

Generated by: LCOV version 1.16