LCOV - code coverage report
Current view: top level - mapping - GeometricPath.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 85 87 97.7 %
Date: 2026-11-05 10:19:04 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016,2017 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/ActionWithVector.h"
      23             : #include "core/ActionRegister.h"
      24             : #include "core/ActionSet.h"
      25             : #include "PathProjectionCalculator.h"
      26             : 
      27             : //+PLUMEDOC COLVAR GEOMETRIC_PATH
      28             : /*
      29             : Distance along and from a path calculated using geometric formulas
      30             : 
      31             : The Path Collective Variables developed by Branduardi and that are described in the first paper that is cited below allow one
      32             : to compute the progress along a high-dimensional path and the distance from the high-dimensional path.  The method introduced in that
      33             : paper is implemented in the shortcut [PATH](PATH.md). This action provides an implementation of the alternative method for calculating
      34             : the position on and distance from the path that was proposed by Diaz Leines and Ensing in the second paper that is cited below.  In their
      35             : method, the progress along the path $s$ is calculated using:
      36             : 
      37             : $$
      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             : $$
      40             : 
      41             : where $\mathbf{v}_1$ and $\mathbf{v}_3$ are the vectors connecting the current position to the closest and second closest node of the path,
      42             : respectfully and $i_1$ and $i_2$ are the projections of the closest and second closest frames of the path. $\mathbf{v}_2$, meanwhile, is the
      43             : vector connecting the closest frame to the second closest frame.  The distance from the path, $z$ is calculated using:
      44             : 
      45             : $$
      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             : $$
      48             : 
      49             : The symbols here are as they were for $s$.
      50             : 
      51             : Please note that the shortcut [GPATH](GPATH.md) provides an interface to this action that is less flexible than using this action directly but that is easier to use.
      52             : 
      53             : ## Examples
      54             : 
      55             : The example input below shows how to use this action
      56             : 
      57             : ```plumed
      58             : #SETTINGS INPUTFILES=regtest/mapping/rt-adapt/mypath.pdb
      59             : # Calculate the instantaneous position in the high dimensional space
      60             : d1: DISTANCE ATOMS=1,2 COMPONENTS
      61             : # Read in the refence coordinates from the path
      62             : ref_d1x: PDB2CONSTANT REFERENCE=regtest/mapping/rt-adapt/mypath.pdb ARG=d1.x
      63             : ref_d1y: PDB2CONSTANT REFERENCE=regtest/mapping/rt-adapt/mypath.pdb ARG=d1.y
      64             : path: CONSTANT VALUES=-0.4,-0.3,-0.2,-0.1,0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2,1.3,1.4,1.5
      65             : # Now calculate the differences between our instantaneous position and the milestones on the high dimensional space
      66             : d1_dataP: DISPLACEMENT ARG2=d1.x,d1.y ARG1=ref_d1x,ref_d1y
      67             : # We need the vectors pointing from the reference positions to the instantaneous positions here which is
      68             : # minus the vector calculated above. Remember we can only specify the vectors in ARG1 for the DISPLACEMENT command
      69             : d1_data: CUSTOM ARG=d1_dataP FUNC=-x PERIODIC=NO
      70             : # And finally the path CV
      71             : pp: GEOMETRIC_PATH ARG=d1_data METRIC={DIFFERENCE} REFERENCE=ref_d1x,ref_d1y PROPERTY=path
      72             : PRINT ARG=d1.x,d1.y,pp.* FILE=colvar
      73             : ```
      74             : 
      75             : The curved path here is defined using a series of points in a two dimensional space.  The input above includes actions ([PDB2CONSTANT](PDB2CONSTANT.md)) that set up
      76             : constant values that hold the values of the arguments for each of the milestones on the path.  A [DISPLACEMENT](DISPLACEMENT.md) shortcut is then used to calculate the vectors
      77             : connecting each of the path milestones to the instantenous coordinates of the system in the high dimensional space.  Notice, how the METRIC keyword is used in the input
      78             : to GEOMETRIC_PATH.  This keyword is necessary as the vectors connecting adjacent milestones on the path appear in the equations for $s$ and $z$ above.  The METRIC keyword tells
      79             : this action how distances between frames should be calculated.  There are essentially two options that we have tested here.  You can use [DIFFERENCE](DIFFERENCE.md) as has been done above.  In that
      80             : case the assumption is the path milestones are vectors of coordinates and the difference $\mathbf{v}$ between two of them, $\mathbf{m}_1$ and $\mathbf{m}_2$, can be calculated as:
      81             : 
      82             : $$
      83             : \mathbf{v} = \mathbf{m}_1 - \mathbf{m}_2
      84             : $$
      85             : 
      86             : with periodic variables treated appropriately.  Alternatively, the path milestones might be the positions of atoms and you may want to calculate differences between
      87             : them using an [RMSD](RMSD.md) action as has been done in the example below.
      88             : 
      89             : ```plumed
      90             : ##SETTINGS INPUTFILES=regtest/trajectories/path_msd/all.pdb
      91             : # Positions for all the frames in the path
      92             : path: CONSTANT VALUES=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42
      93             : # Calculate the vectors connecting the instantaneous position to the path milesones
      94             : vecs: RMSD DISPLACEMENT SQUARED REFERENCE=regtest/trajectories/path_msd/all.pdb TYPE=OPTIMAL
      95             : p1b: GEOMETRIC_PATH ARG=vecs.disp REFERENCE=vecs_ref METRIC={RMSD DISPLACEMENT TYPE=OPTIMAL ALIGN=1,1,1,1,1,1,1,1,1,1,1,1,1 DISPLACE=1,1,1,1,1,1,1,1,1,1,1,1,1} PROPERTY=path METRIC_COMPONENT=disp
      96             : PRINT ARG=p1b.* FILE=colvar_b STRIDE=1
      97             : ```
      98             : 
      99             : Here an [RMSD](RMSD.md) action with label `vecs` is used to calculate the vector connecting the instantaneous position and each of the path milestones.  These vectors thus describe
     100             : how far each atom has been displaced in going from one structure to the other in a way that neglects translation of the center of mass and rotation of the reference frame.  When we
     101             : compute the formula aboves, which include the vectors that connect various milestone configurations on our path we must compute the vector connecting the milestones using the same
     102             : approach. The input associated with the METRIC keyword is thus the same input as was used in the RMSD action that was used to calculate the vectors connecting the milestones to the
     103             : instantaneous configuration.  Furthermore, we must also use METRIC_COMPONENT here to specify that it is the `disp` component of the [RMSD](RMSD.md) action that contains the vector of
     104             : interest.
     105             : 
     106             : It is worth noting that the METRIC keyword in the inputs above can accept inputs for one or multiple PLUMED actions.  There is thus flexibility to implement more complicated variants on the geometric path
     107             : implemented here by using the input for different actions as input to the METRIC keyword. If you are interested in using these features you can look at the code within `src/mapping/PathProjectionCalculator.h
     108             : and `src/mapping/PathProjectionCalculator.cpp` to see how this method called a separate PLUMED instance to calculate the vectors connecting the various milesones in the path.
     109             : 
     110             : 
     111             : */
     112             : //+ENDPLUMEDOC
     113             : 
     114             : namespace PLMD {
     115             : namespace mapping {
     116             : 
     117             : class GeometricPath : public ActionWithVector {
     118             : private:
     119             :   PathProjectionCalculator path_projector;
     120             : public:
     121             :   static void registerKeywords(Keywords& keys);
     122             :   explicit GeometricPath(const ActionOptions&);
     123             :   unsigned getNumberOfDerivatives() override ;
     124             :   void calculate() override ;
     125             : };
     126             : 
     127             : PLUMED_REGISTER_ACTION(GeometricPath,"GEOMETRIC_PATH")
     128             : 
     129          22 : void GeometricPath::registerKeywords(Keywords& keys) {
     130          22 :   ActionWithVector::registerKeywords(keys);
     131          22 :   PathProjectionCalculator::registerKeywords(keys);
     132          44 :   keys.addInputKeyword("compulsory","PROPERTY","vector","the label of a value that contains the coordinates we are projecting these points onto");
     133          44 :   keys.addOutputComponent("s","default","scalar","the position on the path");
     134          44 :   keys.addOutputComponent("z","default","scalar","the distance from the path");
     135          22 :   keys.addDOI("10.1063/1.2432340");
     136          22 :   keys.addDOI("10.1103/PhysRevLett.109.020601");
     137          22 :   keys.needsAction("GEOMETRIC_PATH");
     138          22 :   keys.needsAction("PDB2CONSTANT");
     139          22 : }
     140             : 
     141           9 : GeometricPath::GeometricPath(const ActionOptions&ao):
     142             :   Action(ao),
     143             :   ActionWithVector(ao),
     144           9 :   path_projector(this) {
     145             :   // Get the coordinates in the low dimensional space
     146           9 :   std::vector<std::string> pcoord;
     147           9 :   parseVector("PROPERTY", pcoord );
     148           9 :   std::vector<Value*> theprop;
     149           9 :   ActionWithArguments::interpretArgumentList( pcoord, plumed.getActionSet(), this, theprop );
     150           9 :   if( theprop.size()!=1 ) {
     151           0 :     error("did not find property to project on");
     152             :   }
     153           9 :   if( theprop[0]->getNumberOfValues()!=getPntrToArgument(0)->getShape()[0] ) {
     154           0 :     error("mismatch between number of frames and property of interest");
     155             :   }
     156           9 :   log.printf("  projecting onto : %s \n", theprop[0]->getName().c_str() );
     157           9 :   std::vector<Value*> args( getArguments() );
     158           9 :   args.push_back( theprop[0] );
     159           9 :   requestArguments( args );
     160             :   // Create the values to store the output
     161          18 :   addComponentWithDerivatives("s");
     162          18 :   componentIsNotPeriodic("s");
     163          18 :   addComponentWithDerivatives("z");
     164          18 :   componentIsNotPeriodic("z");
     165           9 : }
     166             : 
     167        4413 : unsigned GeometricPath::getNumberOfDerivatives() {
     168        4413 :   return getPntrToArgument(0)->getShape()[0]*getPntrToArgument(0)->getShape()[1] + getPntrToArgument(1)->getShape()[0];
     169             : }
     170             : 
     171        4395 : void GeometricPath::calculate() {
     172        4395 :   unsigned k=0, iclose1=0, iclose2=0;
     173             :   double v1v1=0, v3v3=0;
     174        4395 :   unsigned nrows = getPntrToArgument(0)->getShape()[0];
     175        4395 :   unsigned ncols = getPntrToArgument(0)->getShape()[1];
     176      160555 :   for(unsigned i=0; i<nrows; ++i) {
     177             :     double dist = 0;
     178     4959540 :     for(unsigned j=0; j<ncols; ++j) {
     179     4803380 :       double tmp = getPntrToArgument(0)->get(k);
     180     4803380 :       dist += tmp*tmp;
     181     4803380 :       k++;
     182             :     }
     183      156160 :     if( i==0 ) {
     184             :       v1v1 = dist;
     185        4395 :       iclose1 = 0;
     186      151765 :     } else if( dist<v1v1 ) {
     187             :       v3v3=v1v1;
     188             :       v1v1=dist;
     189       60282 :       iclose2=iclose1;
     190       60282 :       iclose1=i;
     191       91483 :     } else if( i==1 ) {
     192             :       v3v3=dist;
     193         489 :       iclose2=1;
     194       90994 :     } else if( dist<v3v3 ) {
     195             :       v3v3=dist;
     196        2848 :       iclose2=i;
     197             :     }
     198             :   }
     199             :   // And find third closest point
     200        4395 :   int isign = iclose1 - iclose2;
     201             :   if( isign>1 ) {
     202             :     isign=1;
     203        4395 :   } else if( isign<-1 ) {
     204             :     isign=-1;
     205             :   }
     206        4395 :   int iclose3 = iclose1 + isign;
     207        4395 :   unsigned ifrom=iclose1, ito=iclose3;
     208        4395 :   if( iclose3<0 || static_cast<unsigned>(iclose3)>=nrows ) {
     209         345 :     ifrom=iclose2;
     210         345 :     ito=iclose1;
     211             :   }
     212             : 
     213             :   // And calculate projection of vector connecting current point to closest frame on vector connecting nearest two frames
     214        4395 :   std::vector<double> displace;
     215        4395 :   path_projector.getDisplaceVector( ifrom, ito, displace );
     216             :   double v2v2=0, v1v2=0;
     217        4395 :   k=ncols*iclose1;
     218      151861 :   for(unsigned i=0; i<displace.size(); ++i) {
     219      147466 :     v2v2 += displace[i]*displace[i];
     220      147466 :     v1v2 += displace[i]*getPntrToArgument(0)->get(k+i);
     221             :   }
     222             : 
     223             :   // This computes s value
     224        4395 :   double spacing = getPntrToArgument(1)->get(iclose1) - getPntrToArgument(1)->get(iclose2);
     225        4395 :   double root = sqrt( v1v2*v1v2 - v2v2 * ( v1v1 - v3v3) );
     226        4395 :   double dx = 0.5 * ( (root + v1v2) / v2v2 - 1.);
     227        4395 :   double path_s = getPntrToArgument(1)->get(iclose1) + spacing * dx;
     228        4395 :   Value* sp = getPntrToComponent(0);
     229             :   sp->set( path_s );
     230        4395 :   if( !doNotCalculateDerivatives() ) {
     231       45318 :     for(unsigned i=0; i<ncols; ++i) {
     232       43680 :       sp->addDerivative( ncols*iclose1 + i, 0.5*spacing*(v1v2*displace[i]/v2v2 - getPntrToArgument(0)->get(ncols*iclose1 + i))/root + 0.5*spacing*displace[i]/v2v2 );
     233       43680 :       sp->addDerivative( ncols*iclose2 + i, 0.5*spacing*getPntrToArgument(0)->get(ncols*iclose2 + i)/root );
     234             :     }
     235             :   }
     236             : 
     237             :   // This computes z value
     238        4395 :   path_projector.getDisplaceVector( iclose2, iclose1, displace );
     239             :   double v4v4=0, proj=0;
     240        4395 :   k=ncols*iclose1;
     241      151861 :   for(unsigned i=0; i<displace.size(); ++i) {
     242      147466 :     v4v4 += displace[i]*displace[i];
     243      147466 :     proj += displace[i]*getPntrToArgument(0)->get(k+i);
     244             :   }
     245        4395 :   double path_z = v1v1 + dx*dx*v4v4 - 2*dx*proj;
     246        4395 :   path_z = sqrt(path_z);
     247        4395 :   Value* zp = getPntrToComponent(1);
     248             :   zp->set( path_z );
     249        4395 :   if( !doNotCalculateDerivatives() ) {
     250       45318 :     for(unsigned i=0; i<ncols; ++i) {
     251       43680 :       zp->addDerivative( ncols*iclose1 + i, (1/path_z)*(getPntrToArgument(0)->get(ncols*iclose1 + i) +
     252       43680 :                          (v4v4*dx-proj)*sp->getDerivative(ncols*iclose1 + i)/spacing -
     253       43680 :                          dx*displace[i]) );
     254       43680 :       zp->addDerivative( ncols*iclose2 + i, (v4v4*dx-proj)*sp->getDerivative(ncols*iclose2 + i)/(path_z*spacing) );
     255             :     }
     256             :   }
     257        4395 : }
     258             : 
     259             : }
     260             : }

Generated by: LCOV version 1.16