Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2011-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 "Bias.h"
23 : #include "core/ActionRegister.h"
24 :
25 : namespace PLMD {
26 : namespace bias {
27 :
28 : //+PLUMEDOC BIAS MOVINGRESTRAINT
29 : /*
30 : Add a time-dependent, harmonic restraint on one or more variables.
31 :
32 : This form of bias can be used to perform the steered MD
33 : and Jarzynski sampling calculations that are discussed in the papers cited below.
34 :
35 : The harmonic restraint on your system is given by:
36 :
37 : $$
38 : V(\vec{s},t) = \frac{1}{2} \kappa(t) ( \vec{s} - \vec{s}_0(t) )^2
39 : $$
40 :
41 : The time dependence of $\kappa$ and $\vec{s}_0$ are specified by a list of
42 : STEP, KAPPA and AT keywords. These keywords tell plumed what values $\kappa$ and $\vec{s}_0$
43 : should have at the time specified by the corresponding STEP keyword. In between these times
44 : the values of $\kappa$ and $\vec{s}_0$ are linearly interpolated.
45 :
46 : ## Examples
47 :
48 : The following input is dragging the distance between atoms 2 and 4
49 : from 1 to 2 in the first 1000 steps, then back in the next 1000 steps.
50 : In the following 500 steps the restraint is progressively switched off.
51 :
52 : ```plumed
53 : DISTANCE ATOMS=2,4 LABEL=d
54 : MOVINGRESTRAINT ...
55 : ARG=d
56 : STEP0=0 AT0=1.0 KAPPA0=100.0
57 : STEP1=1000 AT1=2.0
58 : STEP2=2000 AT2=1.0
59 : STEP3=2500 KAPPA3=0.0
60 : ... MOVINGRESTRAINT
61 : ```
62 :
63 : The following input is progressively building restraints
64 : distances between atoms 1 and 5 and between atoms 2 and 4
65 : in the first 1000 steps. Afterwards, the restraint is kept
66 : static.
67 :
68 : ```plumed
69 : DISTANCE ATOMS=1,5 LABEL=d1
70 : DISTANCE ATOMS=2,4 LABEL=d2
71 : MOVINGRESTRAINT ...
72 : ARG=d1,d2
73 : STEP0=0 AT0=1.0,1.5 KAPPA0=0.0,0.0
74 : STEP1=1000 AT1=1.0,1.5 KAPPA1=1.0,1.0
75 : ... MOVINGRESTRAINT
76 : ```
77 :
78 : The following input is progressively bringing atoms 1 and 2
79 : close to each other with an upper wall
80 :
81 : ```plumed
82 : DISTANCE ATOMS=1,2 LABEL=d1
83 : MOVINGRESTRAINT ...
84 : ARG=d1
85 : VERSE=U
86 : STEP0=0 AT0=1.0 KAPPA0=10.0
87 : STEP1=1000 AT1=0.0
88 : ... MOVINGRESTRAINT
89 : ```
90 :
91 : By default the Action is issuing some values which are
92 : the work on each degree of freedom, the center of the harmonic potential,
93 : the total bias deposited
94 :
95 : (See also [DISTANCE](DISTANCE.md)).
96 :
97 : !!! attention ""
98 :
99 : Work is not computed properly when KAPPA is time dependent.
100 :
101 : */
102 : //+ENDPLUMEDOC
103 :
104 :
105 : class MovingRestraint : public Bias {
106 : std::vector<std::vector<double> > at;
107 : std::vector<std::vector<double> > kappa;
108 : std::vector<long long int> step;
109 : std::vector<double> oldaa;
110 : std::vector<double> oldk;
111 : std::vector<double> olddpotdk;
112 : std::vector<double> oldf;
113 : std::vector<std::string> verse;
114 : std::vector<double> work;
115 : //memeber used as temporary working objects
116 : std::vector<double> kk,aa,f,dpotdk;
117 : std::vector<Value*> valueCntr;
118 : std::vector<Value*> valueWork;
119 : std::vector<Value*> valueKappa;
120 : Value* valueTotWork=nullptr;
121 : Value* valueForce2=nullptr;
122 : public:
123 : explicit MovingRestraint(const ActionOptions&);
124 : void calculate() override;
125 : static void registerKeywords( Keywords& keys );
126 : };
127 :
128 : PLUMED_REGISTER_ACTION(MovingRestraint,"MOVINGRESTRAINT")
129 :
130 6 : void MovingRestraint::registerKeywords( Keywords& keys ) {
131 6 : Bias::registerKeywords(keys);
132 6 : keys.add("compulsory","VERSE","B","Tells plumed whether the restraint is only acting for CV larger (U) or smaller (L) than "
133 : "the restraint or whether it is acting on both sides (B)");
134 6 : keys.add("numbered","STEP","This keyword appears multiple times as STEPx with x=0,1,2,...,n. Each value given represents "
135 : "the MD step at which the restraint parameters take the values KAPPAx and ATx.");
136 12 : keys.reset_style("STEP","compulsory");
137 6 : keys.add("numbered","AT","ATx is equal to the position of the restraint at time STEPx. For intermediate times this parameter "
138 : "is linearly interpolated. If no ATx is specified for STEPx then the values of AT are kept constant "
139 : "during the interval of time between STEP(x-1) and STEPx.");
140 12 : keys.reset_style("AT","compulsory");
141 6 : keys.add("numbered","KAPPA","KAPPAx is equal to the value of the force constants at time STEPx. For intermediate times this "
142 : "parameter is linearly interpolated. If no KAPPAx is specified for STEPx then the values of KAPPAx "
143 : "are kept constant during the interval of time between STEP(x-1) and STEPx.");
144 12 : keys.reset_style("KAPPA","compulsory");
145 12 : keys.addOutputComponent("work","default","scalar","the total work performed changing this restraint");
146 12 : keys.addOutputComponent("force2","default","scalar","the instantaneous value of the squared force due to this bias potential");
147 12 : keys.addOutputComponent("_cntr","default","scalar","one or multiple instances of this quantity can be referenced elsewhere in the input file. "
148 : "these quantities will named with the arguments of the bias followed by "
149 : "the character string _cntr. These quantities give the instantaneous position "
150 : "of the center of the harmonic potential.");
151 12 : keys.addOutputComponent("_work","default","scalar","one or multiple instances of this quantity can be referenced elsewhere in the input file. "
152 : "These quantities will named with the arguments of the bias followed by "
153 : "the character string _work. These quantities tell the user how much work has "
154 : "been done by the potential in dragging the system along the various colvar axis.");
155 12 : keys.addOutputComponent("_kappa","default","scalar","one or multiple instances of this quantity can be referenced elsewhere in the input file. "
156 : "These quantities will named with the arguments of the bias followed by "
157 : "the character string _kappa. These quantities tell the user the time dependent value of kappa.");
158 6 : keys.addDOI("10.1126/science.271.5251.997");
159 6 : keys.addDOI("10.1103/PhysRevLett.78.2690");
160 6 : }
161 :
162 4 : MovingRestraint::MovingRestraint(const ActionOptions&ao):
163 : PLUMED_BIAS_INIT(ao),
164 4 : verse(getNumberOfArguments()),
165 4 : kk(getNumberOfArguments()),
166 4 : aa(getNumberOfArguments()),
167 4 : f(getNumberOfArguments()),
168 8 : dpotdk(getNumberOfArguments()) {
169 4 : parseVector("VERSE",verse);
170 4 : std::vector<long long int> ss(1);
171 4 : ss[0]=-1;
172 10 : for(int i=0;; i++) {
173 : // Read in step
174 28 : if( !parseNumberedVector("STEP",i,ss) ) {
175 : break;
176 : }
177 19 : for(unsigned j=0; j<step.size(); j++) {
178 9 : if(ss[0]<step[j]) {
179 0 : error("in moving restraint step number must always increase");
180 : }
181 : }
182 10 : step.push_back(ss[0]);
183 :
184 : // Try to read kappa
185 20 : if( !parseNumberedVector("KAPPA",i,kk) ) {
186 3 : kk=kappa[i-1];
187 : }
188 10 : kappa.push_back(kk);
189 :
190 : // Now read AT
191 20 : if( !parseNumberedVector("AT",i,aa) ) {
192 1 : aa=at[i-1];
193 : }
194 10 : at.push_back(aa);
195 10 : }
196 4 : checkRead();
197 :
198 14 : for(unsigned i=0; i<step.size(); i++) {
199 10 : log.printf(" step%u %lld\n",i,step[i]);
200 10 : log.printf(" at");
201 22 : for(unsigned j=0; j<at[i].size(); j++) {
202 12 : log.printf(" %f",at[i][j]);
203 : }
204 10 : log.printf("\n");
205 10 : log.printf(" with force constant");
206 22 : for(unsigned j=0; j<kappa[i].size(); j++) {
207 12 : log.printf(" %f",kappa[i][j]);
208 : }
209 10 : log.printf("\n");
210 : };
211 :
212 8 : addComponent("force2");
213 4 : componentIsNotPeriodic("force2");
214 4 : valueForce2=getPntrToComponent("force2");
215 :
216 : // add the centers of the restraint as additional components that can be retrieved (useful for debug)
217 :
218 : std::string comp;
219 9 : for(unsigned i=0; i< getNumberOfArguments() ; i++) {
220 10 : comp=getPntrToArgument(i)->getName()+"_cntr"; // each spring has its own center
221 5 : addComponent(comp);
222 5 : componentIsNotPeriodic(comp);
223 5 : valueCntr.push_back(getPntrToComponent(comp));
224 10 : comp=getPntrToArgument(i)->getName()+"_work"; // each spring has its own work
225 5 : addComponent(comp);
226 5 : componentIsNotPeriodic(comp);
227 5 : valueWork.push_back(getPntrToComponent(comp));
228 10 : comp=getPntrToArgument(i)->getName()+"_kappa"; // each spring has its own kappa
229 5 : addComponent(comp);
230 5 : componentIsNotPeriodic(comp);
231 5 : valueKappa.push_back(getPntrToComponent(comp));
232 5 : work.push_back(0.); // initialize the work value
233 : }
234 8 : addComponent("work");
235 4 : componentIsNotPeriodic("work");
236 4 : valueTotWork=getPntrToComponent("work");
237 :
238 4 : log<<" Bibliography ";
239 8 : log<<cite("Grubmuller, Heymann, and Tavan, Science 271, 997 (1996)")<<"\n";
240 :
241 4 : }
242 :
243 :
244 566 : void MovingRestraint::calculate() {
245 : double ene=0.0;
246 : double totf2=0.0;
247 566 : unsigned narg=getNumberOfArguments();
248 566 : long long int now=getStep();
249 566 : if(now<=step[0]) {
250 3 : kk=kappa[0];
251 3 : aa=at[0];
252 3 : oldaa=at[0];
253 3 : oldk=kappa[0];
254 3 : olddpotdk.resize(narg);
255 3 : oldf.resize(narg);
256 563 : } else if(now>=step[step.size()-1]) {
257 47 : kk=kappa[step.size()-1];
258 47 : aa=at[step.size()-1];
259 : } else {
260 : unsigned i=0;
261 521 : for(i=1; i<step.size()-1; i++)
262 10 : if(now<step[i]) {
263 : break;
264 : }
265 516 : double c2=(now-step[i-1])/double(step[i]-step[i-1]);
266 516 : double c1=1.0-c2;
267 1040 : for(unsigned j=0; j<narg; j++) {
268 524 : kk[j]=(c1*kappa[i-1][j]+c2*kappa[i][j]);
269 : }
270 1040 : for(unsigned j=0; j<narg; j++) {
271 524 : const double a1=at[i-1][j];
272 524 : const double a2=at[i][j];
273 524 : aa[j]=(c1*a1+c2*(a1+difference(j,a1,a2)));
274 : }
275 : }
276 : double tot_work=0.0;
277 1142 : for(unsigned i=0; i<narg; ++i) {
278 576 : const double cv=difference(i,aa[i],getArgument(i)); // this gives: getArgument(i) - aa[i]
279 576 : valueCntr[i]->set(aa[i]);
280 576 : const double k=kk[i];
281 576 : f[i]=-k*cv;
282 576 : if(verse[i]=="U" && cv<0) {
283 0 : continue;
284 : }
285 576 : if(verse[i]=="L" && cv>0) {
286 0 : continue;
287 : }
288 1728 : plumed_assert(verse[i]=="U" || verse[i]=="L" || verse[i]=="B");
289 576 : dpotdk[i]=0.5*cv*cv;
290 576 : if(oldaa.size()==aa.size() && oldf.size()==f.size()) {
291 575 : work[i]+=0.5*(oldf[i]+f[i])*(aa[i]-oldaa[i]) + 0.5*( dpotdk[i]+olddpotdk[i] )*(kk[i]-oldk[i]);
292 : }
293 576 : valueWork[i]->set(work[i]);
294 576 : valueKappa[i]->set(kk[i]);
295 576 : tot_work+=work[i];
296 576 : ene+=0.5*k*cv*cv;
297 576 : setOutputForce(i,f[i]);
298 576 : totf2+=f[i]*f[i];
299 : };
300 566 : valueTotWork->set(tot_work);
301 566 : oldf=f;
302 566 : oldaa=aa;
303 566 : oldk=kk;
304 566 : olddpotdk=dpotdk;
305 566 : setBias(ene);
306 566 : valueForce2->set(totf2);
307 566 : }
308 :
309 : }
310 : }
311 :
312 :
|