LCOV - code coverage report
Current view: top level - colvar - DRMSD.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 98 101 97.0 %
Date: 2024-10-18 13:59:31 Functions: 2 3 66.7 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2013-2024 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 "core/ActionShortcut.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "core/PlumedMain.h"
      25             : #include "core/ActionSet.h"
      26             : #include "core/Group.h"
      27             : #include "tools/PDB.h"
      28             : 
      29             : namespace PLMD {
      30             : namespace colvar {
      31             : 
      32             : //+PLUMEDOC DCOLVAR DRMSD
      33             : /*
      34             : Calculate the distance RMSD with respect to a reference structure.
      35             : 
      36             : To calculate the root-mean-square deviation between the atoms in two configurations
      37             : you must first superimpose the two structures in some ways.  Obviously, it is the internal vibrational
      38             : motions of the structure - i.e. not the translations and rotations - that are interesting. However,
      39             : aligning two structures by removing the translational and rotational motions is not easy.  Furthermore,
      40             : in some cases there can be alignment issues caused by so-called frame-fitting problems. It is thus
      41             : often cheaper and easier to calculate the distances between all the pairs of atoms.  The distance
      42             : between the two structures, \f$\mathbf{X}^a\f$ and \f$\mathbf{X}^b\f$ can then be measured as:
      43             : 
      44             : \f[
      45             : d(\mathbf{X}^A, \mathbf{X}^B) = \sqrt{\frac{1}{N(N-1)} \sum_{i \ne j} [ d(\mathbf{x}_i^a,\mathbf{x}_j^a) - d(\mathbf{x}_i^b,\mathbf{x}_j^b) ]^2}
      46             : \f]
      47             : 
      48             : where \f$N\f$ is the number of atoms and \f$d(\mathbf{x}_i,\mathbf{x}_j)\f$ represents the distance between
      49             : atoms \f$i\f$ and \f$j\f$.  Clearly, this representation of the configuration is invariant to translation and rotation.
      50             : However, it can become expensive to calculate when the number of atoms is large.  This can be resolved
      51             : within the DRMSD colvar by setting LOWER_CUTOFF and UPPER_CUTOFF.  These keywords ensure that only
      52             : pairs of atoms that are within a certain range are incorporated into the above sum.
      53             : 
      54             : In PDB files the atomic coordinates and box lengths should be in Angstroms unless
      55             : you are working with natural units.  If you are working with natural units then the coordinates
      56             : should be in your natural length unit.  For more details on the PDB file format visit http://www.wwpdb.org/docs.html
      57             : 
      58             : \par Examples
      59             : 
      60             : The following tells plumed to calculate the distance RMSD between
      61             : the positions of the atoms in the reference file and their instantaneous
      62             : position. Only pairs of atoms whose distance in the reference structure is within
      63             : 0.1 and 0.8 nm are considered.
      64             : 
      65             : \plumedfile
      66             : DRMSD REFERENCE=file1.pdb LOWER_CUTOFF=0.1 UPPER_CUTOFF=0.8
      67             : \endplumedfile
      68             : 
      69             : The reference file is a PDB file that looks like this
      70             : 
      71             : \auxfile{file1.pdb}
      72             : ATOM      8  HT3 ALA     2      -1.480  -1.560   1.212  1.00  1.00      DIA  H
      73             : ATOM      9  CAY ALA     2      -0.096   2.144  -0.669  1.00  1.00      DIA  C
      74             : ATOM     10  HY1 ALA     2       0.871   2.385  -0.588  1.00  1.00      DIA  H
      75             : ATOM     12  HY3 ALA     2      -0.520   2.679  -1.400  1.00  1.00      DIA  H
      76             : ATOM     14  OY  ALA     2      -1.139   0.931  -0.973  1.00  1.00      DIA  O
      77             : END
      78             : \endauxfile
      79             : 
      80             : The following tells plumed to calculate a DRMSD value for a pair of molecules.
      81             : 
      82             : \plumedfile
      83             : DRMSD REFERENCE=file2.pdb LOWER_CUTOFF=0.1 UPPER_CUTOFF=0.8 TYPE=INTER-DRMSD
      84             : \endplumedfile
      85             : 
      86             : In the input reference file (file.pdb) the atoms in each of the two molecules are separated by a TER
      87             : command as shown below.
      88             : 
      89             : \auxfile{file2.pdb}
      90             : ATOM      8  HT3 ALA     2      -1.480  -1.560   1.212  1.00  1.00      DIA  H
      91             : ATOM      9  CAY ALA     2      -0.096   2.144  -0.669  1.00  1.00      DIA  C
      92             : ATOM     10  HY1 ALA     2       0.871   2.385  -0.588  1.00  1.00      DIA  H
      93             : TER
      94             : ATOM     12  HY3 ALA     2      -0.520   2.679  -1.400  1.00  1.00      DIA  H
      95             : ATOM     14  OY  ALA     2      -1.139   0.931  -0.973  1.00  1.00      DIA  O
      96             : END
      97             : \endauxfile
      98             : 
      99             : In this example the INTER-DRMSD type ensures that the set of distances from which the final
     100             : quantity is computed involve one atom from each of the two molecules.  If this is replaced
     101             : by INTRA-DRMSD then only those distances involving pairs of atoms that are both in the same
     102             : molecule are computed.
     103             : 
     104             : */
     105             : //+ENDPLUMEDOC
     106             : 
     107             : class DRMSD : public ActionShortcut {
     108             : public:
     109             :   static void registerKeywords(Keywords& keys);
     110             :   explicit DRMSD(const ActionOptions&);
     111             : };
     112             : 
     113             : PLUMED_REGISTER_ACTION(DRMSD,"DRMSD")
     114             : 
     115          16 : void DRMSD::registerKeywords( Keywords& keys ) {
     116          16 :   ActionShortcut::registerKeywords( keys );
     117          32 :   keys.add("compulsory","REFERENCE","a file in pdb format containing the reference structure and the atoms involved in the CV.");
     118          32 :   keys.add("optional","LOWER_CUTOFF","only pairs of atoms further than LOWER_CUTOFF are considered in the calculation.");
     119          32 :   keys.add("optional","UPPER_CUTOFF","only pairs of atoms closer than UPPER_CUTOFF are considered in the calculation.");
     120          32 :   keys.add("compulsory","TYPE","DRMSD","what kind of DRMSD would you like to calculate.  You can use either the normal DRMSD involving all the distances between "
     121             :            "the atoms in your molecule.  Alternatively, if you have multiple molecules you can use the type INTER-DRMSD "
     122             :            "to compute DRMSD values involving only those distances between the atoms at least two molecules or the type INTRA-DRMSD "
     123             :            "to compute DRMSD values involving only those distances between atoms in the same molecule");
     124          32 :   keys.addFlag("SQUARED",false,"This should be setted if you want MSD instead of RMSD ");
     125          32 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions when calculating distances");
     126             :   // This is just ignored in reality which is probably bad
     127          32 :   keys.addFlag("NUMERICAL_DERIVATIVES",false,"calculate the derivatives for these quantities numerically");
     128          32 :   keys.setValueDescription("scalar/vector","the DRMSD distance between the instantaneous structure and the reference structure");
     129          80 :   keys.needsAction("SUM"); keys.needsAction("DISTANCE"); keys.needsAction("CONSTANT"); keys.needsAction("EUCLIDEAN_DISTANCE"); keys.needsAction("CUSTOM");
     130          16 : }
     131             : 
     132          13 : DRMSD::DRMSD( const ActionOptions& ao ):
     133             :   Action(ao),
     134          13 :   ActionShortcut(ao)
     135             : {
     136             :   // Read in the reference configuration
     137          13 :   std::string reference; parse("REFERENCE",reference);
     138             :   // First bit of input for the instantaneous distances
     139          26 :   bool numder; parseFlag("NUMERICAL_DERIVATIVES",numder); double fake_unit=0.1;
     140          13 :   FILE* fp2=fopen(reference.c_str(),"r"); bool do_read=true; unsigned nframes=0;
     141          65 :   while( do_read ) {
     142          54 :     PDB mypdb; do_read=mypdb.readFromFilepointer(fp2,false,fake_unit);
     143          54 :     if( !do_read && nframes>0 ) break ;
     144          53 :     nframes++;
     145          54 :   }
     146          12 :   fclose(fp2);
     147             : 
     148             :   // Get cutoff information
     149          25 :   double lcut=0; parse("LOWER_CUTOFF",lcut); std::string drmsd_type; parse("TYPE",drmsd_type);
     150          12 :   double ucut=std::numeric_limits<double>::max(); parse("UPPER_CUTOFF",ucut);
     151          24 :   bool nopbc; parseFlag("NOPBC",nopbc); std::string pbc_str; if(nopbc) pbc_str="NOPBC";
     152             :   // Open the pdb file
     153          12 :   FILE* fp=fopen(reference.c_str(),"r"); do_read=true;
     154          12 :   if(!fp) error("could not open reference file " + reference ); unsigned n=0; std::string allpairs="";
     155             :   std::vector<std::pair<unsigned,unsigned> > upairs; std::vector<std::string> refvals;
     156          65 :   while ( do_read ) {
     157          54 :     PDB mypdb; do_read=mypdb.readFromFilepointer(fp,false,fake_unit);
     158          54 :     if( !do_read && n>0 ) break ;
     159          53 :     std::vector<Vector> pos( mypdb.getPositions() ); unsigned nn=1;
     160          53 :     if( pos.size()==0 ) error("read no atoms from file named " + reference );
     161             :     // This is what we do for the first frame
     162          53 :     if( n==0 ) {
     163          12 :       std::vector<AtomNumber> atoms( mypdb.getAtomNumbers() );
     164          12 :       if( drmsd_type=="DRMSD" ) {
     165         102 :         for(unsigned i=0; i<atoms.size()-1; ++i) {
     166          92 :           std::string istr; Tools::convert( atoms[i].serial(), istr );
     167         671 :           for(unsigned j=i+1; j<atoms.size(); ++j) {
     168         579 :             std::string jstr; Tools::convert( atoms[j].serial(), jstr );
     169         579 :             double distance = delta( pos[i], pos[j] ).modulo();
     170         579 :             if( distance < ucut && distance > lcut ) {
     171         386 :               std::string num; Tools::convert( nn, num ); nn++;
     172             :               // Add this pair to list of pairs
     173         386 :               upairs.push_back( std::pair<unsigned,unsigned>(i,j) );
     174             :               // Add this distance to list of reference values
     175         386 :               std::string dstr; Tools::convert( distance, dstr ); refvals.push_back( dstr );
     176             :               // Calculate this distance
     177         694 :               if( nframes==1 ) allpairs += " ATOMS" + num + "=" + istr + "," + jstr;
     178         156 :               else readInputLine( getShortcutLabel() + "_d" + num + ": DISTANCE ATOMS=" + istr + "," + jstr + " " + pbc_str );
     179             :             }
     180             :           }
     181             :         }
     182             :       } else {
     183           2 :         unsigned nblocks = mypdb.getNumberOfAtomBlocks(); std::vector<unsigned> blocks( 1 + nblocks );
     184           2 :         if( nblocks==1 ) { blocks[0]=0; blocks[1]=atoms.size(); }
     185           6 :         else { blocks[0]=0; for(unsigned i=0; i<nblocks; ++i) blocks[i+1]=mypdb.getAtomBlockEnds()[i]; }
     186           2 :         if( drmsd_type=="INTRA-DRMSD" ) {
     187           3 :           for(unsigned i=0; i<nblocks; ++i) {
     188           7 :             for(unsigned iatom=blocks[i]+1; iatom<blocks[i+1]; ++iatom) {
     189           5 :               std::string istr; Tools::convert( atoms[iatom].serial(), istr );
     190          14 :               for(unsigned jatom=blocks[i]; jatom<iatom; ++jatom) {
     191           9 :                 std::string jstr; Tools::convert( atoms[jatom].serial(), jstr );
     192           9 :                 double distance = delta( pos[iatom], pos[jatom] ).modulo();
     193           9 :                 if(distance < ucut && distance > lcut ) {
     194           9 :                   std::string num; Tools::convert( nn, num ); nn++;
     195             :                   // Add this pair to list of pairs
     196           9 :                   upairs.push_back( std::pair<unsigned,unsigned>(iatom,jatom) );
     197             :                   // Add this distance to list of reference values
     198           9 :                   std::string dstr; Tools::convert( distance, dstr ); refvals.push_back( dstr );
     199             :                   // Calculate this distance
     200          18 :                   if( nframes==1 ) allpairs += " ATOMS" + num + "=" + istr + "," + jstr;
     201           0 :                   else readInputLine( getShortcutLabel() + "_d" + num + ": DISTANCE ATOMS=" + istr + "," + jstr + " " + pbc_str );
     202             :                 }
     203             :               }
     204             :             }
     205             :           }
     206           1 :         } else if( drmsd_type=="INTER-DRMSD" ) {
     207           2 :           for(unsigned i=1; i<nblocks; ++i) {
     208           2 :             for(unsigned j=0; j<i; ++j) {
     209           4 :               for(unsigned iatom=blocks[i]; iatom<blocks[i+1]; ++iatom) {
     210           3 :                 std::string istr; Tools::convert( atoms[iatom].serial(), istr );
     211          15 :                 for(unsigned jatom=blocks[j]; jatom<blocks[j+1]; ++jatom) {
     212          12 :                   std::string jstr; Tools::convert( atoms[jatom].serial(), jstr );
     213          12 :                   double distance = delta( pos[iatom], pos[jatom] ).modulo();
     214          12 :                   if(distance < ucut && distance > lcut ) {
     215          12 :                     std::string num; Tools::convert( nn, num ); nn++;
     216             :                     // Add this pair to list of pairs
     217          12 :                     upairs.push_back( std::pair<unsigned,unsigned>(iatom,jatom) );
     218             :                     // Add this distance to list of reference values
     219          12 :                     std::string dstr; Tools::convert( distance, dstr ); refvals.push_back( dstr );
     220             :                     // Calculate this distance
     221          24 :                     if( nframes==1 ) allpairs += " ATOMS" + num + "=" + istr + "," + jstr;
     222           0 :                     else readInputLine( getShortcutLabel() + "_d" + num + ": DISTANCE ATOMS=" + istr + "," + jstr + " " + pbc_str );
     223             :                   }
     224             :                 }
     225             :               }
     226             :             }
     227             :           }
     228           0 :         } else plumed_merror( drmsd_type + " is not valid input to TYPE keyword");
     229             :       }
     230             :       // This is for every subsequent frame
     231             :     } else {
     232        3239 :       for(unsigned i=0; i<refvals.size(); ++i) {
     233        3198 :         std::string dstr; Tools::convert( delta( pos[upairs[i].first], pos[upairs[i].second] ).modulo(), dstr );
     234        6396 :         refvals[i] += "," + dstr;
     235             :       }
     236             :     }
     237          53 :     n++;
     238          54 :   }
     239             :   // Now create values that hold all the reference distances
     240          12 :   fclose(fp);
     241             : 
     242          12 :   if( nframes==1 ) {
     243          22 :     readInputLine( getShortcutLabel() + "_d: DISTANCE" + allpairs + " " + pbc_str );
     244         340 :     std::string refstr = refvals[0]; for(unsigned i=1; i<refvals.size(); ++i) refstr += "," + refvals[i];
     245          22 :     readInputLine( getShortcutLabel() + "_ref: CONSTANT VALUES="  + refstr );
     246          22 :     readInputLine( getShortcutLabel() + "_diffs: CUSTOM ARG=" + getShortcutLabel() + "_d," + getShortcutLabel() + "_ref FUNC=(x-y)*(x-y) PERIODIC=NO");
     247          22 :     readInputLine( getShortcutLabel() + "_u: SUM ARG=" + getShortcutLabel() + "_diffs PERIODIC=NO");
     248             :   } else {
     249             :     std::string arg_str1, arg_str2;
     250          79 :     for(unsigned i=0; i<refvals.size(); ++i ) {
     251          78 :       std::string inum; Tools::convert( i+1, inum );
     252         156 :       readInputLine( getShortcutLabel() + "_ref" + inum + ": CONSTANT VALUES=" + refvals[i] );
     253          80 :       if( i==0 ) { arg_str1 = getShortcutLabel() + "_d" + inum; arg_str2 = getShortcutLabel() + "_ref" + inum; }
     254         231 :       else { arg_str1 += "," + getShortcutLabel() + "_d" + inum; arg_str2 += "," + getShortcutLabel() + "_ref" + inum; }
     255             :     }
     256             :     // And calculate the euclidean distances between the true distances and the references
     257           2 :     readInputLine( getShortcutLabel() + "_u: EUCLIDEAN_DISTANCE SQUARED ARG1=" + arg_str1 + " ARG2=" + arg_str2 );
     258             :   }
     259             :   // And final value
     260          12 :   std::string nvals; Tools::convert( refvals.size(), nvals ); bool squared; parseFlag("SQUARED",squared);
     261          15 :   if( squared ) readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_u FUNC=x/" + nvals + " PERIODIC=NO");
     262             :   else {
     263          18 :     readInputLine( getShortcutLabel() + "_2: CUSTOM ARG=" + getShortcutLabel() + "_u FUNC=(x/" + nvals + ") PERIODIC=NO");
     264          18 :     readInputLine( getShortcutLabel() + ": CUSTOM ARG=" + getShortcutLabel() + "_2 FUNC=sqrt(x) PERIODIC=NO");
     265             :   }
     266          26 : }
     267             : 
     268             : }
     269             : }
     270             : 
     271             : 

Generated by: LCOV version 1.16