LCOV - code coverage report
Current view: top level - colvar - PCARMSD.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 95 96 99.0 %
Date: 2019-08-13 10:15:31 Functions: 10 11 90.9 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2014-2019 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/Atoms.h"
      24             : #include "core/PlumedMain.h"
      25             : #include "ActionRegister.h"
      26             : #include "tools/PDB.h"
      27             : #include "tools/RMSD.h"
      28             : #include "tools/Tools.h"
      29             : #include <memory>
      30             : 
      31             : using namespace std;
      32             : 
      33             : namespace PLMD {
      34             : namespace colvar {
      35             : 
      36           4 : class PCARMSD : public Colvar {
      37             : 
      38             :   std::unique_ptr<PLMD::RMSD> rmsd;
      39             :   bool squared;
      40             :   bool nopbc;
      41             :   std::vector< std::vector<Vector> > eigenvectors;
      42             :   std::vector<PDB> pdbv;
      43             :   std::vector<string> pca_names;
      44             : public:
      45             :   explicit PCARMSD(const ActionOptions&);
      46             :   void calculate() override;
      47             :   static void registerKeywords(Keywords& keys);
      48             : };
      49             : 
      50             : 
      51             : using namespace std;
      52             : 
      53             : //+PLUMEDOC DCOLVAR PCARMSD
      54             : /*
      55             : Calculate the PCA components ( see \cite Sutto:2010 and \cite spiwok )  for a number of provided eigenvectors and an average structure. Performs optimal alignment at every step and reports the rmsd so you know if you are far or close from the average structure.
      56             : It takes the average structure and eigenvectors in form of a pdb.
      57             : Note that beta and occupancy values in the pdb are neglected and all the weights are placed to 1 (differently from the RMSD colvar for example)
      58             : 
      59             : \par Examples
      60             : 
      61             : \plumedfile
      62             : PCARMSD AVERAGE=file.pdb EIGENVECTORS=eigenvectors.pdb
      63             : \endplumedfile
      64             : 
      65             : The input is taken so to be compatible with the output you get from g_covar utility of gromacs (suitably adapted to have a pdb input format).
      66             : The reference configuration (file.pdb) will thus be in a file that looks something like this:
      67             : 
      68             : \auxfile{file.pdb}
      69             : TITLE     Average structure
      70             : MODEL        1
      71             : ATOM      1  CL  ALA     1       1.042  -3.070   0.946  1.00  0.00
      72             : ATOM      5  CLP ALA     1       0.416  -2.033   0.132  1.00  0.00
      73             : ATOM      6  OL  ALA     1       0.415  -2.082  -0.976  1.00  0.00
      74             : ATOM      7  NL  ALA     1      -0.134  -1.045   0.677  1.00  0.00
      75             : ATOM      9  CA  ALA     1      -0.774   0.053   0.003  1.00  0.00
      76             : TER
      77             : ENDMDL
      78             : \endauxfile
      79             : 
      80             : while the eigenvectors will be in a pdb file (eigenvectors.pdb) that looks something like this:
      81             : 
      82             : \auxfile{eigenvectors.pdb}
      83             : TITLE     frame t= -1.000
      84             : MODEL        1
      85             : ATOM      1  CL  ALA     1       1.194  -2.988   0.724  1.00  0.00
      86             : ATOM      5  CLP ALA     1      -0.996   0.042   0.144  1.00  0.00
      87             : ATOM      6  OL  ALA     1      -1.246  -0.178  -0.886  1.00  0.00
      88             : ATOM      7  NL  ALA     1      -2.296   0.272   0.934  1.00  0.00
      89             : ATOM      9  CA  ALA     1      -0.436   2.292   0.814  1.00  0.00
      90             : TER
      91             : ENDMDL
      92             : TITLE     frame t= 0.000
      93             : MODEL        1
      94             : ATOM      1  CL  ALA     1       1.042  -3.070   0.946  1.00  0.00
      95             : ATOM      5  CLP ALA     1      -0.774   0.053   0.003  1.00  0.00
      96             : ATOM      6  OL  ALA     1      -0.849  -0.166  -1.034  1.00  0.00
      97             : ATOM      7  NL  ALA     1      -2.176   0.260   0.563  1.00  0.00
      98             : ATOM      9  CA  ALA     1       0.314   1.825   0.962  1.00  0.00
      99             : TER
     100             : ENDMDL
     101             : 
     102             : \endauxfile
     103             : 
     104             : */
     105             : //+ENDPLUMEDOC
     106             : 
     107        7836 : PLUMED_REGISTER_ACTION(PCARMSD,"PCARMSD")
     108             : 
     109           3 : void PCARMSD::registerKeywords(Keywords& keys) {
     110           3 :   Colvar::registerKeywords(keys);
     111          12 :   keys.add("compulsory","AVERAGE","a file in pdb format containing the reference structure and the atoms involved in the CV.");
     112          12 :   keys.add("compulsory","EIGENVECTORS","a file in pdb format containing the reference structure and the atoms involved in the CV.");
     113          12 :   keys.addOutputComponent("eig","default","the projections on each eigenvalue are stored on values labeled eig-1, eig-2, ...");
     114          12 :   keys.addOutputComponent("residual","default","the distance of the present configuration from the configuration supplied as AVERAGE in terms of mean squared displacement after optimal alignment ");
     115           9 :   keys.addFlag("SQUARED_ROOT",false," This should be set if you want RMSD instead of mean squared displacement ");
     116           3 : }
     117             : 
     118           2 : PCARMSD::PCARMSD(const ActionOptions&ao):
     119             :   PLUMED_COLVAR_INIT(ao),
     120             :   squared(true),
     121           2 :   nopbc(false)
     122             : {
     123             :   string f_average;
     124           4 :   parse("AVERAGE",f_average);
     125             :   string type;
     126           2 :   type.assign("OPTIMAL");
     127             :   string f_eigenvectors;
     128           4 :   parse("EIGENVECTORS",f_eigenvectors);
     129           4 :   bool sq;  parseFlag("SQUARED_ROOT",sq);
     130           2 :   if (sq) { squared=false; }
     131           4 :   parseFlag("NOPBC",nopbc);
     132           2 :   checkRead();
     133             : 
     134           4 :   PDB pdb;
     135             : 
     136             :   // read everything in ang and transform to nm if we are not in natural units
     137           4 :   if( !pdb.read(f_average,plumed.getAtoms().usingNaturalUnits(),0.1/atoms.getUnits().getLength()) )
     138           0 :     error("missing input file " + f_average );
     139             : 
     140           2 :   rmsd.reset( new RMSD() );
     141             :   bool remove_com=true;
     142             :   bool normalize_weights=true;
     143             :   // here align and displace are a simple vector of ones
     144          48 :   std::vector<double> align; align=pdb.getOccupancy(); for(unsigned i=0; i<align.size(); i++) {align[i]=1.;} ;
     145          48 :   std::vector<double> displace;  displace=pdb.getBeta(); for(unsigned i=0; i<displace.size(); i++) {displace[i]=1.;} ;
     146             :   // reset again to reimpose unifrom weights (safe to disable this)
     147           4 :   rmsd->set(align,displace,pdb.getPositions(),type,remove_com,normalize_weights);
     148           2 :   requestAtoms( pdb.getAtomNumbers() );
     149             : 
     150           6 :   addComponentWithDerivatives("residual"); componentIsNotPeriodic("residual");
     151             : 
     152           2 :   log.printf("  average from file %s\n",f_average.c_str());
     153           2 :   log.printf("  which contains %d atoms\n",getNumberOfAtoms());
     154           2 :   log.printf("  with indices : ");
     155          46 :   for(unsigned i=0; i<pdb.getAtomNumbers().size(); ++i) {
     156          22 :     if(i%25==0) log<<"\n";
     157          44 :     log.printf("%d ",pdb.getAtomNumbers()[i].serial());
     158             :   }
     159           2 :   log.printf("\n");
     160           2 :   log.printf("  method for alignment : %s \n",type.c_str() );
     161           2 :   if(nopbc) log.printf("  without periodic boundary conditions\n");
     162           1 :   else      log.printf("  using periodic boundary conditions\n");
     163             : 
     164           6 :   log<<"  Bibliography "<<plumed.cite("Spiwok, Lipovova and Kralova, JPCB, 111, 3073 (2007)  ");
     165           6 :   log<<" "<<plumed.cite( "Sutto, D'Abramo, Gervasio, JCTC, 6, 3640 (2010)");
     166             : 
     167             :   // now get the eigenvectors
     168             :   // open the file
     169           2 :   FILE* fp=fopen(f_eigenvectors.c_str(),"r");
     170             :   std::vector<AtomNumber> aaa;
     171             :   unsigned neigenvects;
     172           2 :   neigenvects=0;
     173           2 :   if (fp!=NULL)
     174             :   {
     175           2 :     log<<"  Opening the eigenvectors file "<<f_eigenvectors.c_str()<<"\n";
     176             :     bool do_read=true;
     177          74 :     while (do_read) {
     178          72 :       PDB mypdb;
     179             :       // check the units for reading this file: how can they make sense?
     180         144 :       do_read=mypdb.readFromFilepointer(fp,plumed.getAtoms().usingNaturalUnits(),0.1/atoms.getUnits().getLength());
     181          72 :       if(do_read) {
     182          70 :         neigenvects++;
     183         140 :         if(mypdb.getAtomNumbers().size()==0) error("number of atoms in a frame should be more than zero");
     184          70 :         unsigned nat=mypdb.getAtomNumbers().size();
     185         140 :         if(nat!=mypdb.getAtomNumbers().size()) error("frames should have the same number of atoms");
     186          70 :         if(aaa.empty()) aaa=mypdb.getAtomNumbers();
     187         140 :         if(aaa!=mypdb.getAtomNumbers()) error("frames should contain same atoms in same order");
     188         140 :         log<<"  Found eigenvector: "<<neigenvects<<" containing  "<<mypdb.getAtomNumbers().size()<<" atoms\n";
     189          70 :         pdbv.push_back(mypdb);
     190          70 :         eigenvectors.push_back(mypdb.getPositions());
     191             :       } else {break ;}
     192          70 :     }
     193           2 :     fclose (fp);
     194           2 :     log<<"  Found total "<<neigenvects<< " eigenvectors in the file "<<f_eigenvectors.c_str()<<" \n";
     195           2 :     if(neigenvects==0) error("at least one eigenvector is expected");
     196             :   }
     197             :   // the components
     198          70 :   for(unsigned i=0; i<neigenvects; i++) {
     199          70 :     std::string num; Tools::convert( i, num );
     200         210 :     string name; name=string("eig-")+num;
     201          70 :     pca_names.push_back(name);
     202          70 :     addComponentWithDerivatives(name); componentIsNotPeriodic(name);
     203             :   }
     204           2 :   turnOnDerivatives();
     205             : 
     206           2 : }
     207             : 
     208             : // calculator
     209        1092 : void PCARMSD::calculate() {
     210        1092 :   if(!nopbc) makeWhole();
     211        1092 :   Tensor rotation,invrotation;
     212             :   Matrix<std::vector<Vector> > drotdpos(3,3);
     213             :   std::vector<Vector> alignedpos;
     214             :   std::vector<Vector> centeredpos;
     215             :   std::vector<Vector> centeredref;
     216             :   std::vector<Vector> ddistdpos;
     217        2184 :   double r=rmsd->calc_PCAelements( getPositions(), ddistdpos, rotation,  drotdpos, alignedpos,centeredpos, centeredref,squared);
     218        1092 :   invrotation=rotation.transpose();
     219             : 
     220        2184 :   Value* verr=getPntrToComponent("residual");
     221             :   verr->set(r);
     222       25116 :   for(unsigned iat=0; iat<getNumberOfAtoms(); iat++) {
     223       24024 :     setAtomsDerivatives (verr,iat,ddistdpos[iat]);
     224             :   }
     225             : 
     226             :   std::vector< Vector > der;
     227        1092 :   der.resize(getNumberOfAtoms());
     228             : 
     229             : 
     230       77532 :   for(unsigned i=0; i<eigenvectors.size(); i++) {
     231       76440 :     Value* value=getPntrToComponent(pca_names[i].c_str());
     232             :     double val; val=0.;
     233      917280 :     for(unsigned iat=0; iat<getNumberOfAtoms(); iat++) {
     234     1261260 :       val+=dotProduct(alignedpos[iat]-centeredref[iat],eigenvectors[i][iat]);   der[iat].zero();
     235             :     }
     236             :     value->set(val);
     237             :     // here the loop is reversed to better suit the structure of the derivative of the rotation matrix
     238             :     double tmp1;
     239      114660 :     for(unsigned a=0; a<3; a++) {
     240      343980 :       for(unsigned b=0; b<3; b++) {
     241             :         tmp1=0.;
     242     7911540 :         for(unsigned n=0; n<getNumberOfAtoms(); n++) {
     243    11351340 :           tmp1+=centeredpos[n][b]*eigenvectors[i][n][a];
     244             :         }
     245     7911540 :         for(unsigned iat=0; iat<getNumberOfAtoms(); iat++) {
     246    11351340 :           der[iat]+=drotdpos[a][b][iat]*tmp1;
     247             :         }
     248             :       }
     249             :     }
     250       38220 :     Vector v1;
     251      917280 :     for(unsigned n=0; n<getNumberOfAtoms(); n++) {
     252     1261260 :       v1+=(1./getNumberOfAtoms())*matmul(invrotation,eigenvectors[i][n]);
     253             :     }
     254      879060 :     for(unsigned iat=0; iat<getNumberOfAtoms(); iat++) {
     255     1261260 :       der[iat]+=matmul(invrotation,eigenvectors[i][iat])-v1;
     256      420420 :       setAtomsDerivatives (value,iat,der[iat]);
     257             :     }
     258             :   }
     259             : 
     260       79716 :   for(int i=0; i<getNumberOfComponents(); ++i) setBoxDerivativesNoPbc( getPntrToComponent(i) );
     261             : 
     262        1092 : }
     263             : 
     264             : }
     265        5874 : }
     266             : 
     267             : 
     268             : 

Generated by: LCOV version 1.14