LCOV - code coverage report
Current view: top level - mapping - TrigonometricPathVessel.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 167 180 92.8 %
Date: 2026-03-30 13:16:06 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-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 "TrigonometricPathVessel.h"
      23             : #include "vesselbase/VesselRegister.h"
      24             : #include "tools/PDB.h"
      25             : 
      26             : namespace PLMD {
      27             : namespace mapping {
      28             : 
      29       13788 : PLUMED_REGISTER_VESSEL(TrigonometricPathVessel,"GPATH")
      30             : 
      31           3 : void TrigonometricPathVessel::registerKeywords( Keywords& keys ) {
      32           3 :   StoreDataVessel::registerKeywords(keys);
      33           3 : }
      34             : 
      35        4595 : void TrigonometricPathVessel::reserveKeyword( Keywords& keys ) {
      36        9190 :   keys.reserve("vessel","GPATH","calculate the position on the path using trigonometry");
      37        9190 :   keys.addOutputComponent("gspath","GPATH","the position on the path calculated using trigonometry");
      38        9190 :   keys.addOutputComponent("gzpath","GPATH","the distance from the path calculated using trigonometry");
      39        4595 : }
      40             : 
      41           3 : TrigonometricPathVessel::TrigonometricPathVessel( const vesselbase::VesselOptions& da ):
      42             :   StoreDataVessel(da),
      43           6 :   projdir(ReferenceConfigurationOptions("DIRECTION")),
      44           3 :   mydpack1( 1, getAction()->getNumberOfDerivatives() ),
      45           3 :   mydpack2( 1, getAction()->getNumberOfDerivatives() ),
      46           3 :   mydpack3( 1, getAction()->getNumberOfDerivatives() ),
      47           3 :   mypack1( 0, 0, mydpack1 ),
      48           3 :   mypack2( 0, 0, mydpack2 ),
      49           9 :   mypack3( 0, 0, mydpack3 ) {
      50           3 :   mymap=dynamic_cast<Mapping*>( getAction() );
      51           3 :   plumed_massert( mymap, "Trigonometric path vessel can only be used with mappings");
      52             :   // Retrieve the index of the property in the underlying mapping
      53           3 :   if( mymap->getNumberOfProperties()!=1 ) {
      54           0 :     error("cannot use trigonometric paths when there are multiple properties");
      55             :   }
      56             : 
      57         125 :   for(unsigned i=0; i<mymap->getFullNumberOfTasks(); ++i) {
      58         122 :     if( mymap->getTaskCode(i)!=mymap->getPositionInFullTaskList(i) ) {
      59           0 :       error("mismatched tasks and codes");
      60             :     }
      61             :   }
      62           3 :   mymap->addComponentWithDerivatives("gspath");
      63           3 :   mymap->componentIsNotPeriodic("gspath");
      64           3 :   sp=mymap->copyOutput( mymap->getNumberOfComponents()-1 );
      65           3 :   sp->resizeDerivatives( mymap->getNumberOfDerivatives() );
      66           3 :   mymap->addComponentWithDerivatives("gzpath");
      67           3 :   mymap->componentIsNotPeriodic("gzpath");
      68           3 :   zp=mymap->copyOutput( mymap->getNumberOfComponents()-1 );
      69           3 :   zp->resizeDerivatives( mymap->getNumberOfDerivatives() );
      70             : 
      71             :   // Check we have PCA
      72           3 :   ReferenceConfiguration* ref0=mymap->getReferenceConfiguration(0);
      73         125 :   for(unsigned i=0; i<mymap->getFullNumberOfTasks(); ++i) {
      74         122 :     if( !(mymap->getReferenceConfiguration(i))->pcaIsEnabledForThisReference() ) {
      75           0 :       error("pca must be implemented in order to use trigometric path");
      76             :     }
      77         244 :     if( ref0->getName()!=(mymap->getReferenceConfiguration(i))->getName() ) {
      78           0 :       error("cannot use mixed metrics");
      79             :     }
      80         122 :     if( mymap->getNumberOfAtoms()!=(mymap->getReferenceConfiguration(i))->getNumberOfReferencePositions() ) {
      81           0 :       error("all frames must use the same set of atoms");
      82             :     }
      83         122 :     if( mymap->getNumberOfArguments()!=(mymap->getReferenceConfiguration(i))->getNumberOfReferenceArguments() ) {
      84           0 :       error("all frames must use the same set of arguments");
      85             :     }
      86             :   }
      87             : 
      88           3 :   cargs.resize( mymap->getNumberOfArguments() );
      89           3 :   std::vector<std::string> argument_names( mymap->getNumberOfArguments() );
      90           7 :   for(unsigned i=0; i<mymap->getNumberOfArguments(); ++i) {
      91           8 :     argument_names[i] = (mymap->getPntrToArgument(i))->getName();
      92             :   }
      93           3 :   PDB mypdb;
      94           3 :   mypdb.setAtomNumbers( mymap->getAbsoluteIndexes() );
      95           3 :   mypdb.addBlockEnd( mymap->getAbsoluteIndexes().size() );
      96           3 :   if( argument_names.size()>0 ) {
      97           2 :     mypdb.setArgumentNames( argument_names );
      98             :   }
      99           3 :   projdir.read( mypdb );
     100           3 :   mypack1.resize( mymap->getNumberOfArguments(), mymap->getNumberOfAtoms() );
     101           3 :   ref0->setupPCAStorage( mypack1 );
     102           3 :   mypack2.resize( mymap->getNumberOfArguments(), mymap->getNumberOfAtoms() );
     103           3 :   ref0->setupPCAStorage( mypack2 );
     104           3 :   mypack3.resize( mymap->getNumberOfArguments(), mymap->getNumberOfAtoms() );
     105          16 :   for(unsigned i=0; i<mymap->getNumberOfAtoms(); ++i) {
     106             :     mypack1.setAtomIndex(i,i);
     107             :     mypack2.setAtomIndex(i,i);
     108             :     mypack3.setAtomIndex(i,i);
     109             :   }
     110           3 :   mypack1_stashd_atoms.resize( mymap->getNumberOfAtoms() );
     111           3 :   mypack1_stashd_args.resize( mymap->getNumberOfArguments() );
     112           3 : }
     113             : 
     114           3 : std::string TrigonometricPathVessel::description() {
     115           3 :   return "values gspath and gzpath contain the position on and distance from the path calculated using trigonometry";
     116             : }
     117             : 
     118           7 : void TrigonometricPathVessel::resize() {
     119           7 :   StoreDataVessel::resize();
     120           7 :   if( getAction()->derivativesAreRequired() ) {
     121           4 :     unsigned nderivatives=getAction()->getNumberOfDerivatives();
     122           4 :     sp->resizeDerivatives( nderivatives );
     123           4 :     zp->resizeDerivatives( nderivatives );
     124             :   }
     125           7 : }
     126             : 
     127        1193 : void TrigonometricPathVessel::finish( const std::vector<double>& buffer ) {
     128             :   // Store the data calculated during mpi loop
     129        1193 :   StoreDataVessel::finish( buffer );
     130             :   // Get current value of all arguments
     131        2487 :   for(unsigned i=0; i<cargs.size(); ++i) {
     132        1294 :     cargs[i]=mymap->getArgument(i);
     133             :   }
     134             : 
     135             :   // Determine closest and second closest point to current position
     136        1193 :   double lambda=mymap->getLambda();
     137        1193 :   std::vector<double> dist( getNumberOfComponents() ), dist2( getNumberOfComponents() );;
     138        1193 :   retrieveSequentialValue( 0, false, dist );
     139        1193 :   retrieveSequentialValue( 1, false, dist2 );
     140        1193 :   iclose1=getStoreIndex(0);
     141        1193 :   iclose2=getStoreIndex(1);
     142        1193 :   double mindist1=dist[0], mindist2=dist2[0];
     143        1193 :   if( lambda>0.0 ) {
     144           0 :     mindist1=-std::log( dist[0] ) / lambda;
     145           0 :     mindist2=-std::log( dist2[0] ) / lambda;
     146             :   }
     147        1193 :   if( mindist2<mindist1 ) {
     148             :     double tmp=mindist1;
     149             :     mindist1=mindist2;
     150             :     mindist2=tmp;
     151         866 :     iclose1=getStoreIndex(1);
     152         866 :     iclose2=getStoreIndex(0);
     153             :   }
     154       56519 :   for(unsigned i=2; i<getNumberOfStoredValues(); ++i) {
     155       55326 :     retrieveSequentialValue( i, false, dist );
     156       55326 :     double ndist=dist[0];
     157       55326 :     if( lambda>0.0 ) {
     158           0 :       ndist=-std::log( dist[0] ) / lambda;
     159             :     }
     160       55326 :     if( ndist<mindist1 ) {
     161             :       mindist2=mindist1;
     162       19737 :       iclose2=iclose1;
     163             :       mindist1=ndist;
     164       19737 :       iclose1=getStoreIndex(i);
     165       35589 :     } else if( ndist<mindist2 ) {
     166             :       mindist2=ndist;
     167        1062 :       iclose2=getStoreIndex(i);
     168             :     }
     169             :   }
     170             :   // And find third closest point
     171        1193 :   int isign = iclose1 - iclose2;
     172             :   if( isign>1 ) {
     173             :     isign=1;
     174             :   } else if( isign<-1 ) {
     175             :     isign=-1;
     176             :   }
     177        1193 :   int iclose3 = iclose1 + isign;
     178             :   double v2v2;
     179             :   // We now have to compute vectors connecting the three closest points to the
     180             :   // new point
     181        1193 :   double v1v1 = (mymap->getReferenceConfiguration( iclose1 ))->calculate( mymap->getPositions(), mymap->getPbc(), mymap->getArguments(), mypack1, true );
     182        1193 :   double v3v3 = (mymap->getReferenceConfiguration( iclose2 ))->calculate( mymap->getPositions(), mymap->getPbc(), mymap->getArguments(), mypack3, true );
     183        1193 :   if( iclose3<0 || iclose3>=mymap->getFullNumberOfTasks() ) {
     184          31 :     ReferenceConfiguration* conf2=mymap->getReferenceConfiguration( iclose1 );
     185          62 :     v2v2=(mymap->getReferenceConfiguration( iclose2 ))->calc( conf2->getReferencePositions(), mymap->getPbc(), mymap->getArguments(),
     186          31 :          conf2->getReferenceArguments(), mypack2, true );
     187          62 :     (mymap->getReferenceConfiguration( iclose2 ))->extractDisplacementVector( conf2->getReferencePositions(), mymap->getArguments(),
     188          62 :         conf2->getReferenceArguments(), false, projdir );
     189             :   } else {
     190        1162 :     ReferenceConfiguration* conf2=mymap->getReferenceConfiguration( iclose3 );
     191        2324 :     v2v2=(mymap->getReferenceConfiguration( iclose1 ))->calc( conf2->getReferencePositions(), mymap->getPbc(), mymap->getArguments(),
     192        1162 :          conf2->getReferenceArguments(), mypack2, true );
     193        2324 :     (mymap->getReferenceConfiguration( iclose1 ))->extractDisplacementVector( conf2->getReferencePositions(), mymap->getArguments(),
     194        2324 :         conf2->getReferenceArguments(), false, projdir );
     195             :   }
     196             : 
     197             :   // Stash derivatives of v1v1
     198        2487 :   for(unsigned i=0; i<mymap->getNumberOfArguments(); ++i) {
     199        1294 :     mypack1_stashd_args[i]=mypack1.getArgumentDerivative(i);
     200             :   }
     201        1193 :   if( mymap->getNumberOfAtoms()>0 ) {
     202         546 :     ReferenceAtoms* at = dynamic_cast<ReferenceAtoms*>( mymap->getReferenceConfiguration( iclose1 ) );
     203             :     const std::vector<double> & displace( at->getDisplace() );
     204        7644 :     for(unsigned i=0; i<mymap->getNumberOfAtoms(); ++i) {
     205        7098 :       mypack1_stashd_atoms[i]=mypack1.getAtomDerivative(i);
     206        7098 :       mypack1.getAtomsDisplacementVector()[i] /= displace[i];
     207             :     }
     208             :   }
     209             :   // Calculate the dot product of v1 with v2
     210        1193 :   double v1v2 = (mymap->getReferenceConfiguration(iclose1))->projectDisplacementOnVector( projdir, mymap->getArguments(), cargs, mypack1 );
     211             : 
     212             :   // This computes s value
     213        1193 :   double spacing = mymap->getPropertyValue( iclose1, (mymap->property.begin())->first ) - mymap->getPropertyValue( iclose2, (mymap->property.begin())->first );
     214        1193 :   double root = sqrt( v1v2*v1v2 - v2v2 * ( v1v1 - v3v3) );
     215        1193 :   dx = 0.5 * ( (root + v1v2) / v2v2 - 1.);
     216        1193 :   double path_s = mymap->getPropertyValue(iclose1, (mymap->property.begin())->first ) + spacing * dx;
     217        1193 :   sp->set( path_s );
     218        1193 :   double fact = 0.25*spacing / v2v2;
     219             :   // Derivative of s wrt arguments
     220        2487 :   for(unsigned i=0; i<mymap->getNumberOfArguments(); ++i) {
     221        1294 :     sp->setDerivative( i, fact*( mypack2.getArgumentDerivative(i) + (v2v2 * (-mypack1_stashd_args[i] + mypack3.getArgumentDerivative(i))
     222        1294 :                                  + v1v2*mypack2.getArgumentDerivative(i) )/root ) );
     223             :   }
     224             :   // Derivative of s wrt atoms
     225        1193 :   unsigned narg=mymap->getNumberOfArguments();
     226        1193 :   Tensor vir;
     227        1193 :   vir.zero();
     228        1193 :   fact = 0.5*spacing / v2v2;
     229        1193 :   if( mymap->getNumberOfAtoms()>0 ) {
     230        7644 :     for(unsigned i=0; i<mymap->getNumberOfAtoms(); ++i) {
     231        7098 :       Vector ader = fact*(( v1v2*mypack1.getAtomDerivative(i) + 0.5*v2v2*(-mypack1_stashd_atoms[i] + mypack3.getAtomDerivative(i) ) )/root + mypack1.getAtomDerivative(i) );
     232       28392 :       for(unsigned k=0; k<3; ++k) {
     233       21294 :         sp->setDerivative( narg+3*i+k, ader[k] );
     234             :       }
     235        7098 :       vir-=Tensor( mymap->getPosition(i), ader );
     236             :     }
     237             :     // Set the virial
     238         546 :     unsigned nbase=narg+3*mymap->getNumberOfAtoms();
     239        2184 :     for(unsigned i=0; i<3; ++i)
     240        6552 :       for(unsigned j=0; j<3; ++j) {
     241        4914 :         sp->setDerivative( nbase+3*i+j, vir(i,j) );
     242             :       }
     243             :   }
     244             :   // Now compute z value
     245        1193 :   ReferenceConfiguration* conf2=mymap->getReferenceConfiguration( iclose1 );
     246        2386 :   double v4v4=(mymap->getReferenceConfiguration( iclose2 ))->calc( conf2->getReferencePositions(), mymap->getPbc(), mymap->getArguments(),
     247        1193 :               conf2->getReferenceArguments(), mypack2, true );
     248             :   // Extract vector connecting frames
     249        2386 :   (mymap->getReferenceConfiguration( iclose2 ))->extractDisplacementVector( conf2->getReferencePositions(), mymap->getArguments(),
     250        1193 :       conf2->getReferenceArguments(), false, projdir );
     251             :   // Calculate projection of vector on line connnecting frames
     252        1193 :   double proj = (mymap->getReferenceConfiguration(iclose1))->projectDisplacementOnVector( projdir, mymap->getArguments(), cargs, mypack1 );
     253        1193 :   double path_z = v1v1 + dx*dx*v4v4 - 2*dx*proj;
     254             :   // Derivatives for z path
     255        1193 :   path_z = sqrt(path_z);
     256        1193 :   zp->set( path_z );
     257        1193 :   vir.zero();
     258        2487 :   for(unsigned i=0; i<mymap->getNumberOfArguments(); ++i) {
     259        1294 :     zp->setDerivative( i, (mypack1_stashd_args[i] - 2*dx*mypack1.getArgumentDerivative(i))/(2.0*path_z) );
     260             :   }
     261             :   // Derivative wrt atoms
     262        1193 :   if( mymap->getNumberOfAtoms()>0 ) {
     263        7644 :     for(unsigned i=0; i<mymap->getNumberOfAtoms(); ++i) {
     264        7098 :       Vector dxder;
     265       28392 :       for(unsigned k=0; k<3; ++k) {
     266       21294 :         dxder[k] = ( 2*v4v4*dx - 2*proj )*spacing*sp->getDerivative( narg + 3*i+k );
     267             :       }
     268        7098 :       Vector ader = ( mypack1_stashd_atoms[i] - 2.*dx*mypack1.getAtomDerivative(i) + dxder )/ (2.0*path_z);
     269       28392 :       for(unsigned k=0; k<3; ++k) {
     270       21294 :         zp->setDerivative( narg+3*i+k, ader[k] );
     271             :       }
     272        7098 :       vir-=Tensor( mymap->getPosition(i), ader );
     273             :     }
     274             :     // Set the virial
     275         546 :     unsigned nbase=narg+3*mymap->getNumberOfAtoms();
     276        2184 :     for(unsigned i=0; i<3; ++i)
     277        6552 :       for(unsigned j=0; j<3; ++j) {
     278        4914 :         zp->setDerivative( nbase+3*i+j, vir(i,j) );
     279             :       }
     280             :   }
     281        1193 : }
     282             : 
     283        1193 : bool TrigonometricPathVessel::applyForce( std::vector<double>& forces ) {
     284        1193 :   std::vector<double> tmpforce( forces.size(), 0.0 );
     285        1193 :   forces.assign(forces.size(),0.0);
     286             :   bool wasforced=false;
     287        1193 :   if( sp->applyForce( tmpforce ) ) {
     288             :     wasforced=true;
     289           0 :     for(unsigned j=0; j<forces.size(); ++j) {
     290           0 :       forces[j]+=tmpforce[j];
     291             :     }
     292             :   }
     293        1193 :   tmpforce.assign(forces.size(),0.0);
     294        1193 :   if( zp->applyForce( tmpforce ) ) {
     295             :     wasforced=true;
     296           0 :     for(unsigned j=0; j<forces.size(); ++j) {
     297           0 :       forces[j]+=tmpforce[j];
     298             :     }
     299             :   }
     300        1193 :   return wasforced;
     301             : }
     302             : 
     303             : }
     304             : }

Generated by: LCOV version 1.16