LCOV - code coverage report
Current view: top level - mapping - TrigonometricPathVessel.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 134 138 97.1 %
Date: 2024-10-11 08:09:47 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       10422 : PLUMED_REGISTER_VESSEL(TrigonometricPathVessel,"GPATH")
      30             : 
      31           3 : void TrigonometricPathVessel::registerKeywords( Keywords& keys ) {
      32           3 :   StoreDataVessel::registerKeywords(keys);
      33           3 : }
      34             : 
      35        3473 : void TrigonometricPathVessel::reserveKeyword( Keywords& keys ) {
      36        6946 :   keys.reserve("vessel","GPATH","calculate the position on the path using trigonometry");
      37        6946 :   keys.addOutputComponent("gspath","GPATH","the position on the path calculated using trigonometry");
      38        6946 :   keys.addOutputComponent("gzpath","GPATH","the distance from the path calculated using trigonometry");
      39        3473 : }
      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             : {
      51           3 :   mymap=dynamic_cast<Mapping*>( getAction() );
      52           3 :   plumed_massert( mymap, "Trigonometric path vessel can only be used with mappings");
      53             :   // Retrieve the index of the property in the underlying mapping
      54           3 :   if( mymap->getNumberOfProperties()!=1 ) error("cannot use trigonometric paths when there are multiple properties");
      55             : 
      56         125 :   for(unsigned i=0; i<mymap->getFullNumberOfTasks(); ++i) {
      57         122 :     if( mymap->getTaskCode(i)!=mymap->getPositionInFullTaskList(i) ) error("mismatched tasks and codes");
      58             :   }
      59           6 :   mymap->addComponentWithDerivatives("gspath"); mymap->componentIsNotPeriodic("gspath");
      60           3 :   sp=mymap->copyOutput( mymap->getNumberOfComponents()-1 ); sp->resizeDerivatives( mymap->getNumberOfDerivatives() );
      61           6 :   mymap->addComponentWithDerivatives("gzpath"); mymap->componentIsNotPeriodic("gzpath");
      62           3 :   zp=mymap->copyOutput( mymap->getNumberOfComponents()-1 ); zp->resizeDerivatives( mymap->getNumberOfDerivatives() );
      63             : 
      64             :   // Check we have PCA
      65           3 :   ReferenceConfiguration* ref0=mymap->getReferenceConfiguration(0);
      66         125 :   for(unsigned i=0; i<mymap->getFullNumberOfTasks(); ++i) {
      67         122 :     if( !(mymap->getReferenceConfiguration(i))->pcaIsEnabledForThisReference() ) error("pca must be implemented in order to use trigometric path");
      68         244 :     if( ref0->getName()!=(mymap->getReferenceConfiguration(i))->getName() ) error("cannot use mixed metrics");
      69         122 :     if( mymap->getNumberOfAtoms()!=(mymap->getReferenceConfiguration(i))->getNumberOfReferencePositions() ) error("all frames must use the same set of atoms");
      70         122 :     if( mymap->getNumberOfArguments()!=(mymap->getReferenceConfiguration(i))->getNumberOfReferenceArguments() ) error("all frames must use the same set of arguments");
      71             :   }
      72             : 
      73           3 :   cargs.resize( mymap->getNumberOfArguments() ); std::vector<std::string> argument_names( mymap->getNumberOfArguments() );
      74           7 :   for(unsigned i=0; i<mymap->getNumberOfArguments(); ++i) argument_names[i] = (mymap->getPntrToArgument(i))->getName();
      75           3 :   PDB mypdb; mypdb.setAtomNumbers( mymap->getAbsoluteIndexes() ); mypdb.addBlockEnd( mymap->getAbsoluteIndexes().size() );
      76           3 :   if( argument_names.size()>0 ) mypdb.setArgumentNames( argument_names );
      77           3 :   projdir.read( mypdb );
      78           3 :   mypack1.resize( mymap->getNumberOfArguments(), mymap->getNumberOfAtoms() ); ref0->setupPCAStorage( mypack1 );
      79           3 :   mypack2.resize( mymap->getNumberOfArguments(), mymap->getNumberOfAtoms() ); ref0->setupPCAStorage( mypack2 );
      80           3 :   mypack3.resize( mymap->getNumberOfArguments(), mymap->getNumberOfAtoms() );
      81          16 :   for(unsigned i=0; i<mymap->getNumberOfAtoms(); ++i) { mypack1.setAtomIndex(i,i); mypack2.setAtomIndex(i,i); mypack3.setAtomIndex(i,i); }
      82           3 :   mypack1_stashd_atoms.resize( mymap->getNumberOfAtoms() ); mypack1_stashd_args.resize( mymap->getNumberOfArguments() );
      83           3 : }
      84             : 
      85           3 : std::string TrigonometricPathVessel::description() {
      86           3 :   return "values gspath and gzpath contain the position on and distance from the path calculated using trigonometry";
      87             : }
      88             : 
      89           7 : void TrigonometricPathVessel::resize() {
      90           7 :   StoreDataVessel::resize();
      91           7 :   if( getAction()->derivativesAreRequired() ) {
      92           4 :     unsigned nderivatives=getAction()->getNumberOfDerivatives();
      93           8 :     sp->resizeDerivatives( nderivatives ); zp->resizeDerivatives( nderivatives );
      94             :   }
      95           7 : }
      96             : 
      97        1193 : void TrigonometricPathVessel::finish( const std::vector<double>& buffer ) {
      98             :   // Store the data calculated during mpi loop
      99        1193 :   StoreDataVessel::finish( buffer );
     100             :   // Get current value of all arguments
     101        2487 :   for(unsigned i=0; i<cargs.size(); ++i) cargs[i]=mymap->getArgument(i);
     102             : 
     103             :   // Determine closest and second closest point to current position
     104        1193 :   double lambda=mymap->getLambda();
     105        1193 :   std::vector<double> dist( getNumberOfComponents() ), dist2( getNumberOfComponents() );;
     106        1193 :   retrieveSequentialValue( 0, false, dist );
     107        1193 :   retrieveSequentialValue( 1, false, dist2 );
     108        1193 :   iclose1=getStoreIndex(0); iclose2=getStoreIndex(1);
     109        1193 :   double mindist1=dist[0], mindist2=dist2[0];
     110        1193 :   if( lambda>0.0 ) {
     111           0 :     mindist1=-std::log( dist[0] ) / lambda;
     112           0 :     mindist2=-std::log( dist2[0] ) / lambda;
     113             :   }
     114        1193 :   if( mindist2<mindist1 ) {
     115             :     double tmp=mindist1; mindist1=mindist2; mindist2=tmp;
     116         866 :     iclose1=getStoreIndex(1); iclose2=getStoreIndex(0);
     117             :   }
     118       56519 :   for(unsigned i=2; i<getNumberOfStoredValues(); ++i) {
     119       55326 :     retrieveSequentialValue( i, false, dist );
     120       55326 :     double ndist=dist[0];
     121       55326 :     if( lambda>0.0 ) ndist=-std::log( dist[0] ) / lambda;
     122       55326 :     if( ndist<mindist1 ) {
     123       19737 :       mindist2=mindist1; iclose2=iclose1;
     124       19737 :       mindist1=ndist; iclose1=getStoreIndex(i);
     125       35589 :     } else if( ndist<mindist2 ) {
     126        1062 :       mindist2=ndist; iclose2=getStoreIndex(i);
     127             :     }
     128             :   }
     129             :   // And find third closest point
     130        1193 :   int isign = iclose1 - iclose2;
     131             :   if( isign>1 ) isign=1; else if( isign<-1 ) isign=-1;
     132        1193 :   int iclose3 = iclose1 + isign; double v2v2;
     133             :   // We now have to compute vectors connecting the three closest points to the
     134             :   // new point
     135        1193 :   double v1v1 = (mymap->getReferenceConfiguration( iclose1 ))->calculate( mymap->getPositions(), mymap->getPbc(), mymap->getArguments(), mypack1, true );
     136        1193 :   double v3v3 = (mymap->getReferenceConfiguration( iclose2 ))->calculate( mymap->getPositions(), mymap->getPbc(), mymap->getArguments(), mypack3, true );
     137        1193 :   if( iclose3<0 || iclose3>=mymap->getFullNumberOfTasks() ) {
     138          31 :     ReferenceConfiguration* conf2=mymap->getReferenceConfiguration( iclose1 );
     139          62 :     v2v2=(mymap->getReferenceConfiguration( iclose2 ))->calc( conf2->getReferencePositions(), mymap->getPbc(), mymap->getArguments(),
     140          31 :          conf2->getReferenceArguments(), mypack2, true );
     141          62 :     (mymap->getReferenceConfiguration( iclose2 ))->extractDisplacementVector( conf2->getReferencePositions(), mymap->getArguments(),
     142          62 :         conf2->getReferenceArguments(), false, projdir );
     143             :   } else {
     144        1162 :     ReferenceConfiguration* conf2=mymap->getReferenceConfiguration( iclose3 );
     145        2324 :     v2v2=(mymap->getReferenceConfiguration( iclose1 ))->calc( conf2->getReferencePositions(), mymap->getPbc(), mymap->getArguments(),
     146        1162 :          conf2->getReferenceArguments(), mypack2, true );
     147        2324 :     (mymap->getReferenceConfiguration( iclose1 ))->extractDisplacementVector( conf2->getReferencePositions(), mymap->getArguments(),
     148        2324 :         conf2->getReferenceArguments(), false, projdir );
     149             :   }
     150             : 
     151             :   // Stash derivatives of v1v1
     152        2487 :   for(unsigned i=0; i<mymap->getNumberOfArguments(); ++i) mypack1_stashd_args[i]=mypack1.getArgumentDerivative(i);
     153        1193 :   if( mymap->getNumberOfAtoms()>0 ) {
     154         546 :     ReferenceAtoms* at = dynamic_cast<ReferenceAtoms*>( mymap->getReferenceConfiguration( iclose1 ) );
     155             :     const std::vector<double> & displace( at->getDisplace() );
     156        7644 :     for(unsigned i=0; i<mymap->getNumberOfAtoms(); ++i) {
     157        7098 :       mypack1_stashd_atoms[i]=mypack1.getAtomDerivative(i); mypack1.getAtomsDisplacementVector()[i] /= displace[i];
     158             :     }
     159             :   }
     160             :   // Calculate the dot product of v1 with v2
     161        1193 :   double v1v2 = (mymap->getReferenceConfiguration(iclose1))->projectDisplacementOnVector( projdir, mymap->getArguments(), cargs, mypack1 );
     162             : 
     163             :   // This computes s value
     164        1193 :   double spacing = mymap->getPropertyValue( iclose1, (mymap->property.begin())->first ) - mymap->getPropertyValue( iclose2, (mymap->property.begin())->first );
     165        1193 :   double root = sqrt( v1v2*v1v2 - v2v2 * ( v1v1 - v3v3) );
     166        1193 :   dx = 0.5 * ( (root + v1v2) / v2v2 - 1.);
     167        1193 :   double path_s = mymap->getPropertyValue(iclose1, (mymap->property.begin())->first ) + spacing * dx; sp->set( path_s );
     168        1193 :   double fact = 0.25*spacing / v2v2;
     169             :   // Derivative of s wrt arguments
     170        2487 :   for(unsigned i=0; i<mymap->getNumberOfArguments(); ++i) {
     171        1294 :     sp->setDerivative( i, fact*( mypack2.getArgumentDerivative(i) + (v2v2 * (-mypack1_stashd_args[i] + mypack3.getArgumentDerivative(i))
     172        1294 :                                  + v1v2*mypack2.getArgumentDerivative(i) )/root ) );
     173             :   }
     174             :   // Derivative of s wrt atoms
     175        1193 :   unsigned narg=mymap->getNumberOfArguments(); Tensor vir; vir.zero(); fact = 0.5*spacing / v2v2;
     176        1193 :   if( mymap->getNumberOfAtoms()>0 ) {
     177        7644 :     for(unsigned i=0; i<mymap->getNumberOfAtoms(); ++i) {
     178        7098 :       Vector ader = fact*(( v1v2*mypack1.getAtomDerivative(i) + 0.5*v2v2*(-mypack1_stashd_atoms[i] + mypack3.getAtomDerivative(i) ) )/root + mypack1.getAtomDerivative(i) );
     179       28392 :       for(unsigned k=0; k<3; ++k) sp->setDerivative( narg+3*i+k, ader[k] );
     180        7098 :       vir-=Tensor( mymap->getPosition(i), ader );
     181             :     }
     182             :     // Set the virial
     183         546 :     unsigned nbase=narg+3*mymap->getNumberOfAtoms();
     184        7098 :     for(unsigned i=0; i<3; ++i) for(unsigned j=0; j<3; ++j) sp->setDerivative( nbase+3*i+j, vir(i,j) );
     185             :   }
     186             :   // Now compute z value
     187        1193 :   ReferenceConfiguration* conf2=mymap->getReferenceConfiguration( iclose1 );
     188        2386 :   double v4v4=(mymap->getReferenceConfiguration( iclose2 ))->calc( conf2->getReferencePositions(), mymap->getPbc(), mymap->getArguments(),
     189        1193 :               conf2->getReferenceArguments(), mypack2, true );
     190             :   // Extract vector connecting frames
     191        2386 :   (mymap->getReferenceConfiguration( iclose2 ))->extractDisplacementVector( conf2->getReferencePositions(), mymap->getArguments(),
     192        1193 :       conf2->getReferenceArguments(), false, projdir );
     193             :   // Calculate projection of vector on line connnecting frames
     194        1193 :   double proj = (mymap->getReferenceConfiguration(iclose1))->projectDisplacementOnVector( projdir, mymap->getArguments(), cargs, mypack1 );
     195        1193 :   double path_z = v1v1 + dx*dx*v4v4 - 2*dx*proj;
     196             :   // Derivatives for z path
     197        2386 :   path_z = sqrt(path_z); zp->set( path_z ); vir.zero();
     198        2487 :   for(unsigned i=0; i<mymap->getNumberOfArguments(); ++i) zp->setDerivative( i, (mypack1_stashd_args[i] - 2*dx*mypack1.getArgumentDerivative(i))/(2.0*path_z) );
     199             :   // Derivative wrt atoms
     200        1193 :   if( mymap->getNumberOfAtoms()>0 ) {
     201        7644 :     for(unsigned i=0; i<mymap->getNumberOfAtoms(); ++i) {
     202       28392 :       Vector dxder; for(unsigned k=0; k<3; ++k) dxder[k] = ( 2*v4v4*dx - 2*proj )*spacing*sp->getDerivative( narg + 3*i+k );
     203        7098 :       Vector ader = ( mypack1_stashd_atoms[i] - 2.*dx*mypack1.getAtomDerivative(i) + dxder )/ (2.0*path_z);
     204       28392 :       for(unsigned k=0; k<3; ++k) zp->setDerivative( narg+3*i+k, ader[k] );
     205        7098 :       vir-=Tensor( mymap->getPosition(i), ader );
     206             :     }
     207             :     // Set the virial
     208         546 :     unsigned nbase=narg+3*mymap->getNumberOfAtoms();
     209        7098 :     for(unsigned i=0; i<3; ++i) for(unsigned j=0; j<3; ++j) zp->setDerivative( nbase+3*i+j, vir(i,j) );
     210             :   }
     211        1193 : }
     212             : 
     213        1193 : bool TrigonometricPathVessel::applyForce( std::vector<double>& forces ) {
     214        1193 :   std::vector<double> tmpforce( forces.size(), 0.0 );
     215        1193 :   forces.assign(forces.size(),0.0); bool wasforced=false;
     216        1193 :   if( sp->applyForce( tmpforce ) ) {
     217             :     wasforced=true;
     218           0 :     for(unsigned j=0; j<forces.size(); ++j) forces[j]+=tmpforce[j];
     219             :   }
     220        1193 :   tmpforce.assign(forces.size(),0.0);
     221        1193 :   if( zp->applyForce( tmpforce ) ) {
     222             :     wasforced=true;
     223           0 :     for(unsigned j=0; j<forces.size(); ++j) forces[j]+=tmpforce[j];
     224             :   }
     225        1193 :   return wasforced;
     226             : }
     227             : 
     228             : }
     229             : }

Generated by: LCOV version 1.15