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