1 /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 Copyright (c) 2015-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 "Bias.h"
23 #include "ActionRegister.h"
24 #include "tools/Random.h"
25 #include "core/PlumedMain.h"
26 #include "core/Atoms.h"
27
28 #include <iostream>
29
30
31 using namespace std;
32
33
34 namespace PLMD {
35 namespace bias {
36
37 //+PLUMEDOC BIAS EXTENDED_LAGRANGIAN
38 /*
39 Add extended Lagrangian.
40
41 This action can be used to create fictitious collective variables coupled to the real ones.
42 Given \f$x_i\f$ the \f$i\f$th argument of this bias potential, potential
43 and kinetic contributions are added to the energy of the system as
44 \f[
45 V=\sum_i \frac{k_i}{2} (x_i-s_i)^2 + \sum_i \frac{\dot{s}_i^2}{2m_i}
46 \f].
47
48 The resulting potential is thus similar to a \ref RESTRAINT,
49 but the restraint center moved with time following Hamiltonian
50 dynamics with mass \f$m_i\f$.
51
52 This bias potential accepts thus vectorial keywords (one element per argument)
53 to define the coupling constant (KAPPA) and a relaxation time \f$tau\f$ (TAU).
54 The mass is them computed as \f$m=k(\frac{\tau}{2\pi})^2\f$.
55
56 Notice that this action creates several components.
57 The ones named XX_fict are the fictitious coordinates. It is possible
58 to add further forces on them by means of other bias potential,
59 e.g. to obtain an indirect \ref METAD as in \cite continua .
60 Also notice that the velocities of the fictitious coordinates
61 are reported (XX_vfict). However, printed velocities are the ones
62 at the previous step.
63
64 It is also possible to provide a non-zero friction (one value per component).
65 This is then used to implement a Langevin thermostat, so as to implement
66 TAMD/dAFED method \cite Maragliano2006 \cite AbramsJ2008 . Notice that
67 here a massive Langevin thermostat is used, whereas usually
68 TAMD employs an overamped Langevin dynamics and dAFED
69 a Gaussian thermostat.
70
71 \warning
72 The bias potential is reported in the component bias.
73 Notice that this bias potential, although formally compatible with
74 replica exchange framework, probably does not work as expected in that case.
75 Indeed, since fictitious coordinates are not swapped upon exchange,
76 acceptace can be expected to be extremely low unless (by chance) two neighboring
77 replicas have the fictitious variables located properly in space.
78
79 \warning
80 \ref RESTART is not properly supported by this action. Indeed,
81 at every start the position of the fictitious variable is reset to the value
82 of the real variable, and its velocity is set to zero.
83 This is not expected to introduce big errors, but certainly is
84 introducing a small inconsistency between a single long run
85 and many shorter runs.
86
87 \par Examples
88
89 The following input tells plumed to perform a metadynamics
90 with an extended Lagrangian on two torsional angles.
91 \plumedfile
92 phi: TORSION ATOMS=5,7,9,15
93 psi: TORSION ATOMS=7,9,15,17
94 ex: EXTENDED_LAGRANGIAN ARG=phi,psi KAPPA=20,20.0 TAU=0.1,0.1
95 METAD ARG=ex.phi_fict,ex.psi_fict PACE=100 SIGMA=0.35,0.35 HEIGHT=0.1
96 # monitor the two variables
97 PRINT STRIDE=10 ARG=phi,psi,ex.phi_fict,ex.psi_fict FILE=COLVAR
98 \endplumedfile
99
100 The following input tells plumed to perform a TAMD (or dAFED)
101 calculation on two torsional angles, keeping the two variables
102 at a fictitious temperature of 3000K with a Langevin thermostat
103 with friction 10
104 \plumedfile
105 phi: TORSION ATOMS=5,7,9,15
106 psi: TORSION ATOMS=7,9,15,17
107 ex: EXTENDED_LAGRANGIAN ARG=phi,psi KAPPA=20,20.0 TAU=0.1,0.1 FRICTION=10,10 TEMP=3000
108 # monitor the two variables
109 PRINT STRIDE=10 ARG=phi,psi,ex.phi_fict,ex.psi_fict FILE=COLVAR
110 \endplumedfile
111
112 */
113 //+ENDPLUMEDOC
114
115 class ExtendedLagrangian : public Bias {
116 bool firsttime;
117 std::vector<double> fict;
118 std::vector<double> vfict;
119 std::vector<double> vfict_laststep;
120 std::vector<double> ffict;
121 std::vector<double> kappa;
122 std::vector<double> tau;
123 std::vector<double> friction;
124 std::vector<Value*> fictValue;
125 std::vector<Value*> vfictValue;
126 double kbt;
127 Random rand;
128 public:
129 explicit ExtendedLagrangian(const ActionOptions&);
130 void calculate() override;
131 void update() override;
132 static void registerKeywords(Keywords& keys);
133 };
134
135 PLUMED_REGISTER_ACTION(ExtendedLagrangian,"EXTENDED_LAGRANGIAN")
136
registerKeywords(Keywords & keys)137 void ExtendedLagrangian::registerKeywords(Keywords& keys) {
138 Bias::registerKeywords(keys);
139 keys.use("ARG");
140 keys.add("compulsory","KAPPA","specifies that the restraint is harmonic and what the values of the force constants on each of the variables are");
141 keys.add("compulsory","TAU","specifies that the restraint is harmonic and what the values of the force constants on each of the variables are");
142 keys.add("compulsory","FRICTION","0.0","add a friction to the variable");
143 keys.add("optional","TEMP","the system temperature - needed when FRICTION is present. If not provided will be taken from MD code (if available)");
144 componentsAreNotOptional(keys);
145 keys.addOutputComponent("_fict","default","one or multiple instances of this quantity can be referenced elsewhere in the input file. "
146 "These quantities will named with the arguments of the bias followed by "
147 "the character string _tilde. It is possible to add forces on these variable.");
148 keys.addOutputComponent("_vfict","default","one or multiple instances of this quantity can be referenced elsewhere in the input file. "
149 "These quantities will named with the arguments of the bias followed by "
150 "the character string _tilde. It is NOT possible to add forces on these variable.");
151 }
152
ExtendedLagrangian(const ActionOptions & ao)153 ExtendedLagrangian::ExtendedLagrangian(const ActionOptions&ao):
154 PLUMED_BIAS_INIT(ao),
155 firsttime(true),
156 fict(getNumberOfArguments(),0.0),
157 vfict(getNumberOfArguments(),0.0),
158 vfict_laststep(getNumberOfArguments(),0.0),
159 ffict(getNumberOfArguments(),0.0),
160 kappa(getNumberOfArguments(),0.0),
161 tau(getNumberOfArguments(),0.0),
162 friction(getNumberOfArguments(),0.0),
163 fictValue(getNumberOfArguments(),NULL),
164 vfictValue(getNumberOfArguments(),NULL),
165 kbt(0.0)
166 {
167 parseVector("TAU",tau);
168 parseVector("FRICTION",friction);
169 parseVector("KAPPA",kappa);
170 double temp=-1.0;
171 parse("TEMP",temp);
172 if(temp>=0.0) kbt=plumed.getAtoms().getKBoltzmann()*temp;
173 else kbt=plumed.getAtoms().getKbT();
174 checkRead();
175
176 log.printf(" with harmonic force constant");
177 for(unsigned i=0; i<kappa.size(); i++) log.printf(" %f",kappa[i]);
178 log.printf("\n");
179
180 log.printf(" with relaxation time");
181 for(unsigned i=0; i<tau.size(); i++) log.printf(" %f",tau[i]);
182 log.printf("\n");
183
184 bool hasFriction=false;
185 for(unsigned i=0; i<getNumberOfArguments(); ++i) if(friction[i]>0.0) hasFriction=true;
186
187 if(hasFriction) {
188 log.printf(" with friction");
189 for(unsigned i=0; i<friction.size(); i++) log.printf(" %f",friction[i]);
190 log.printf("\n");
191 }
192
193 log.printf(" and kbt");
194 log.printf(" %f",kbt);
195 log.printf("\n");
196
197 for(unsigned i=0; i<getNumberOfArguments(); i++) {
198 std::string comp=getPntrToArgument(i)->getName()+"_fict";
199 addComponentWithDerivatives(comp);
200 if(getPntrToArgument(i)->isPeriodic()) {
201 std::string a,b;
202 getPntrToArgument(i)->getDomain(a,b);
203 componentIsPeriodic(comp,a,b);
204 } else componentIsNotPeriodic(comp);
205 fictValue[i]=getPntrToComponent(comp);
206 comp=getPntrToArgument(i)->getName()+"_vfict";
207 addComponent(comp);
208 componentIsNotPeriodic(comp);
209 vfictValue[i]=getPntrToComponent(comp);
210 }
211
212 log<<" Bibliography "<<plumed.cite("Iannuzzi, Laio, and Parrinello, Phys. Rev. Lett. 90, 238302 (2003)");
213 if(hasFriction) {
214 log<<plumed.cite("Maragliano and Vanden-Eijnden, Chem. Phys. Lett. 426, 168 (2006)");
215 log<<plumed.cite("Abrams and Tuckerman, J. Phys. Chem. B 112, 15742 (2008)");
216 }
217 log<<"\n";
218 }
219
220
calculate()221 void ExtendedLagrangian::calculate() {
222
223 if(firsttime) {
224 for(unsigned i=0; i<getNumberOfArguments(); ++i) {
225 fict[i]=getArgument(i);
226 }
227 firsttime=false;
228 }
229 double ene=0.0;
230 for(unsigned i=0; i<getNumberOfArguments(); ++i) {
231 const double cv=difference(i,fict[i],getArgument(i));
232 const double k=kappa[i];
233 const double f=-k*cv;
234 ene+=0.5*k*cv*cv;
235 setOutputForce(i,f);
236 ffict[i]=-f;
237 };
238 setBias(ene);
239 for(unsigned i=0; i<getNumberOfArguments(); ++i) {
240 fict[i]=fictValue[i]->bringBackInPbc(fict[i]);
241 fictValue[i]->set(fict[i]);
242 vfictValue[i]->set(vfict_laststep[i]);
243 }
244 }
245
update()246 void ExtendedLagrangian::update() {
247 double dt=getTimeStep()*getStride();
248 for(unsigned i=0; i<getNumberOfArguments(); ++i) {
249 double mass=kappa[i]*tau[i]*tau[i]/(4*pi*pi); // should be k/omega**2
250 double c1=exp(-0.5*friction[i]*dt);
251 double c2=sqrt(kbt*(1.0-c1*c1)/mass);
252 // consider additional forces on the fictitious particle
253 // (e.g. MetaD stuff)
254 ffict[i]+=fictValue[i]->getForce();
255
256 // update velocity (half step)
257 vfict[i]+=ffict[i]*0.5*dt/mass;
258 // thermostat (half step)
259 vfict[i]=c1*vfict[i]+c2*rand.Gaussian();
260 // save full step velocity to be dumped at next step
261 vfict_laststep[i]=vfict[i];
262 // thermostat (half step)
263 vfict[i]=c1*vfict[i]+c2*rand.Gaussian();
264 // update velocity (half step)
265 vfict[i]+=ffict[i]*0.5*dt/mass;
266 // update position (full step)
267 fict[i]+=vfict[i]*dt;
268 }
269 }
270
271 }
272
273 }
274