LCOV - code coverage report
Current view: top level - function - FuncPathMSD.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 95 107 88.8 %
Date: 2026-11-05 10:19:04 Functions: 5 6 83.3 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-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             : 
      23             : #include "Function.h"
      24             : #include "core/ActionRegister.h"
      25             : 
      26             : namespace PLMD {
      27             : namespace function {
      28             : 
      29             : //+PLUMEDOC FUNCTION FUNCPATHMSD
      30             : /*
      31             : This function calculates path collective variables.
      32             : 
      33             : This is the Path Collective Variables implementation described in the paper from the bibliography.
      34             : This variable computes the progress along a given set of frames that is provided
      35             : in input ("s" component) and the distance from them ("z" component).
      36             : It is a function of mean squared displacement that are obtained by the joint use of mean squared displacement variables with the SQUARED flag
      37             : (see below).
      38             : 
      39             : ## Examples
      40             : 
      41             : Here below is a case where you have defined three frames and you want to
      42             : calculate the progress along the path and the distance from it in p1
      43             : 
      44             : ```plumed
      45             : #SETTINGS INPUTFILES=regtest/trajectories/path_msd/frame_1.dat,regtest/trajectories/path_msd/frame_21.dat,regtest/trajectories/path_msd/frame_42.dat
      46             : t1: RMSD REFERENCE=regtest/trajectories/path_msd/frame_1.dat TYPE=OPTIMAL SQUARED
      47             : t2: RMSD REFERENCE=regtest/trajectories/path_msd/frame_21.dat TYPE=OPTIMAL SQUARED
      48             : t3: RMSD REFERENCE=regtest/trajectories/path_msd/frame_42.dat TYPE=OPTIMAL SQUARED
      49             : p1: FUNCPATHMSD ARG=t1,t2,t3 LAMBDA=500.0
      50             : PRINT ARG=t1,t2,t3,p1.s,p1.z STRIDE=1 FILE=colvar FMT=%8.4f
      51             : ```
      52             : 
      53             : You can see that the reference coordinates here are described in three separate pdb files that you view above.
      54             : 
      55             : This second example shows how to define a PATH in [CONTACTMAP](CONTACTMAP.md) space:
      56             : 
      57             : ```plumed
      58             : CONTACTMAP ...
      59             : ATOMS1=1,2 REFERENCE1=0.1
      60             : ATOMS2=3,4 REFERENCE2=0.5
      61             : ATOMS3=4,5 REFERENCE3=0.25
      62             : ATOMS4=5,6 REFERENCE4=0.0
      63             : SWITCH={RATIONAL R_0=1.5}
      64             : LABEL=c1
      65             : CMDIST
      66             : ... CONTACTMAP
      67             : 
      68             : CONTACTMAP ...
      69             : ATOMS1=1,2 REFERENCE1=0.3
      70             : ATOMS2=3,4 REFERENCE2=0.9
      71             : ATOMS3=4,5 REFERENCE3=0.45
      72             : ATOMS4=5,6 REFERENCE4=0.1
      73             : SWITCH={RATIONAL R_0=1.5}
      74             : LABEL=c2
      75             : CMDIST
      76             : ... CONTACTMAP
      77             : 
      78             : CONTACTMAP ...
      79             : ATOMS1=1,2 REFERENCE1=1.0
      80             : ATOMS2=3,4 REFERENCE2=1.0
      81             : ATOMS3=4,5 REFERENCE3=1.0
      82             : ATOMS4=5,6 REFERENCE4=1.0
      83             : SWITCH={RATIONAL R_0=1.5}
      84             : LABEL=c3
      85             : CMDIST
      86             : ... CONTACTMAP
      87             : 
      88             : p1: FUNCPATHMSD ARG=c1,c2,c3 LAMBDA=500.0
      89             : PRINT ARG=c1,c2,c3,p1.s,p1.z STRIDE=1 FILE=colvar FMT=%8.4f
      90             : ```
      91             : 
      92             : This third example shows how to define a PATH in [PIV](PIV.md) space:
      93             : 
      94             : ```plumed
      95             : #SETTINGS INPUTFILES=regtest/piv/rt-piv-distance/Liq.pdb,regtest/piv/rt-piv-distance/Ice.pdb
      96             : PIV ...
      97             : LABEL=c1
      98             : PRECISION=1000
      99             : NLIST
     100             : REF_FILE=regtest/piv/rt-piv-distance/Liq.pdb
     101             : PIVATOMS=2
     102             : ATOMTYPES=A,B
     103             : ONLYDIRECT
     104             : SFACTOR=1.0,0.2
     105             : SORT=1,1
     106             : SWITCH1={RATIONAL R_0=0.6 MM=12 NN=4}
     107             : SWITCH2={RATIONAL R_0=0.5 MM=10 NN=5}
     108             : NL_CUTOFF=1.2,1.2
     109             : NL_STRIDE=10,10
     110             : NL_SKIN=0.1,0.1
     111             : ... PIV
     112             : PIV ...
     113             : LABEL=c2
     114             : PRECISION=1000
     115             : NLIST
     116             : REF_FILE=regtest/piv/rt-piv-distance/Ice.pdb
     117             : PIVATOMS=2
     118             : ATOMTYPES=A,B
     119             : ONLYDIRECT
     120             : SFACTOR=1.0,0.2
     121             : SORT=1,1
     122             : SWITCH1={RATIONAL R_0=0.6 MM=12 NN=4}
     123             : SWITCH2={RATIONAL R_0=0.4 MM=10 NN=5}
     124             : NL_CUTOFF=1.2,1.2
     125             : NL_STRIDE=10,10
     126             : NL_SKIN=0.1,0.1
     127             : ... PIV
     128             : 
     129             : p1: FUNCPATHMSD ARG=c1,c2 LAMBDA=0.180338
     130             : METAD ARG=p1.s,p1.z SIGMA=0.01,0.2 HEIGHT=0.8 PACE=500   LABEL=res
     131             : PRINT ARG=c1,c2,p1.s,p1.z,res.bias STRIDE=500  FILE=colvar FMT=%15.6f
     132             : ```
     133             : 
     134             : */
     135             : //+ENDPLUMEDOC
     136             : 
     137             : class FuncPathMSD : public Function {
     138             :   double lambda;
     139             :   int neigh_size;
     140             :   double neigh_stride;
     141             :   std::vector< std::pair<Value *,double> > neighpair;
     142             :   std::map<Value *,double > indexmap; // use double to allow isomaps
     143             : /// the index (as in indexmap) of each element of neighpair, so the map is not searched at every step
     144             :   std::vector<double> neighindex;
     145             :   std::vector <Value*> allArguments;
     146             : /// rebuild neighindex after neighpair has changed
     147             :   void updateNeighIndex();
     148             : // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     149             : // this below is useful when one wants to sort a vector of double and have back the order
     150             : // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     151             : // create a custom sorter
     152             :   typedef std::vector<double>::const_iterator myiter;
     153             :   struct ordering {
     154             :     bool operator ()(std::pair<unsigned, myiter> const& a, std::pair<unsigned, myiter> const& b) {
     155             :       return *(a.second) < *(b.second);
     156             :     }
     157             :   };
     158             : // sorting utility
     159             :   std::vector<int> increasingOrder( std::vector<double> &v) {
     160             :     // make a pair
     161             :     std::vector< std::pair<unsigned, myiter> > order(v.size());
     162             :     unsigned n = 0;
     163             :     for (myiter it = v.begin(); it != v.end(); ++it, ++n) {
     164             :       order[n] = make_pair(n, it); // note: heere i do not put the values but the addresses that point to the value
     165             :     }
     166             :     // now sort according the second value
     167             :     std::sort(order.begin(), order.end(), ordering());
     168             :     std::vector<int> vv(v.size());
     169             :     n=0;
     170             :     for (const auto & it : order) {
     171             :       vv[n]=it.first;
     172             :       n++;
     173             :     }
     174             :     return vv;
     175             :   }
     176             : // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     177             : // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     178             : 
     179             :   struct pairordering {
     180             :     bool operator ()(std::pair<Value *, double> const& a, std::pair<Value*, double> const& b) {
     181         452 :       return (a).second > (b).second;
     182             :     }
     183             :   };
     184             : 
     185             : public:
     186             :   explicit FuncPathMSD(const ActionOptions&);
     187             : // active methods:
     188             :   void calculate() override;
     189             :   void prepare() override;
     190             :   static void registerKeywords(Keywords& keys);
     191             : };
     192             : 
     193             : PLUMED_REGISTER_ACTION(FuncPathMSD,"FUNCPATHMSD")
     194             : 
     195           8 : void FuncPathMSD::registerKeywords(Keywords& keys) {
     196           8 :   Function::registerKeywords(keys);
     197           8 :   keys.add("compulsory","LAMBDA","the lambda parameter is needed for smoothing, is in the units of plumed");
     198           8 :   keys.add("optional","NEIGH_SIZE","size of the neighbor list");
     199           8 :   keys.add("optional","NEIGH_STRIDE","how often the neighbor list needs to be calculated in time units");
     200          16 :   keys.addOutputComponent("s","default","scalar","the position on the path");
     201          16 :   keys.addOutputComponent("z","default","scalar","the distance from the path");
     202           8 :   keys.addDOI("10.1063/1.2432340");
     203           8 : }
     204           4 : FuncPathMSD::FuncPathMSD(const ActionOptions&ao):
     205             :   Action(ao),
     206             :   Function(ao),
     207           4 :   neigh_size(-1),
     208           4 :   neigh_stride(-1.) {
     209             : 
     210           4 :   parse("LAMBDA",lambda);
     211           4 :   parse("NEIGH_SIZE",neigh_size);
     212           4 :   parse("NEIGH_STRIDE",neigh_stride);
     213           4 :   checkRead();
     214           4 :   log.printf("  lambda is %f\n",lambda);
     215           4 :   if( !(lambda>0) ) {
     216           0 :     error("LAMBDA should be positive");
     217             :   }
     218           4 :   if( neigh_size>0 && !(neigh_stride>0) ) {
     219           0 :     error("with NEIGH_SIZE you should also give a positive NEIGH_STRIDE");
     220             :   }
     221             :   // list the action involved and check the type
     222           4 :   std::string myname=getPntrToArgument(0)->getPntrToAction()->getName();
     223             :   // DISTANCE is a shortcut: the scalar distance is computed by DISTANCE_SCALAR
     224           4 :   if(myname!="RMSD_SCALAR"&&myname!="CONTACTMAP"&&myname!="DISTANCE"&&myname!="DISTANCE_SCALAR"&&myname!="PIV") {
     225           0 :     error("One or more of your arguments is not of RMSD/CONTACTMAP/DISTANCE/PIV type!!!");
     226             :   }
     227          14 :   for(unsigned i=1; i<getNumberOfArguments(); i++) {
     228             :     // for each value get the name and the label of the corresponding action
     229          10 :     if( getPntrToArgument(i)->getPntrToAction()->getName()!=myname ) {
     230           0 :       error("mismatch between the types of arguments");
     231             :     }
     232             :   }
     233           4 :   log.printf("  Consistency check completed! Your path cvs look good!\n");
     234             :   // do some neighbor printout
     235           4 :   if(neigh_size>0) {
     236           2 :     if(neigh_size>static_cast<int>(getNumberOfArguments())) {
     237           0 :       log.printf(" List size required ( %d ) is too large: resizing to the maximum number of arg required: %d  \n",neigh_size,getNumberOfArguments());
     238           0 :       neigh_size=getNumberOfArguments();
     239             :     }
     240           2 :     log.printf("  Neighbor list enabled: \n");
     241           2 :     log.printf("                size   :  %d elements\n",neigh_size);
     242           2 :     log.printf("                stride :  %f time \n",neigh_stride);
     243             :   } else {
     244           2 :     log.printf("  Neighbor list NOT enabled \n");
     245             :   }
     246             : 
     247           8 :   addComponentWithDerivatives("s");
     248           8 :   componentIsNotPeriodic("s");
     249           8 :   addComponentWithDerivatives("z");
     250           4 :   componentIsNotPeriodic("z");
     251             : 
     252             :   // now backup the arguments
     253          18 :   for(unsigned i=0; i<getNumberOfArguments(); i++) {
     254          14 :     allArguments.push_back(getPntrToArgument(i));
     255             :   }
     256             :   double i=1.;
     257          18 :   for(const auto & it : allArguments) {
     258          14 :     indexmap[it]=i;
     259          14 :     i+=1.;
     260             :   }
     261             : 
     262           4 : }
     263        1102 : void FuncPathMSD::updateNeighIndex() {
     264        1102 :   neighindex.resize(neighpair.size());
     265        4005 :   for(unsigned i=0; i<neighpair.size(); ++i) {
     266        2903 :     neighindex[i]=indexmap[neighpair[i].first];
     267             :   }
     268        1102 : }
     269             : 
     270             : // calculator
     271        1102 : void FuncPathMSD::calculate() {
     272             :   double s_path=0.;
     273             :   double partition=0.;
     274        1102 :   if(neighpair.empty()) { // at first step, resize it
     275           0 :     neighpair.resize(allArguments.size());
     276           0 :     for(unsigned i=0; i<allArguments.size(); i++) {
     277           0 :       neighpair[i].first=allArguments[i];
     278             :     }
     279             :   }
     280        1102 :   if( neighindex.size()!=neighpair.size() ) {
     281           0 :     updateNeighIndex();
     282             :   }
     283             : 
     284        1102 :   Value* val_s_path=getPntrToComponent("s");
     285        2204 :   Value* val_z_path=getPntrToComponent("z");
     286             : 
     287             :   // The weights are computed relative to the closest frame, exp(-lambda*(d_i-d_min)),
     288             :   // so that they do not all underflow to zero far from the path.
     289        1102 :   double dmin=neighpair[0].first->get();
     290        4005 :   for(const auto & it : neighpair) {
     291        3624 :     dmin=std::min(dmin,it.first->get());
     292             :   }
     293        4005 :   for(unsigned i=0; i<neighpair.size(); ++i) {
     294             :     auto & it=neighpair[i];
     295        2903 :     it.second=std::exp(-lambda*(it.first->get()-dmin));
     296        2903 :     s_path+=neighindex[i]*it.second;
     297        2903 :     partition+=it.second;
     298             :   }
     299             :   // the closest frame has weight one, so partition>=1; the check makes this explicit
     300             :   // (and keeps static analysis from flagging the logarithm below)
     301        1102 :   if( !(partition>=1.0) ) {
     302           0 :     error("the weights of the frames of the path are not finite: check LAMBDA and the arguments");
     303             :     return;
     304             :   }
     305        1102 :   s_path/=partition;
     306             :   val_s_path->set(s_path);
     307        1102 :   val_z_path->set(dmin-(1./lambda)*std::log(partition));
     308        4005 :   for(unsigned n=0; n<neighpair.size(); ++n) {
     309        2903 :     double expval=neighpair[n].second;
     310        2903 :     double tmp=lambda*expval*(s_path-neighindex[n])/partition;
     311             :     setDerivative(val_s_path,n,tmp);
     312        2903 :     setDerivative(val_z_path,n,expval/partition);
     313             :   }
     314             : }
     315             : ///
     316             : /// this function updates the needed argument list
     317             : ///
     318        1102 : void FuncPathMSD::prepare() {
     319             : 
     320             :   // neighbor list: rank and activate the chain for the next step
     321             : 
     322             :   // neighbor list: if neigh_size<0 never sort and keep the full vector
     323             :   // neighbor list: if neigh_size>0
     324             :   //                if the size is full -> sort the vector and decide the dependencies for next step
     325             :   //                if the size is not full -> check if next step will need the full dependency otherwise keep this dependencies
     326             : 
     327             :   // here just resize the neighpair. The real resizing of reinit will be done by the prepare stage that will modify the  list of arguments
     328        1102 :   if (neigh_size>0) {
     329         551 :     if(neighpair.size()==allArguments.size()) { // I just did the complete round: need to sort, shorten and give it a go
     330             :       // sort the values
     331         139 :       std::sort(neighpair.begin(),neighpair.end(),pairordering());
     332             :       // resize the effective list
     333         139 :       neighpair.resize(neigh_size);
     334         139 :       log.printf("  NEIGH LIST NOW INCLUDE INDEXES: ");
     335         417 :       for(int i=0; i<neigh_size; ++i) {
     336         278 :         log.printf(" %f ",indexmap[neighpair[i].first]);
     337             :       }
     338         139 :       log.printf(" \n");
     339             :     } else {
     340         412 :       const long long nstride=std::max(1LL,static_cast<long long>(neigh_stride/getTimeStep()));
     341         412 :       if( getStep()%nstride==0 ) {
     342         140 :         log.printf(" Time %f : recalculating full neighlist \n",getStep()*getTimeStep());
     343         140 :         neighpair.resize(allArguments.size());
     344         563 :         for(unsigned i=0; i<allArguments.size(); i++) {
     345         423 :           neighpair[i].first=allArguments[i];
     346             :         }
     347             :       }
     348             :     }
     349             :   } else {
     350         551 :     if( int(getStep())==0) {
     351           2 :       neighpair.resize(allArguments.size());
     352           9 :       for(unsigned i=0; i<allArguments.size(); i++) {
     353           7 :         neighpair[i].first=allArguments[i];
     354             :       }
     355             :     }
     356             :   }
     357        1102 :   std::vector<Value*> argstocall;
     358        1102 :   if(!neighpair.empty()) {
     359        4005 :     for(const auto & it : neighpair) {
     360        2903 :       argstocall.push_back( it.first );
     361             :     }
     362             :   } else {
     363           0 :     argstocall=allArguments;
     364             :   }
     365        1102 :   updateNeighIndex();
     366             : // the list of arguments (and the dependencies) only needs to be updated when it changes
     367        1102 :   if( argstocall!=getArguments() ) {
     368         277 :     requestArguments(argstocall);
     369         831 :     for(unsigned i=0; i< getNumberOfComponents(); i++) {
     370         554 :       getPntrToComponent(i)->clearDerivatives();
     371         554 :       getPntrToComponent(i)->resizeDerivatives(getNumberOfArguments());
     372             :     }
     373             :   }
     374        1102 : }
     375             : 
     376             : }
     377             : }
     378             : 
     379             : 

Generated by: LCOV version 1.16