LCOV - code coverage report
Current view: top level - mapping - AdaptivePath.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 89 92 96.7 %
Date: 2020-11-18 11:20:57 Functions: 14 15 93.3 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-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 "Mapping.h"
      23             : #include "TrigonometricPathVessel.h"
      24             : #include "PathReparameterization.h"
      25             : #include "reference/Direction.h"
      26             : #include "core/ActionRegister.h"
      27             : #include "core/PlumedMain.h"
      28             : 
      29             : //+PLUMEDOC COLVAR ADAPTIVE_PATH
      30             : /*
      31             : Compute path collective variables that adapt to the lowest free energy path connecting states A and B.
      32             : 
      33             : The Path Collective Variables developed by Branduardi and co-workers \cite brand07 allow one
      34             : to compute the progress along a high-dimensional path and the distance from the high-dimensional
      35             : path.  The progress along the path (s) is computed using:
      36             : 
      37             : \f[
      38             : s = i_2 + \textrm{sign}(i_2-i_1) \frac{ \sqrt{( \mathbf{v}_1\cdot\mathbf{v}_2 )^2 - |\mathbf{v}_3|^2(|\mathbf{v}_1|^2 - |\mathbf{v}_2|^2) } }{2|\mathbf{v}_3|^2} - \frac{\mathbf{v}_1\cdot\mathbf{v}_3 - |\mathbf{v}_3|^2}{2|\mathbf{v}_3|^2}
      39             : \f]
      40             : 
      41             : In this expression \f$\mathbf{v}_1\f$ and \f$\mathbf{v}_3\f$ are the vectors connecting the current position to the closest and second closest node of the path,
      42             : respectfully and \f$i_1\f$ and \f$i_2\f$ are the projections of the closest and second closest frames of the path. \f$\mathbf{v}_2\f$, meanwhile, is the
      43             : vector connecting the closest frame to the second closest frame.  The distance from the path, \f$z\f$ is calculated using:
      44             : 
      45             : \f[
      46             : z = \sqrt{ \left[ |\mathbf{v}_1|^2 - |\mathbf{v}_2| \left( \frac{ \sqrt{( \mathbf{v}_1\cdot\mathbf{v}_2 )^2 - |\mathbf{v}_3|^2(|\mathbf{v}_1|^2 - |\mathbf{v}_2|^2) } }{2|\mathbf{v}_3|^2} - \frac{\mathbf{v}_1\cdot\mathbf{v}_3 - |\mathbf{v}_3|^2}{2|\mathbf{v}_3|^2} \right) \right]^2 }
      47             : \f]
      48             : 
      49             : Notice that these are the definitions of \f$s\f$ and \f$z\f$ that are used by \ref PATH when the GPATH option is employed.  The reason for this is that
      50             : the adaptive path method implemented in this action was inspired by the work of Diaz and Ensing in which these formula were used \cite BerndAdaptivePath.
      51             : To learn more about how the path is adapted we strongly recommend reading this paper.
      52             : 
      53             : \par Examples
      54             : 
      55             : The input below provides an example of how the adaptive path works in practise. The path is updated every 50 steps of
      56             : MD based on the data accumulated during the preceding 50 time steps.
      57             : 
      58             : \plumedfile
      59             : d1: DISTANCE ATOMS=1,2 COMPONENTS
      60             : pp: ADAPTIVE_PATH TYPE=EUCLIDEAN FIXED=5,15 UPDATE=50 WFILE=out-path.pdb WSTRIDE=50 REFERENCE=mypath.pdb
      61             : PRINT ARG=d1.x,d1.y,pp.* FILE=colvar
      62             : \endplumedfile
      63             : 
      64             : In the case above the distance between frames is calculated based on the \f$x\f$ and \f$y\f$ components of the vector connecting
      65             : atoms 1 and 2.  As such an extract from the input reference path (mypath.pdb) would look as follows:
      66             : 
      67             : \verbatim
      68             : REMARK ARG=d1.x,d1.y d1.x=1.12 d1.y=-.60
      69             : END
      70             : REMARK ARG=d1.x,d1.y d1.x=.99 d1.y=-.45
      71             : END
      72             : \endverbatim
      73             : 
      74             : Notice that one can also use RMSD frames in place of arguments like those above.
      75             : 
      76             : */
      77             : //+ENDPLUMEDOC
      78             : 
      79             : namespace PLMD {
      80             : namespace mapping {
      81             : 
      82           3 : class AdaptivePath : public Mapping {
      83             : private:
      84             :   OFile pathfile;
      85             :   std::string ofmt;
      86             :   double fadefact, tolerance;
      87             :   unsigned update_str, wstride;
      88             :   std::vector<unsigned> fixedn;
      89             :   TrigonometricPathVessel* mypathv;
      90             :   std::vector<double> wsum;
      91             :   Direction displacement,displacement2;
      92             :   std::vector<Direction> pdisplacements;
      93             : public:
      94             :   static void registerKeywords( Keywords& keys );
      95             :   explicit AdaptivePath(const ActionOptions&);
      96             :   void calculate();
      97             :   void performTask( const unsigned&, const unsigned&, MultiValue& ) const ;
      98         101 :   double getLambda() { return 0.0; }
      99             :   double transformHD( const double& dist, double& df ) const ;
     100             :   void update();
     101             : };
     102             : 
     103        6453 : PLUMED_REGISTER_ACTION(AdaptivePath,"ADAPTIVE_PATH")
     104             : 
     105           2 : void AdaptivePath::registerKeywords( Keywords& keys ) {
     106           4 :   Mapping::registerKeywords( keys ); keys.remove("PROPERTY");
     107           8 :   keys.add("compulsory","FIXED","the positions in the list of input frames of the two path nodes whose positions remain fixed during the path optimization");
     108          10 :   keys.add("compulsory","HALFLIFE","-1","the number of MD steps after which a previously measured path distance weighs only 50% in the average. This option may increase convergence by allowing to \"forget\" the memory of a bad initial guess path. The default is to set this to infinity");
     109           8 :   keys.add("compulsory","UPDATE","the frequency with which the path should be updated");
     110          10 :   keys.add("compulsory","TOLERANCE","1E-6","the tolerance to use for the path updating algorithm that makes all frames equidistant");
     111           8 :   keys.add("optional","WFILE","file on which to write out the path");
     112          10 :   keys.add("compulsory","FMT","%f","the format to use for output files");
     113           8 :   keys.add("optional","WSTRIDE","frequency with which to write out the path");
     114           2 : }
     115             : 
     116           1 : AdaptivePath::AdaptivePath(const ActionOptions& ao):
     117             :   Action(ao),
     118             :   Mapping(ao),
     119             :   fixedn(2),
     120           3 :   displacement(ReferenceConfigurationOptions("DIRECTION")),
     121           7 :   displacement2(ReferenceConfigurationOptions("DIRECTION"))
     122             : {
     123           2 :   setLowMemOption( true ); parseVector("FIXED",fixedn);
     124           2 :   if( fixedn[0]<1 || fixedn[1]>getNumberOfReferencePoints() ) error("fixed nodes must be in range from 0 to number of nodes");
     125           1 :   if( fixedn[0]>=fixedn[1] ) error("invalid selection for fixed nodes first index provided must be smaller than second index");
     126           2 :   log.printf("  fixing position of frames numbered %u and %u \n",fixedn[0],fixedn[1]);
     127           2 :   fixedn[0]--; fixedn[1]--;   // Set fixed notes with c++ indexing starting from zero
     128           2 :   parse("UPDATE",update_str); if( update_str<1 ) error("update frequency for path should be greater than or equal to one");
     129           1 :   log.printf("  updating path every %u MD steps \n",update_str);
     130             : 
     131           2 :   double halflife; parse("HALFLIFE",halflife);
     132           1 :   log.printf("  weight of contribution to frame halves every %f steps \n",halflife);
     133           1 :   if( halflife<0 ) fadefact=1.0;
     134           0 :   else fadefact = exp( -0.693147180559945 / static_cast<double>(halflife) );
     135             : 
     136             :   // Create the list of tasks (and reset projections of frames)
     137           2 :   std::vector<std::string> argument_names( getNumberOfArguments() );
     138           5 :   for(unsigned i=0; i<getNumberOfArguments(); ++i) argument_names[i] = getPntrToArgument(i)->getName();
     139           2 :   displacement.setNamesAndAtomNumbers( getAbsoluteIndexes(), argument_names );
     140           1 :   displacement2.setNamesAndAtomNumbers( getAbsoluteIndexes(), argument_names );
     141          41 :   for(int i=0; i<getNumberOfReferencePoints(); ++i) {
     142          80 :     addTaskToList( i ); pdisplacements.push_back( Direction(ReferenceConfigurationOptions("DIRECTION")) );
     143          20 :     setPropertyValue( i, 0, static_cast<double>( i - static_cast<int>(fixedn[0]) ) / static_cast<double>( fixedn[1] - fixedn[0] ) );
     144          60 :     pdisplacements[i].setNamesAndAtomNumbers( getAbsoluteIndexes(), argument_names ); wsum.push_back( 0.0 );
     145             :   }
     146           2 :   plumed_assert( getPropertyValue( fixedn[0], 0 )==0.0 && getPropertyValue( fixedn[1], 0 )==1.0 );
     147             :   // And activate them all
     148           1 :   deactivateAllTasks();
     149          41 :   for(unsigned i=0; i<getFullNumberOfTasks(); ++i) taskFlags[i]=1;
     150           1 :   lockContributors();
     151             : 
     152             :   // Setup the vessel to hold the trig path
     153           2 :   std::string input; addVessel("GPATH", input, -1 );
     154           1 :   readVesselKeywords();
     155             :   // Check that there is only one vessel - the one holding the trig path
     156             :   plumed_dbg_assert( getNumberOfVessels()==1 );
     157             :   // Retrieve the path vessel
     158           1 :   mypathv = dynamic_cast<TrigonometricPathVessel*>( getPntrToVessel(0) );
     159           1 :   plumed_assert( mypathv );
     160             : 
     161             :   // Information for write out
     162           2 :   std::string wfilename; parse("WFILE",wfilename);
     163           1 :   if( wfilename.length()>0 ) {
     164           3 :     wstride=0; parse("WSTRIDE",wstride); parse("FMT",ofmt);
     165           1 :     pathfile.link(*this); pathfile.open( wfilename ); pathfile.setHeavyFlush();
     166           1 :     if( wstride<update_str ) error("makes no sense to write out path more frequently than update stride");
     167           2 :     log.printf("  writing path out every %u steps to file named %s with format %s \n",wstride,wfilename.c_str(),ofmt.c_str());
     168             :   }
     169           3 :   log<<"  Bibliography "<<plumed.cite("Diaz Leines and Ensing, Phys. Rev. Lett. 109, 020601 (2012)")<<"\n";
     170           1 : }
     171             : 
     172         101 : void AdaptivePath::calculate() {
     173         101 :   runAllTasks();
     174         101 : }
     175             : 
     176        2020 : void AdaptivePath::performTask( const unsigned& task_index, const unsigned& current, MultiValue& myvals ) const {
     177             :   // This builds a pack to hold the derivatives
     178        4040 :   ReferenceValuePack mypack( getNumberOfArguments(), getNumberOfAtoms(), myvals );
     179        2020 :   finishPackSetup( current, mypack );
     180             :   // Calculate the distance from the frame
     181        2020 :   double val=calculateDistanceFunction( current, mypack, true );
     182             :   // Put the element value in element zero
     183             :   myvals.setValue( 0, val ); myvals.setValue( 1, 1.0 );
     184        2020 :   return;
     185             : }
     186             : 
     187        2020 : double AdaptivePath::transformHD( const double& dist, double& df ) const {
     188        2020 :   df=1.0; return dist;
     189             : }
     190             : 
     191         101 : void AdaptivePath::update() {
     192         101 :   double weight2 = -1.*mypathv->dx;
     193         101 :   double weight1 = 1.0 + mypathv->dx;
     194         101 :   if( weight1>1.0 ) {
     195           0 :     weight1=1.0; weight2=0.0;
     196         101 :   } else if( weight2>1.0 ) {
     197           0 :     weight1=0.0; weight2=1.0;
     198             :   }
     199             :   // Add projections to dispalcement accumulators
     200         101 :   ReferenceConfiguration* myref = getReferenceConfiguration( mypathv->iclose1 );
     201         202 :   myref->extractDisplacementVector( getPositions(), getArguments(), mypathv->cargs, false, displacement );
     202         101 :   getReferenceConfiguration( mypathv->iclose2 )->extractDisplacementVector( myref->getReferencePositions(), getArguments(), myref->getReferenceArguments(), false, displacement2 );
     203         101 :   displacement.addDirection( -mypathv->dx, displacement2 );
     204         202 :   pdisplacements[mypathv->iclose1].addDirection( weight1, displacement );
     205         202 :   pdisplacements[mypathv->iclose2].addDirection( weight2, displacement );
     206             :   // Update weight accumulators
     207         202 :   wsum[mypathv->iclose1] *= fadefact;
     208         202 :   wsum[mypathv->iclose2] *= fadefact;
     209         202 :   wsum[mypathv->iclose1] += weight1;
     210         202 :   wsum[mypathv->iclose2] += weight2;
     211             : 
     212             :   // This does the update of the path if it is time to
     213         101 :   if( (getStep()>0) && (getStep()%update_str==0) ) {
     214           8 :     wsum[fixedn[0]]=wsum[fixedn[1]]=0.;
     215          84 :     for(unsigned inode=0; inode<getNumberOfReferencePoints(); ++inode) {
     216          80 :       if( wsum[inode]>0 ) {
     217             :         // First displace the node by the weighted direction
     218          12 :         getReferenceConfiguration( inode )->displaceReferenceConfiguration( 1./wsum[inode], pdisplacements[inode] );
     219             :         // Reset the displacement
     220          12 :         pdisplacements[inode].zeroDirection();
     221             :       }
     222             :     }
     223             :     // Now ensure all the nodes of the path are equally spaced
     224           4 :     PathReparameterization myspacings( getPbc(), getArguments(), getAllReferenceConfigurations() );
     225           4 :     myspacings.reparameterize( fixedn[0], fixedn[1], tolerance );
     226             :   }
     227         101 :   if( (getStep()>0) && (getStep()%wstride==0) ) {
     228           2 :     pathfile.printf("# PATH AT STEP %d TIME %f \n", getStep(), getTime() );
     229             :     std::vector<ReferenceConfiguration*>& myconfs=getAllReferenceConfigurations();
     230         124 :     for(unsigned i=0; i<myconfs.size(); ++i) myconfs[i]->print( pathfile, ofmt, atoms.getUnits().getLength()/0.1 );
     231           2 :     pathfile.flush();
     232             :   }
     233         101 : }
     234             : 
     235             : }
     236        4839 : }

Generated by: LCOV version 1.13