LCOV - code coverage report
Current view: top level - colvar - RMSDVector.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 231 243 95.1 %
Date: 2025-03-25 09:33:27 Functions: 11 12 91.7 %

          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 "RMSDVector.h"
      23             : #include "core/PlumedMain.h"
      24             : #include "core/ActionRegister.h"
      25             : #include "tools/PDB.h"
      26             : 
      27             : //+PLUMEDOC DCOLVAR RMSD_VECTOR
      28             : /*
      29             : Calculate the RMSD distance between the instaneous configuration and multiple reference configurations
      30             : 
      31             : 
      32             : \par Examples
      33             : 
      34             : */
      35             : //+ENDPLUMEDOC
      36             : 
      37             : namespace PLMD {
      38             : namespace colvar {
      39             : 
      40             : PLUMED_REGISTER_ACTION(RMSDVector,"RMSD_VECTOR")
      41             : 
      42          81 : void RMSDVector::registerKeywords(Keywords& keys) {
      43          81 :   ActionWithVector::registerKeywords(keys);
      44          81 :   keys.setDisplayName("RMSD");
      45         162 :   keys.addInputKeyword("compulsory","ARG","vector/matrix","the labels of two actions that you are calculating the RMSD between");
      46          81 :   keys.add("compulsory","TYPE","SIMPLE","the manner in which RMSD alignment is performed.  Should be OPTIMAL or SIMPLE.");
      47          81 :   keys.add("compulsory","ALIGN","1.0","the weights to use when aligning to the reference structure");
      48          81 :   keys.add("compulsory","DISPLACE","1.0","the weights to use when calculating the displacement from the reference structure");
      49          81 :   keys.addFlag("SQUARED",false," This should be set if you want mean squared displacement instead of RMSD ");
      50          81 :   keys.addFlag("UNORMALIZED",false,"by default the mean sequare deviation or root mean square deviation is calculated.  If this option is given no averaging is done");
      51          81 :   keys.addFlag("DISPLACEMENT",false,"Calculate the vector of displacements instead of the length of this vector");
      52         162 :   keys.addOutputComponent("disp","DISPLACEMENT","vector/matrix","the vector of displacements for the atoms");
      53         162 :   keys.addOutputComponent("dist","DISPLACEMENT","scalar/vector","the RMSD distance the atoms have moved");
      54         162 :   keys.setValueDescription("scalar/vector","a vector containing the RMSD between the instantaneous structure and each of the reference structures that were input");
      55          81 : }
      56             : 
      57          49 : RMSDVector::RMSDVector(const ActionOptions&ao):
      58             :   Action(ao),
      59             :   ActionWithVector(ao),
      60          49 :   firststep(true) {
      61          49 :   if( getPntrToArgument(0)->getRank()!=1 ) {
      62           0 :     error("first argument should be vector");
      63             :   }
      64          49 :   if( getPntrToArgument(1)->getRank()<1 ) {
      65           0 :     error("second argument should be matrix or a vector");
      66             :   }
      67          49 :   if( getPntrToArgument(1)->getRank()==1 ) {
      68           7 :     if( getPntrToArgument(0)->getNumberOfValues()!=getPntrToArgument(1)->getNumberOfValues() ) {
      69           0 :       error("mismatch between sizes of input vectors");
      70             :     }
      71          42 :   } else if( getPntrToArgument(1)->getRank()==2 ) {
      72          42 :     if( getPntrToArgument(0)->getNumberOfValues()!=getPntrToArgument(1)->getShape()[1] ) {
      73           0 :       error("mismatch between sizes of input vectors");
      74             :     }
      75             :   }
      76          49 :   if( getPntrToArgument(0)->getNumberOfValues()%3!=0 ) {
      77           0 :     error("number of components in input arguments should be multiple of three");
      78             :   }
      79             : 
      80          49 :   unsigned natoms = getPntrToArgument(0)->getNumberOfValues() / 3;
      81          49 :   type.assign("SIMPLE");
      82          49 :   parse("TYPE",type);
      83          49 :   parseFlag("SQUARED",squared);
      84          49 :   align.resize( natoms );
      85          49 :   parseVector("ALIGN",align);
      86          49 :   displace.resize( natoms );
      87          49 :   parseVector("DISPLACE",displace);
      88          49 :   bool unorm=false;
      89          49 :   parseFlag("UNORMALIZED",unorm);
      90          49 :   norm_weights=!unorm;
      91             :   double wa=0, wd=0;
      92          49 :   sqrtdisplace.resize( displace.size() );
      93         630 :   for(unsigned i=0; i<align.size(); ++i) {
      94         581 :     wa+=align[i];
      95         581 :     wd+=displace[i];
      96             :   }
      97             : 
      98          49 :   if( wa>epsilon ) {
      99          49 :     double iwa = 1. / wa;
     100         630 :     for(unsigned i=0; i<align.size(); ++i) {
     101         581 :       align[i] *= iwa;
     102             :     }
     103             :   } else {
     104           0 :     double iwa = 1. / natoms;
     105           0 :     for(unsigned i=0; i<align.size(); ++i) {
     106           0 :       align[i] = iwa;
     107             :     }
     108             :   }
     109          49 :   if( wd>epsilon ) {
     110          49 :     if( !norm_weights ) {
     111             :       wd = 1;
     112             :     }
     113          49 :     double iwd = 1. / wd;
     114         630 :     for(unsigned i=0; i<align.size(); ++i) {
     115         581 :       displace[i] *= iwd;
     116             :     }
     117             :   } else {
     118           0 :     double iwd = 1. / natoms;
     119           0 :     for(unsigned i=0; i<align.size(); ++i) {
     120           0 :       displace[i] = iwd;
     121             :     }
     122             :   }
     123         630 :   for(unsigned i=0; i<align.size(); ++i) {
     124         581 :     sqrtdisplace[i] = sqrt(displace[i]);
     125             :   }
     126             : 
     127          49 :   parseFlag("DISPLACEMENT",displacement);
     128          49 :   if( displacement && (getPntrToArgument(1)->getRank()==1 || getPntrToArgument(1)->getShape()[0]<=1) ) {
     129          52 :     addComponentWithDerivatives("dist");
     130          52 :     componentIsNotPeriodic("dist");
     131          26 :     std::vector<unsigned> shape( 1, getPntrToArgument(0)->getNumberOfValues() );
     132          26 :     addComponent( "disp", shape );
     133          26 :     getPntrToComponent(1)->buildDataStore();
     134          52 :     componentIsNotPeriodic("disp");
     135          23 :   } else if( displacement ) {
     136           2 :     std::vector<unsigned> shape( 1, getPntrToArgument(1)->getShape()[0] );
     137           2 :     addComponent( "dist", shape );
     138           2 :     getPntrToComponent(0)->buildDataStore();
     139           2 :     componentIsNotPeriodic("dist");
     140           2 :     shape.resize(2);
     141           2 :     shape[0] = getPntrToArgument(1)->getShape()[0];
     142           2 :     shape[1] = getPntrToArgument(0)->getNumberOfValues();
     143           2 :     addComponent( "disp", shape );
     144           2 :     getPntrToComponent(1)->buildDataStore();
     145           2 :     getPntrToComponent(1)->reshapeMatrixStore( shape[1] );
     146           4 :     componentIsNotPeriodic("disp");
     147          21 :   } else if( (getPntrToArgument(1)->getRank()==1 || getPntrToArgument(1)->getShape()[0]==1) ) {
     148          13 :     addValue();
     149          13 :     setNotPeriodic();
     150             :   } else {
     151           8 :     std::vector<unsigned> shape( 1, getPntrToArgument(1)->getShape()[0] );
     152           8 :     addValue( shape );
     153           8 :     setNotPeriodic();
     154             :   }
     155          49 :   if( getPntrToArgument(1)->getRank()==1 || getPntrToArgument(1)->getNumberOfValues()==0 ) {
     156           7 :     myrmsd.resize(1);
     157             :   } else {
     158          42 :     myrmsd.resize( getPntrToArgument(1)->getShape()[0] );
     159             :   }
     160             : 
     161          49 :   if( getPntrToArgument(1)->getRank()==1 )
     162           7 :     log.printf("  calculating RMSD distance between %d atoms. Distance between the avectors of atoms in %s and %s\n",
     163             :                natoms, getPntrToArgument(0)->getName().c_str(), getPntrToArgument(1)->getName().c_str() );
     164             :   else
     165          42 :     log.printf("  calculating RMSD distance between %d sets of %d atoms. Distance between vector %s of atoms and matrix of configurations in %s\n",
     166             :                getPntrToArgument(1)->getShape()[0], natoms, getPntrToArgument(0)->getName().c_str(), getPntrToArgument(1)->getName().c_str() );
     167          49 :   log.printf("  method for alignment : %s \n",type.c_str() );
     168          49 :   if(squared) {
     169          28 :     log.printf("  chosen to use SQUARED option for MSD instead of RMSD\n");
     170             :   } else {
     171          21 :     log.printf("  using periodic boundary conditions\n");
     172             :   }
     173          49 : }
     174             : 
     175         804 : unsigned RMSDVector::getNumberOfDerivatives() {
     176         804 :   return getPntrToArgument(0)->getNumberOfValues() + getPntrToArgument(1)->getNumberOfValues();
     177             : }
     178             : 
     179       10631 : void RMSDVector::setReferenceConfigurations() {
     180       10631 :   unsigned natoms = getPntrToArgument(0)->getShape()[0] / 3;
     181       10631 :   Vector center;
     182       10631 :   std::vector<Vector> pos( natoms );
     183       22578 :   for(unsigned jconf=0; jconf<myrmsd.size(); ++jconf) {
     184       11947 :     center.zero();
     185      172431 :     for(unsigned i=0; i<pos.size(); ++i) {
     186      641936 :       for(unsigned j=0; j<3; ++j) {
     187      481452 :         pos[i][j] = getPntrToArgument(1)->get( (3*jconf+j)*pos.size() + i );
     188             :       }
     189      160484 :       center+=pos[i]*align[i];
     190             :     }
     191      172431 :     for(unsigned i=0; i<pos.size(); ++i) {
     192      160484 :       pos[i] -= center;
     193             :     }
     194       11947 :     myrmsd[jconf].clear();
     195       11947 :     myrmsd[jconf].set(align,displace,pos,type,true,norm_weights);
     196             :   }
     197       10631 : }
     198             : 
     199      192996 : double RMSDVector::calculateRMSD( const unsigned& current, std::vector<Vector>& pos, std::vector<Vector>& der, std::vector<Vector>& direction ) const {
     200      192996 :   unsigned natoms = pos.size();
     201     2704147 :   for(unsigned i=0; i<natoms; ++i) {
     202    10044604 :     for(unsigned j=0; j<3; ++j) {
     203     7533453 :       pos[i][j] = getPntrToArgument(0)->get( j*natoms + i );
     204             :     }
     205             :   }
     206             : 
     207      192996 :   if( displacement && type=="SIMPLE" ) {
     208         646 :     const Value* myval = getConstPntrToComponent(1);
     209         646 :     double r = myrmsd[current].simpleAlignment( align, displace, pos, myrmsd[current].getReference(), der, direction, squared );
     210         646 :     if( !doNotCalculateDerivatives() && myval->forcesWereAdded() ) {
     211          33 :       Vector comforce;
     212          33 :       comforce.zero();
     213         187 :       for(unsigned i=0; i<natoms; i++) {
     214         616 :         for(unsigned k=0; k<3; ++k) {
     215         462 :           comforce[k] += align[i]*myval->getForce( (3*current+k)*natoms + i);
     216             :         }
     217             :       }
     218         187 :       for(unsigned i=0; i<natoms; i++) {
     219         616 :         for(unsigned k=0; k<3; ++k) {
     220         462 :           direction[i][k] = myval->getForce( (3*current+k)*natoms + i ) - comforce[k];
     221             :         }
     222             :       }
     223             :     }
     224         646 :     return r;
     225      192350 :   } else if( displacement ) {
     226       55589 :     const Value* myval = getConstPntrToComponent(1);
     227       55589 :     Tensor rot;
     228             :     Matrix<std::vector<Vector> > DRotDPos(3,3);
     229       55589 :     std::vector<Vector> centeredpos( natoms ), centeredreference( natoms );
     230       55589 :     double r = myrmsd[current].calc_PCAelements( pos, der, rot, DRotDPos, direction, centeredpos, centeredreference, squared );
     231       55589 :     std::vector<Vector> ref( myrmsd[current].getReference() );
     232       55589 :     if( !doNotCalculateDerivatives() && myval->forcesWereAdded() ) {
     233        1671 :       Tensor trot=rot.transpose();
     234        1671 :       double prefactor = 1 / static_cast<double>( natoms );
     235        1671 :       Vector v1;
     236        1671 :       v1.zero();
     237       22573 :       for(unsigned n=0; n<natoms; n++) {
     238       20902 :         Vector ff;
     239       83608 :         for(unsigned k=0; k<3; ++k ) {
     240       62706 :           ff[k] = myval->getForce( (3*current+k)*natoms + n );
     241             :         }
     242       20902 :         v1+=prefactor*matmul(trot,ff);
     243             :       }
     244             :       // Notice that we use centreredreference here to accumulate the true forces
     245       22573 :       for(unsigned n=0; n<natoms; n++) {
     246       20902 :         Vector ff;
     247       83608 :         for(unsigned k=0; k<3; ++k ) {
     248       62706 :           ff[k] = myval->getForce( (3*current+k)*natoms + n );
     249             :         }
     250       20902 :         centeredreference[n] = sqrtdisplace[n]*( matmul(trot,ff) - v1 );
     251             :       }
     252        6684 :       for(unsigned a=0; a<3; a++) {
     253       20052 :         for(unsigned b=0; b<3; b++) {
     254             :           double tmp1=0.;
     255      203157 :           for(unsigned m=0; m<natoms; m++) {
     256      188118 :             tmp1+=centeredpos[m][b]*myval->getForce( (3*current+a)*natoms + m );
     257             :           }
     258      203157 :           for(unsigned i=0; i<natoms; i++) {
     259      188118 :             centeredreference[i] += sqrtdisplace[i]*tmp1*DRotDPos[a][b][i];
     260             :           }
     261             :         }
     262             :       }
     263             :       // Now subtract the current force and add on the true force
     264       22573 :       for(unsigned n=0; n<natoms; n++) {
     265       83608 :         for(unsigned k=0; k<3; ++k) {
     266       62706 :           direction[n][k] = centeredreference[n][k];
     267             :         }
     268             :       }
     269             :     } else {
     270      759226 :       for(unsigned i=0; i<direction.size(); ++i) {
     271      705308 :         direction[i] = sqrtdisplace[i]*( direction[i] - ref[i] );
     272             :       }
     273             :     }
     274             :     return r;
     275             :   }
     276      136761 :   return myrmsd[current].calculate( pos, der, squared );
     277             : }
     278             : 
     279             : // calculator
     280       17182 : void RMSDVector::calculate() {
     281       17182 :   if( firststep || !getPntrToArgument(1)->isConstant() ) {
     282       10608 :     setReferenceConfigurations();
     283       10608 :     firststep=false;
     284             :   }
     285             : 
     286       17182 :   if( getPntrToComponent(0)->getRank()==0 ) {
     287       11796 :     unsigned natoms = getPntrToArgument(0)->getShape()[0] / 3;
     288       11796 :     std::vector<Vector> pos( natoms ), der( natoms ), direction( natoms );
     289       11796 :     double r = calculateRMSD( 0, pos, der, direction );
     290             : 
     291       11796 :     getPntrToComponent(0)->set( r );
     292       11796 :     if( getNumberOfComponents()==2 ) {
     293       11775 :       Value* mydisp = getPntrToComponent(1);
     294      168204 :       for(unsigned i=0; i<natoms; i++) {
     295      625716 :         for(unsigned j=0; j<3; ++j ) {
     296      469287 :           mydisp->set( j*natoms+i, direction[i][j] );
     297             :         }
     298             :       }
     299             :     }
     300       11796 :     if( doNotCalculateDerivatives() ) {
     301             :       return;
     302             :     }
     303             : 
     304         612 :     Value* myval = getPntrToComponent(0);
     305        7472 :     for(unsigned i=0; i<natoms; i++) {
     306       27440 :       for(unsigned j=0; j<3; ++j ) {
     307       20580 :         myval->setDerivative( j*natoms+i, der[i][j] );
     308             :       }
     309             :     }
     310             :   } else {
     311        5386 :     runAllTasks();
     312             :   }
     313             : }
     314             : 
     315      160524 : bool RMSDVector::checkForTaskForce( const unsigned& itask, const Value* myval ) const {
     316      160524 :   if( myval->getRank()<2 ) {
     317      137592 :     return ActionWithVector::checkForTaskForce( itask, myval );
     318             :   }
     319       22932 :   unsigned nelements = myval->getShape()[1], startr = itask*nelements;
     320      874692 :   for(unsigned j=0; j<nelements; ++j ) {
     321      852852 :     if( fabs( myval->getForce( startr + j ) )>epsilon ) {
     322             :       return true;
     323             :     }
     324             :   }
     325             :   return false;
     326             : }
     327             : 
     328       17162 : void RMSDVector::apply() {
     329       17162 :   if( doNotCalculateDerivatives() ) {
     330             :     return;
     331             :   }
     332             : 
     333        4980 :   if( getPntrToComponent(0)->getRank()==0 ) {
     334         612 :     std::vector<double> forces( getNumberOfDerivatives(), 0 );
     335         612 :     bool wasforced = getPntrToComponent(0)->applyForce( forces );
     336             : 
     337         612 :     if( getNumberOfComponents()==2 && getPntrToComponent(1)->forcesWereAdded() ) {
     338         612 :       unsigned natoms = getPntrToArgument(0)->getShape()[0] / 3;
     339         612 :       std::vector<Vector> pos( natoms ), der( natoms ), direction( natoms );
     340         612 :       double r = calculateRMSD( 0, pos, der, direction );
     341        7472 :       for(unsigned i=0; i<natoms; ++i) {
     342       27440 :         for(unsigned j=0; j<3; ++j ) {
     343       20580 :           forces[j*natoms+i] += direction[i][j];
     344             :         }
     345             :       }
     346             :       wasforced=true;
     347             :     }
     348           0 :     if( wasforced ) {
     349         612 :       unsigned ss=0;
     350         612 :       addForcesOnArguments( 0, forces, ss, getLabel() );
     351             :     }
     352             :   } else {
     353        4368 :     ActionWithVector::apply();
     354             :   }
     355             : }
     356             : 
     357      180588 : void RMSDVector::performTask( const unsigned& current, MultiValue& myvals ) const {
     358      180588 :   unsigned natoms = getPntrToArgument(0)->getShape()[0] / 3;
     359             :   std::vector<Vector>& pos( myvals.getFirstAtomVector() );
     360             :   std::vector<std::vector<Vector> > & allder( myvals.getFirstAtomDerivativeVector() );
     361      180588 :   if( allder.size()!=2 ) {
     362       14048 :     allder.resize(2);
     363             :   }
     364             :   std::vector<Vector>& der( allder[0] );
     365             :   std::vector<Vector>& direction( allder[1] );
     366      180588 :   if( pos.size()!=natoms ) {
     367       14048 :     pos.resize( natoms );
     368       14048 :     der.resize( natoms );
     369       14048 :     direction.resize( natoms );
     370             :   }
     371     2528232 :   for(unsigned i=0; i<pos.size(); ++i) {
     372     9390576 :     for(unsigned j=0; j<3; ++j) {
     373     7042932 :       pos[i][j] = getPntrToArgument(0)->get( j*natoms + i );
     374             :     }
     375             :   }
     376      180588 :   double r = calculateRMSD( current, pos, der, direction );
     377      180588 :   unsigned ostrn = getConstPntrToComponent(0)->getPositionInStream();
     378      180588 :   myvals.setValue( ostrn, r );
     379             : 
     380      180588 :   if( doNotCalculateDerivatives() ) {
     381             :     return;
     382             :   }
     383             : 
     384     1929648 :   for(unsigned i=0; i<natoms; i++) {
     385     7167264 :     for(unsigned j=0; j<3; ++j ) {
     386     5375448 :       myvals.addDerivative( ostrn, j*natoms+i, der[i][j] );
     387     5375448 :       myvals.updateIndex( ostrn, j*natoms+i );
     388             :     }
     389             :   }
     390             : }
     391             : 
     392      202902 : void RMSDVector::gatherStoredValue( const unsigned& valindex, const unsigned& code, const MultiValue& myvals,
     393             :                                     const unsigned& bufstart, std::vector<double>& buffer ) const {
     394      202902 :   if( getConstPntrToComponent(valindex)->getRank()==1 ) {
     395      160146 :     ActionWithVector::gatherStoredValue( valindex, code, myvals, bufstart, buffer );
     396      160146 :     return;
     397             :   }
     398             :   const std::vector<Vector>& direction( myvals.getConstFirstAtomDerivativeVector()[1] );
     399       42756 :   unsigned natoms = direction.size();
     400       42756 :   unsigned vindex = bufstart + 3*code*natoms;
     401      598584 :   for(unsigned i=0; i<natoms; ++i) {
     402     2223312 :     for(unsigned j=0; j<3; ++j ) {
     403     1667484 :       buffer[vindex + j*natoms + i] += direction[i][j];
     404             :     }
     405             :   }
     406             : }
     407             : 
     408       20442 : void RMSDVector::gatherForcesOnStoredValue( const Value* myval, const unsigned& itask, const MultiValue& myvals, std::vector<double>& forces ) const {
     409       20442 :   if( myval->getRank()==1 ) {
     410       19350 :     ActionWithVector::gatherForcesOnStoredValue( myval, itask, myvals, forces );
     411       19350 :     return;
     412             :   }
     413             :   const std::vector<Vector>& direction( myvals.getConstFirstAtomDerivativeVector()[1] );
     414        1092 :   unsigned natoms = direction.size();
     415       15288 :   for(unsigned i=0; i<natoms; ++i) {
     416       56784 :     for(unsigned j=0; j<3; ++j ) {
     417       42588 :       forces[j*natoms+i] += direction[i][j];
     418             :     }
     419             :   }
     420             : }
     421             : 
     422             : 
     423             : }
     424             : }
     425             : 
     426             : 
     427             : 

Generated by: LCOV version 1.16