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