Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2017 of Haochuan Chen (excluding colvar_UIestimator.h)
3 : Copyright (c) 2017 of Haohao Fu (colvar_UIestimator.h)
4 :
5 : This program is free software: you can redistribute it and/or modify
6 : it under the terms of the GNU Lesser General Public License as published
7 : by the Free Software Foundation, either version 3 of the License, or
8 : (at your option) any later version.
9 :
10 : This program is distributed in the hope that it will be useful,
11 : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU Lesser General Public License for more details.
14 :
15 : You should have received a copy of the GNU Lesser General Public License
16 : along with this program. If not, see <http://www.gnu.org/licenses/>.
17 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
18 : #ifdef __PLUMED_HAS_BOOST_SERIALIZATION
19 : #include "core/ActionRegister.h"
20 : #include "bias/Bias.h"
21 : #include "core/Atoms.h"
22 : #include "core/PlumedMain.h"
23 : #include "DRR.h"
24 : #include "tools/Random.h"
25 : #include "tools/Tools.h"
26 : #include "colvar_UIestimator.h"
27 :
28 : #include <boost/archive/binary_iarchive.hpp>
29 : #include <boost/archive/binary_oarchive.hpp>
30 : #include <boost/serialization/vector.hpp>
31 : #include <cmath>
32 : #include <fstream>
33 : #include <iomanip>
34 : #include <iostream>
35 : #include <limits>
36 : #include <random>
37 : #include <string>
38 :
39 : using namespace PLMD;
40 : using namespace bias;
41 : using namespace std;
42 :
43 : namespace PLMD {
44 : namespace drr {
45 :
46 : //+PLUMEDOC EABFMOD_BIAS DRR
47 : /*
48 : Used to performed extended-system adaptive biasing force(eABF)
49 :
50 : This method was introduced in \cite Lelievre2007. It is used
51 : on one or more collective variables. This method is also
52 : called dynamic reference restraining(DRR) \cite Zheng2012 . A detailed description
53 : of this module can be found at \cite Chen2018 .
54 :
55 : For each collective variable \f$\xi_i\f$, a fictitious variable \f$\lambda_i\f$
56 : is attached through a spring. The fictitious variable \f$\lambda_i\f$ undergoes
57 : overdamped Langevin dynamics just like \ref EXTENDED_LAGRANGIAN. The ABF
58 : algorithm applies bias force on \f$\lambda_i\f$. The bias force acts on
59 : \f$\lambda_i\f$ is the negative average spring force on \f$\lambda_i\f$, which
60 : enhances the sampling of \f$\lambda_i\f$.
61 :
62 : \f[
63 : F_{bias}(\lambda_i)=k(\lambda_i-\langle\xi_i\rangle_{\lambda_i})
64 : \f]
65 :
66 : If spring force constant k is large enough, then \f$\xi_i\f$ synchronizes with
67 : \f$\lambda_i\f$. The naive(ABF) estimator is just the negative
68 : average spring force of \f$\lambda_i\f$.
69 :
70 : The naive(ABF) estimator is biased. There are unbiased estimators such as
71 : CZAR(Corrected z-averaged restraint) \cite Lesage2016 and UI(Umbrella
72 : Integration).
73 : The CZAR estimates the gradients as:
74 :
75 : \f[
76 : \frac{\partial{A}}{\partial{\xi_i}}\left({\xi}\right)=-\frac{1}{\beta}\frac{\partial\ln\tilde{\rho}\left(\xi\right)}{\partial{\xi_i}}+k\left(\langle\lambda_i\rangle_\xi-\xi_i\right)
77 : \f]
78 :
79 : The UI estimates the gradients as:
80 : \f[
81 : A'(\xi^*)=\frac{{\sum_\lambda}N\left(\xi^*,\lambda\right)\left[\frac{\xi^*-\langle\xi\rangle_\lambda}{\beta\sigma_\lambda^2}-k(\xi^*-\lambda)\right]}{{\sum_\lambda}N\left(\xi^*,\lambda\right)}
82 : \f]
83 :
84 : The code performing UI(colvar_UIestimator.h) is contributed by Haohao Fu \cite Fu2016 .
85 : It may be slow. I only change the Boltzmann constant and output
86 : precision in it. For new version and issues, please see:
87 : https://github.com/fhh2626/colvars
88 :
89 : After running eABF/DRR, the \ref drr_tool utility can be used to extract the gradients and counts files from .drrstate. Naive(ABF) estimator's result is in .abf.grad and .abf.count files and CZAR estimator's result is in .czar.grad and .czar.count files. The additional .zcount and .zgrad files contain the number of samples of \f$\boldsymbol{\xi}\f$, and the negative of \f$\boldsymbol{\xi}\f$-averaged spring forces, respectively, which are mainly for inspecting and debugging purpose. To get PMF, the abf_integrate(https://github.com/Colvars/colvars/tree/master/colvartools) is useful for numerically integrating the .czar.grad file.
90 :
91 : \par Examples
92 :
93 : The following input tells plumed to perform a eABF/DRR simulation on two
94 : torsional angles.
95 : \plumedfile
96 : phi: TORSION ATOMS=5,7,9,15
97 : psi: TORSION ATOMS=7,9,15,17
98 :
99 : DRR ...
100 : LABEL=eabf
101 : ARG=phi,psi
102 : FULLSAMPLES=500
103 : GRID_MIN=-pi,-pi
104 : GRID_MAX=pi,pi
105 : GRID_BIN=180,180
106 : FRICTION=8.0,8.0
107 : TAU=0.5,0.5
108 : OUTPUTFREQ=50000
109 : HISTORYFREQ=500000
110 : ... DRR
111 :
112 : # monitor the two variables, their fictitious variables and applied forces.
113 : PRINT STRIDE=10 ARG=phi,psi,eabf.phi_fict,eabf.psi_fict,eabf.phi_biasforce,eabf.psi_biasforce FILE=COLVAR
114 : \endplumedfile
115 :
116 : The following input tells plumed to perform a eABF/DRR simulation on the
117 : distance of atom 10 and 92. The distance is restraint by \ref LOWER_WALLS and
118 : \ref UPPER_WALLS.
119 : \plumedfile
120 : dist1: DISTANCE ATOMS=10,92
121 : eabf_winall: DRR ARG=dist1 FULLSAMPLES=2000 GRID_MIN=1.20 GRID_MAX=3.20 GRID_BIN=200 FRICTION=8.0 TAU=0.5 OUTPUTFREQ=5000 HISTORYFREQ=500000
122 : uwall: UPPER_WALLS ARG=eabf_winall.dist1_fict AT=3.2 KAPPA=418.4
123 : lwall: LOWER_WALLS ARG=eabf_winall.dist1_fict AT=1.2 KAPPA=418.4
124 : PRINT STRIDE=10 ARG=dist1,eabf_winall.dist1_fict,eabf_winall.dist1_biasforce FILE=COLVAR
125 : \endplumedfile
126 :
127 : It's also possible to run extended generalized adaptive biasing force (egABF) described in \cite Zhao2017 .
128 : An egABF example:
129 : \plumedfile
130 : phi: TORSION ATOMS=5,7,9,15
131 : psi: TORSION ATOMS=7,9,15,17
132 :
133 : DRR ...
134 : LABEL=gabf_phi
135 : ARG=phi
136 : FULLSAMPLES=500
137 : GRID_MIN=-pi
138 : GRID_MAX=pi
139 : GRID_BIN=180
140 : FRICTION=8.0
141 : TAU=0.5
142 : OUTPUTFREQ=50000
143 : HISTORYFREQ=500000
144 : ... DRR
145 :
146 : DRR ...
147 : LABEL=gabf_psi
148 : ARG=psi
149 : FULLSAMPLES=500
150 : GRID_MIN=-pi
151 : GRID_MAX=pi
152 : GRID_BIN=180
153 : FRICTION=8.0
154 : TAU=0.5
155 : OUTPUTFREQ=50000
156 : HISTORYFREQ=500000
157 : ... DRR
158 :
159 : DRR ...
160 : LABEL=gabf_2d
161 : ARG=phi,psi
162 : EXTERNAL_FORCE=gabf_phi.phi_springforce,gabf_psi.psi_springforce
163 : EXTERNAL_FICT=gabf_phi.phi_fictNoPBC,gabf_psi.psi_fictNoPBC
164 : GRID_MIN=-pi,-pi
165 : GRID_MAX=pi,pi
166 : GRID_BIN=180,180
167 : NOBIAS
168 : OUTPUTFREQ=50000
169 : HISTORYFREQ=500000
170 : ... DRR
171 :
172 : PRINT STRIDE=10 ARG=phi,psi FILE=COLVAR
173 : \endplumedfile
174 :
175 : */
176 : //+ENDPLUMEDOC
177 :
178 : using std::vector;
179 : using std::string;
180 :
181 : class DynamicReferenceRestraining : public Bias {
182 : private:
183 : bool firsttime;
184 : bool nobias;
185 : vector<double> fictNoPBC;
186 : vector<double> real;
187 : vector<double> springlength; // spring lengths
188 : vector<double> fict; // coordinates of extended variables
189 : vector<double> vfict; // velocities of extended variables
190 : vector<double> vfict_laststep;
191 : vector<double> ffict; // forces exerted on extended variables
192 : vector<double> fbias; // bias forces from eABF
193 : vector<double> kappa;
194 : vector<double> tau;
195 : vector<double> friction;
196 : vector<double> etemp;
197 : vector<double> ffict_measured;
198 : vector<double> force_external;
199 : vector<double> fict_external;
200 : vector<Value *> biasforceValue;
201 : vector<Value *> springforceValue;
202 : vector<Value *> fictValue;
203 : vector<Value *> vfictValue;
204 : vector<Value *> fictNoPBCValue;
205 : vector<Value *> externalForceValue;
206 : vector<Value *> externalFictValue;
207 : vector<double> c1;
208 : vector<double> c2;
209 : vector<double> mass;
210 : vector<DRRAxis> delim;
211 : string outputname;
212 : string cptname;
213 : string outputprefix;
214 : const size_t ndims;
215 : double dt;
216 : double kbt;
217 : double outputfreq;
218 : double historyfreq;
219 : bool isRestart;
220 : bool useCZARestimator;
221 : bool useUIestimator;
222 : bool mergeHistoryFiles;
223 : bool textoutput;
224 : bool withExternalForce;
225 : bool withExternalFict;
226 : vector<unsigned> reflectingWall;
227 : ABF ABFGrid;
228 : CZAR CZARestimator;
229 : double fullsamples;
230 : vector<double> maxFactors;
231 : UIestimator::UIestimator eabf_UI;
232 : Random rand;
233 :
234 : public:
235 : explicit DynamicReferenceRestraining(const ActionOptions &);
236 : void calculate();
237 : void update();
238 : void save(const string &filename, long long int step);
239 : void load(const string &filename);
240 : void backupFile(const string &filename);
241 : static void registerKeywords(Keywords &keys);
242 : bool is_file_exist(const char *fileName);
243 : };
244 :
245 13809 : PLUMED_REGISTER_ACTION(DynamicReferenceRestraining, "DRR")
246 :
247 16 : void DynamicReferenceRestraining::registerKeywords(Keywords &keys) {
248 16 : Bias::registerKeywords(keys);
249 16 : keys.use("ARG");
250 32 : keys.add("optional", "KAPPA", "specifies that the restraint is harmonic and "
251 : "what the values of the force constants on "
252 : "each of the variables are (default to "
253 : "\\f$k_BT\\f$/(GRID_SPACING)^2)");
254 32 : keys.add("compulsory", "TAU", "0.5", "specifies relaxation time on each of "
255 : "variables are, similar to "
256 : "extended Time Constant in Colvars");
257 32 : keys.add("compulsory", "FRICTION", "8.0",
258 : "add a friction to the variable, similar to extended Langevin Damping "
259 : "in Colvars");
260 32 : keys.add("compulsory", "GRID_MIN", "the lower bounds for the grid (GRID_BIN "
261 : "or GRID_SPACING should be specified)");
262 32 : keys.add("compulsory", "GRID_MAX", "the upper bounds for the grid (GRID_BIN "
263 : "or GRID_SPACING should be specified)");
264 32 : keys.add("compulsory", "REFLECTINGWALL", "0", "whether add reflecting walls "
265 : "for each CV at GRID_MIN and GRID_MAX. Setting non-zero values will "
266 : "enable this feature");
267 32 : keys.add("optional", "GRID_BIN", "the number of bins for the grid");
268 32 : keys.add("optional", "GRID_SPACING", "the approximate grid spacing (to be "
269 : "used as an alternative or together "
270 : "with GRID_BIN)");
271 32 : keys.add("optional", "ZGRID_MIN", "the lower bounds for the grid (ZGRID_BIN"
272 : " or ZGRID_SPACING should be specified)");
273 32 : keys.add("optional", "ZGRID_MAX", "the upper bounds for the grid (ZGRID_BIN"
274 : " or ZGRID_SPACING should be specified)");
275 32 : keys.add("optional", "ZGRID_BIN", "the number of bins for the grid");
276 32 : keys.add("optional", "ZGRID_SPACING", "the approximate grid spacing (to be "
277 : "used as an alternative or together "
278 : "with ZGRID_BIN)");
279 32 : keys.add("optional", "EXTERNAL_FORCE", "use forces from other action instead"
280 : " of internal spring force, this disable the extended system!");
281 32 : keys.add("optional", "EXTERNAL_FICT", "position of external fictitious "
282 : "particles, useful for UIESTIMATOR");
283 32 : keys.add("compulsory", "FULLSAMPLES", "500",
284 : "number of samples in a bin prior to application of the ABF");
285 32 : keys.add("compulsory", "MAXFACTOR", "1.0",
286 : "maximum scaling factor of biasing force");
287 32 : keys.add("compulsory", "OUTPUTFREQ", "write results to a file every N steps");
288 32 : keys.add("optional", "HISTORYFREQ", "save history to a file every N steps");
289 32 : keys.addFlag("NOCZAR", false, "disable the CZAR estimator");
290 32 : keys.addFlag("UI", false,
291 : "enable the umbrella integration estimator");
292 32 : keys.add("optional", "UIRESTARTPREFIX",
293 : "specify the restart files for umbrella integration");
294 32 : keys.add("optional", "OUTPUTPREFIX",
295 : "specify the output prefix (default to the label name)");
296 32 : keys.add("optional", "TEMP", "the system temperature - needed when FRICTION "
297 : "is present. If not provided will be taken from "
298 : "MD code (if available)");
299 32 : keys.add(
300 : "optional", "EXTTEMP",
301 : "the temperature of extended variables (default to system temperature)");
302 32 : keys.add("optional", "DRR_RFILE",
303 : "specifies the restart file (.drrstate file)");
304 32 : keys.addFlag("NOBIAS", false, "DO NOT apply bias forces.");
305 32 : keys.addFlag("TEXTOUTPUT", false, "use text output for grad and count files "
306 : "instead of boost::serialization binary "
307 : "output");
308 32 : keys.addFlag("MERGEHISTORYFILES", false, "output all historic results "
309 : "to a single file rather than multiple .drrstate files. "
310 : "This option is effective only when textOutput is on.");
311 16 : componentsAreNotOptional(keys);
312 32 : keys.addOutputComponent(
313 : "_fict", "default",
314 : "one or multiple instances of this quantity can be referenced "
315 : "elsewhere in the input file. "
316 : "These quantities will named with the arguments of the bias followed by "
317 : "the character string _tilde. It is possible to add forces on these "
318 : "variable.");
319 32 : keys.addOutputComponent(
320 : "_vfict", "default",
321 : "one or multiple instances of this quantity can be referenced "
322 : "elsewhere in the input file. "
323 : "These quantities will named with the arguments of the bias followed by "
324 : "the character string _tilde. It is NOT possible to add forces on these "
325 : "variable.");
326 32 : keys.addOutputComponent(
327 : "_biasforce", "default",
328 : "The bias force from eABF/DRR of the fictitious particle.");
329 32 : keys.addOutputComponent("_springforce", "default", "Spring force between real CVs and extended CVs");
330 32 : keys.addOutputComponent("_fictNoPBC", "default",
331 : "the positions of fictitious particles (without PBC).");
332 16 : }
333 :
334 12 : DynamicReferenceRestraining::DynamicReferenceRestraining(
335 12 : const ActionOptions &ao)
336 12 : : PLUMED_BIAS_INIT(ao), firsttime(true), nobias(false),
337 12 : fictNoPBC(getNumberOfArguments(), 0.0), real(getNumberOfArguments(), 0.0),
338 12 : springlength(getNumberOfArguments(), 0.0),
339 12 : fict(getNumberOfArguments(), 0.0), vfict(getNumberOfArguments(), 0.0),
340 12 : vfict_laststep(getNumberOfArguments(), 0.0),
341 12 : ffict(getNumberOfArguments(), 0.0), fbias(getNumberOfArguments(), 0.0),
342 12 : kappa(getNumberOfArguments(), 0.0), tau(getNumberOfArguments(), 0.0),
343 12 : friction(getNumberOfArguments(), 0.0), etemp(getNumberOfArguments(), 0.0),
344 12 : ffict_measured(getNumberOfArguments(), 0.0),
345 12 : biasforceValue(getNumberOfArguments(), NULL),
346 12 : springforceValue(getNumberOfArguments(), NULL),
347 12 : fictValue(getNumberOfArguments(), NULL),
348 12 : vfictValue(getNumberOfArguments(), NULL),
349 12 : fictNoPBCValue(getNumberOfArguments(), NULL),
350 12 : externalForceValue(getNumberOfArguments(), NULL),
351 12 : externalFictValue(getNumberOfArguments(), NULL),
352 12 : c1(getNumberOfArguments(), 0.0),
353 12 : c2(getNumberOfArguments(), 0.0), mass(getNumberOfArguments(), 0.0),
354 12 : delim(getNumberOfArguments()), outputname(""), cptname(""),
355 12 : outputprefix(""), ndims(getNumberOfArguments()), dt(0.0), kbt(0.0),
356 12 : outputfreq(0.0), historyfreq(-1.0), isRestart(false),
357 12 : useCZARestimator(true), useUIestimator(false), mergeHistoryFiles(false),
358 12 : textoutput(false), withExternalForce(false), withExternalFict(false),
359 12 : reflectingWall(getNumberOfArguments(), 0),
360 36 : maxFactors(getNumberOfArguments(), 1.0) {
361 12 : log << "eABF/DRR: You now are using the extended adaptive biasing "
362 : "force(eABF) method."
363 12 : << '\n';
364 12 : log << "eABF/DRR: Some people also refer to it as dynamic reference "
365 : "restraining(DRR) method."
366 12 : << '\n';
367 12 : log << "eABF/DRR: Currently the CZAR and naive(ABF on extended variables) "
368 : "estimator is enabled by default."
369 12 : << '\n';
370 12 : log << "eABF/DRR: For reasons of performance, the umbrella integration "
371 : "estimator is not enabled by default."
372 12 : << '\n';
373 12 : log << "eABF/DRR: This method is originally implemented in "
374 : "colvars(https://github.com/colvars/colvars)."
375 12 : << '\n';
376 12 : log << "eABF/DRR: The code in plumed is heavily modified from "
377 : "ExtendedLagrangian.cpp and doesn't implemented all variants of "
378 : "eABF/DRR."
379 12 : << '\n';
380 12 : log << "eABF/DRR: The thermostat used here may be different from Colvars."
381 12 : << '\n';
382 12 : log << "eABF/DRR: To integrate the gradients file, you can use abf_integrate "
383 : "from https://github.com/colvars/colvars/tree/master/colvartools."
384 12 : << '\n';
385 12 : log << "eABF/DRR: Please read relevant articles and use this biasing "
386 : "method carefully!"
387 12 : << '\n';
388 12 : parseFlag("NOBIAS", nobias);
389 12 : parseFlag("UI", useUIestimator);
390 12 : bool noCZAR = false;
391 12 : parseFlag("NOCZAR", noCZAR);
392 : // noCZAR == false ? useCZARestimator = true : useCZARestimator = false;
393 12 : parseFlag("TEXTOUTPUT", textoutput);
394 12 : parseFlag("MERGEHISTORYFILES", mergeHistoryFiles);
395 12 : parseVector("TAU", tau);
396 12 : parseVector("FRICTION", friction);
397 12 : parseVector("EXTTEMP", etemp);
398 12 : parseVector("KAPPA", kappa);
399 12 : parseVector("REFLECTINGWALL", reflectingWall);
400 12 : double temp = -1.0;
401 12 : parse("TEMP", temp);
402 12 : parse("FULLSAMPLES", fullsamples);
403 12 : parseVector("MAXFACTOR", maxFactors);
404 12 : parse("OUTPUTFREQ", outputfreq);
405 12 : parse("HISTORYFREQ", historyfreq);
406 24 : parse("OUTPUTPREFIX", outputprefix);
407 : string restart_prefix;
408 24 : parse("DRR_RFILE", restart_prefix);
409 : string uirprefix;
410 12 : parse("UIRESTARTPREFIX", uirprefix);
411 12 : parseArgumentList("EXTERNAL_FORCE", externalForceValue);
412 24 : parseArgumentList("EXTERNAL_FICT", externalFictValue);
413 12 : if (externalForceValue.empty()) {
414 11 : withExternalForce = false;
415 1 : } else if (externalForceValue.size() != ndims) {
416 0 : error("eABF/DRR: Number of forces doesn't match ARGS!");
417 : } else {
418 1 : withExternalForce = true;
419 : }
420 12 : if (withExternalForce && useUIestimator) {
421 1 : if (externalFictValue.empty()) {
422 0 : error("eABF/DRR: No external fictitious particles specified. UI estimator needs it.");
423 1 : } else if(externalFictValue.size() != ndims) {
424 0 : error("eABF/DRR: Number of fictitious particles doesn't match ARGS!");
425 : } else {
426 1 : withExternalFict = true;
427 : }
428 : }
429 12 : if (temp >= 0.0) {
430 12 : kbt = plumed.getAtoms().getKBoltzmann() * temp;
431 : } else {
432 0 : kbt = plumed.getAtoms().getKbT();
433 0 : if (kbt <= std::numeric_limits<double>::epsilon()) {
434 0 : error("eABF/DRR: It seems the MD engine does not setup the temperature correctly for PLUMED."
435 : "Please set it by the TEMP keyword manually.");
436 : }
437 : }
438 12 : if (fullsamples < 0.5) {
439 0 : fullsamples = 500.0;
440 0 : log << "eABF/DRR: The fullsamples parametre is not set. Set it to "
441 : "500(default)."
442 0 : << '\n';
443 : }
444 12 : if (getRestart()) {
445 1 : if (restart_prefix.length() != 0) {
446 1 : isRestart = true;
447 1 : firsttime = false;
448 1 : load(restart_prefix);
449 : } else {
450 0 : log << "eABF/DRR: You don't specify the file for restarting." << '\n';
451 0 : log << "eABF/DRR: So I assume you are splitting windows." << '\n';
452 0 : isRestart = false;
453 0 : firsttime = true;
454 : }
455 : }
456 :
457 12 : vector<string> gmin(ndims);
458 12 : vector<string> zgmin(ndims);
459 12 : parseVector("GRID_MIN", gmin);
460 24 : parseVector("ZGRID_MIN", zgmin);
461 12 : if (gmin.size() != ndims) {
462 0 : error("eABF/DRR: not enough values for GRID_MIN");
463 : }
464 12 : if (zgmin.size() != ndims) {
465 33 : log << "eABF/DRR: You didn't specify ZGRID_MIN. " << '\n'
466 11 : << "eABF/DRR: The GRID_MIN will be used instead.";
467 11 : zgmin = gmin;
468 : }
469 12 : vector<string> gmax(ndims);
470 12 : vector<string> zgmax(ndims);
471 12 : parseVector("GRID_MAX", gmax);
472 24 : parseVector("ZGRID_MAX", zgmax);
473 12 : if (gmax.size() != ndims) {
474 0 : error("eABF/DRR: not enough values for GRID_MAX");
475 : }
476 12 : if (zgmax.size() != ndims) {
477 33 : log << "eABF/DRR: You didn't specify ZGRID_MAX. " << '\n'
478 11 : << "eABF/DRR: The GRID_MAX will be used instead.";
479 11 : zgmax = gmax;
480 : }
481 12 : vector<unsigned> gbin(ndims);
482 12 : vector<unsigned> zgbin(ndims);
483 12 : vector<double> gspacing(ndims);
484 12 : vector<double> zgspacing(ndims);
485 12 : parseVector("GRID_BIN", gbin);
486 12 : parseVector("ZGRID_BIN", zgbin);
487 12 : parseVector("GRID_SPACING", gspacing);
488 24 : parseVector("ZGRID_SPACING", zgspacing);
489 12 : if (gbin.size() != ndims) {
490 3 : log << "eABF/DRR: You didn't specify GRID_BIN. Trying to use GRID_SPACING "
491 : "instead."
492 3 : << '\n';
493 3 : if (gspacing.size() != ndims) {
494 0 : error("eABF/DRR: not enough values for GRID_BIN");
495 : } else {
496 3 : gbin.resize(ndims);
497 6 : for (size_t i = 0; i < ndims; ++i) {
498 : double l, h;
499 3 : PLMD::Tools::convert(gmin[i], l);
500 3 : PLMD::Tools::convert(gmax[i], h);
501 3 : gbin[i] = std::nearbyint((h - l) / gspacing[i]);
502 3 : gspacing[i] = (h - l) / gbin[i];
503 3 : log << "GRID_BIN[" << i << "] is " << gbin[i] << '\n';
504 : }
505 : }
506 : }
507 12 : if (zgbin.size() != ndims) {
508 11 : log << "eABF/DRR: You didn't specify ZGRID_BIN. Trying to use ZGRID_SPACING instead." << '\n';
509 11 : if (zgspacing.size() != ndims) {
510 11 : log << "eABF/DRR: You didn't specify ZGRID_SPACING. Trying to use GRID_SPACING or GRID_BIN instead." << '\n';
511 11 : zgbin = gbin;
512 11 : zgspacing = gspacing;
513 : } else {
514 0 : zgbin.resize(ndims);
515 0 : for (size_t i = 0; i < ndims; ++i) {
516 : double l, h;
517 0 : PLMD::Tools::convert(zgmin[i], l);
518 0 : PLMD::Tools::convert(zgmax[i], h);
519 0 : zgbin[i] = std::nearbyint((h - l) / zgspacing[i]);
520 0 : zgspacing[i] = (h - l) / zgbin[i];
521 0 : log << "ZGRID_BIN[" << i << "] is " << zgbin[i] << '\n';
522 : }
523 : }
524 : }
525 12 : checkRead();
526 :
527 : // Set up kbt for extended system
528 12 : log << "eABF/DRR: The fullsamples is " << fullsamples << '\n';
529 12 : log << "eABF/DRR: The kbt(real system) is " << kbt << '\n';
530 12 : dt = getTimeStep() * getStride();
531 12 : log << "eABF/DRR: timestep = " << getTimeStep() << " ps with stride = " << getStride() << " steps\n";
532 12 : vector<double> ekbt(ndims, 0.0);
533 12 : if (etemp.size() != ndims) {
534 12 : etemp.assign(ndims, kbt / plumed.getAtoms().getKBoltzmann());
535 : }
536 12 : if (tau.size() != ndims) {
537 0 : tau.assign(ndims, 0.5);
538 : }
539 12 : if (friction.size() != ndims) {
540 0 : friction.assign(ndims, 8.0);
541 : }
542 12 : if (maxFactors.size() != ndims) {
543 0 : maxFactors.assign(ndims, 1.0);
544 : }
545 31 : for (size_t i = 0; i < ndims; ++i) {
546 19 : log << "eABF/DRR: The maximum scaling factor [" << i << "] is " << maxFactors[i] << '\n';
547 19 : if (maxFactors[i] > 1.0) {
548 0 : log << "eABF/DRR: Warning! The maximum scaling factor larger than 1.0 is not recommended!" << '\n';
549 : }
550 : }
551 31 : for (size_t i = 0; i < ndims; ++i) {
552 19 : ekbt[i] = etemp[i] * plumed.getAtoms().getKBoltzmann();
553 19 : log << "eABF/DRR: The kbt(extended system) of [" << i << "] is " << ekbt[i]
554 19 : << '\n';
555 19 : log << "eABF/DRR: relaxation time tau [" << i << "] is " << tau[i] << '\n';
556 19 : log << "eABF/DRR: Extended variable [" << i << "] has friction: " << friction[i] << '\n';
557 : }
558 :
559 : // Set up the force grid
560 12 : vector<DRRAxis> zdelim(ndims);
561 31 : for (size_t i = 0; i < ndims; ++i) {
562 19 : log << "eABF/DRR: The " << i << " dimensional grid minimum is " << gmin[i]
563 19 : << '\n';
564 19 : log << "eABF/DRR: The " << i << " dimensional grid maximum is " << gmax[i]
565 19 : << '\n';
566 19 : log << "eABF/DRR: The " << i << " dimensional grid has " << gbin[i]
567 19 : << " bins" << '\n';
568 19 : log << "eABF/DRR: The " << i << " dimensional zgrid minimum is " << zgmin[i]
569 19 : << '\n';
570 19 : log << "eABF/DRR: The " << i << " dimensional zgrid maximum is " << zgmax[i]
571 19 : << '\n';
572 19 : log << "eABF/DRR: The " << i << " dimensional zgrid has " << zgbin[i]
573 19 : << " bins" << '\n';
574 : double l, h;
575 19 : PLMD::Tools::convert(gmin[i], l);
576 19 : PLMD::Tools::convert(gmax[i], h);
577 19 : delim[i].set(l, h, gbin[i]);
578 : double zl,zh;
579 19 : PLMD::Tools::convert(zgmin[i], zl);
580 19 : PLMD::Tools::convert(zgmax[i], zh);
581 19 : zdelim[i].set(zl, zh, zgbin[i]);
582 : }
583 12 : if (kappa.size() != ndims) {
584 9 : kappa.resize(ndims, 0.0);
585 25 : for (size_t i = 0; i < ndims; ++i) {
586 16 : if (kappa[i] <= 0) {
587 16 : log << "eABF/DRR: The spring force constant kappa[" << i
588 16 : << "] is not set." << '\n';
589 16 : kappa[i] = ekbt[i] / (delim[i].getWidth() * delim[i].getWidth());
590 16 : log << "eABF/DRR: set kappa[" << i
591 16 : << "] according to bin width(ekbt/(binWidth^2))." << '\n';
592 : }
593 16 : log << "eABF/DRR: The spring force constant kappa[" << i << "] is "
594 16 : << std::fixed << std::setprecision(10) << kappa[i] << '\n';
595 : }
596 : } else {
597 3 : log << "eABF/DRR: The kappa have been set manually." << '\n';
598 6 : for (size_t i = 0; i < ndims; ++i) {
599 3 : log << "eABF/DRR: The spring force constant kappa[" << i << "] is "
600 3 : << std::fixed << std::setprecision(10) << kappa[i] << '\n';
601 : }
602 : }
603 :
604 31 : for (size_t i = 0; i < ndims; ++i) {
605 19 : mass[i] = kappa[i] * tau[i] * tau[i] / (4 * pi * pi);
606 19 : log << "eABF/DRR: Fictitious mass[" << i << "] is " << mass[i] << '\n';
607 19 : c1[i] = exp(-0.5 * friction[i] * dt);
608 19 : c2[i] = sqrt(ekbt[i] * (1.0 - c1[i] * c1[i]) / mass[i]);
609 : }
610 :
611 31 : for (size_t i = 0; i < ndims; ++i) {
612 : // Position output
613 19 : string comp = getPntrToArgument(i)->getName() + "_fict";
614 19 : addComponentWithDerivatives(comp);
615 19 : if (getPntrToArgument(i)->isPeriodic()) {
616 : string a, b;
617 : double c, d;
618 16 : getPntrToArgument(i)->getDomain(a, b);
619 16 : getPntrToArgument(i)->getDomain(c, d);
620 16 : componentIsPeriodic(comp, a, b);
621 16 : delim[i].setPeriodicity(c, d);
622 : zdelim[i].setPeriodicity(c, d);
623 : } else {
624 3 : componentIsNotPeriodic(comp);
625 : }
626 19 : fictValue[i] = getPntrToComponent(comp);
627 : // Velocity output
628 19 : comp = getPntrToArgument(i)->getName() + "_vfict";
629 19 : addComponent(comp);
630 19 : componentIsNotPeriodic(comp);
631 19 : vfictValue[i] = getPntrToComponent(comp);
632 : // Bias force from eABF/DRR output
633 19 : comp = getPntrToArgument(i)->getName() + "_biasforce";
634 19 : addComponent(comp);
635 19 : componentIsNotPeriodic(comp);
636 19 : biasforceValue[i] = getPntrToComponent(comp);
637 : // Spring force output, useful for perform egABF and other analysis
638 19 : comp = getPntrToArgument(i)->getName() + "_springforce";
639 19 : addComponent(comp);
640 19 : componentIsNotPeriodic(comp);
641 19 : springforceValue[i] = getPntrToComponent(comp);
642 : // Position output, no pbc-aware
643 19 : comp = getPntrToArgument(i)->getName() + "_fictNoPBC";
644 19 : addComponent(comp);
645 19 : componentIsNotPeriodic(comp);
646 19 : fictNoPBCValue[i] = getPntrToComponent(comp);
647 : }
648 :
649 12 : if (outputprefix.length() == 0) {
650 0 : outputprefix = getLabel();
651 : }
652 : // Support multiple replica
653 12 : string replica_suffix = plumed.getSuffix();
654 12 : if (replica_suffix.empty() == false) {
655 4 : outputprefix = outputprefix + replica_suffix;
656 : }
657 12 : outputname = outputprefix + ".drrstate";
658 12 : cptname = outputprefix + ".cpt.drrstate";
659 :
660 12 : if (!isRestart) {
661 : // If you want to use on-the-fly text output for CZAR and naive estimator,
662 : // you should turn it to true first!
663 11 : ABFGrid = ABF(delim, ".abf", fullsamples, maxFactors, textoutput);
664 : // Just initialize it even useCZARestimator is off.
665 22 : CZARestimator = CZAR(zdelim, ".czar", kbt, textoutput);
666 11 : log << "eABF/DRR: The init function of the grid is finished." << '\n';
667 : } else {
668 : // ABF Parametres are not saved in binary files
669 : // So manully set them up
670 1 : ABFGrid.setParameters(fullsamples, maxFactors);
671 : }
672 12 : if (useCZARestimator) {
673 12 : log << "eABF/DRR: Using corrected z-average restraint estimator of gradients" << '\n';
674 24 : log << " Bibliography " << plumed.cite("Lesage, Lelièvre, Stoltz and Hénin, "
675 24 : "J. Phys. Chem. B 3676, 121 (2017)");
676 12 : log << plumed.cite("Darve and Pohorille, J. Chem. Phys. 9169, 115 (2001)") << '\n';
677 : }
678 12 : if (useUIestimator) {
679 9 : log << "eABF/DRR: Using umbrella integration(Zheng and Yang's) estimator "
680 : "of gradients."
681 9 : << '\n';
682 9 : log << "eABF/DRR: The UI estimator code is contributed by Haohao Fu."
683 9 : << '\n';
684 18 : log << " Bibliography " << plumed.cite(
685 18 : "Fu, Shao, Chipot and Cai, J. Chem. Theory Comput. 3506, 12 (2016)");
686 18 : log << plumed.cite("Zheng and Yang, J. Chem. Theory Comput. 810, 8 (2012)");
687 9 : log << plumed.cite("Darve and Pohorille, J. Chem. Phys. 9169, 115 (2001)") << '\n';
688 9 : vector<double> lowerboundary(zdelim.size(), 0);
689 9 : vector<double> upperboundary(zdelim.size(), 0);
690 9 : vector<double> width(zdelim.size(), 0);
691 24 : for (size_t i = 0; i < zdelim.size(); ++i) {
692 15 : lowerboundary[i] = zdelim[i].getMin();
693 15 : upperboundary[i] = zdelim[i].getMax();
694 15 : width[i] = zdelim[i].getWidth();
695 : }
696 : vector<string> input_filename;
697 : bool uirestart = false;
698 9 : if (isRestart && (uirprefix.length() != 0)) {
699 1 : input_filename.push_back(uirprefix);
700 : uirestart = true;
701 : }
702 9 : if (isRestart && (uirprefix.length() == 0)) {
703 0 : input_filename.push_back(outputprefix);
704 : }
705 9 : eabf_UI = UIestimator::UIestimator(
706 9 : lowerboundary, upperboundary, width, kappa, outputprefix, int(outputfreq),
707 18 : uirestart, input_filename, kbt / plumed.getAtoms().getKBoltzmann());
708 9 : }
709 24 : }
710 :
711 123 : void DynamicReferenceRestraining::calculate() {
712 123 : long long int step_now = getStep();
713 123 : if (firsttime) {
714 28 : for (size_t i = 0; i < ndims; ++i) {
715 17 : fict[i] = getArgument(i);
716 17 : if(reflectingWall[i] && (fict[i]>=delim[i].getMax() || fict[i]<=delim[i].getMin())) {
717 0 : error("eABF/DRR: initial position not in the range of [gmin, gmax]!");
718 : }
719 : }
720 11 : firsttime = false;
721 : }
722 123 : if (step_now != 0) {
723 111 : if ((step_now % int(outputfreq)) == 0) {
724 15 : save(outputname, step_now);
725 15 : if (textoutput) {
726 10 : ABFGrid.writeAll(outputprefix);
727 10 : if (useCZARestimator) {
728 10 : CZARestimator.writeAll(outputprefix);
729 10 : CZARestimator.writeZCountZGrad(outputprefix);
730 : }
731 : }
732 : }
733 111 : if (historyfreq > 0 && (step_now % int(historyfreq)) == 0) {
734 4 : if (textoutput) {
735 : const string filename =
736 4 : outputprefix + ".another.drrstate";
737 4 : save(filename, step_now);
738 : const string textfilename =
739 4 : mergeHistoryFiles ? (outputprefix + ".hist") : (outputprefix + "." + std::to_string(step_now));
740 4 : ABFGrid.writeAll(textfilename, mergeHistoryFiles);
741 4 : if (useCZARestimator) {
742 4 : CZARestimator.writeAll(textfilename, mergeHistoryFiles);
743 4 : CZARestimator.writeZCountZGrad(textfilename, mergeHistoryFiles);
744 : }
745 : } else {
746 : const string filename =
747 0 : outputprefix + "." + std::to_string(step_now) + ".drrstate";
748 0 : save(filename, step_now);
749 : }
750 : }
751 111 : if (getCPT()) {
752 0 : log << "eABF/DRR: The MD engine is writing checkpoint so we also write a "
753 : "DRR state file at step: "
754 0 : << step_now << ".\n";
755 0 : save(cptname, step_now);
756 : }
757 : }
758 123 : if (withExternalForce == false) {
759 : double ene = 0.0;
760 294 : for (size_t i = 0; i < ndims; ++i) {
761 183 : real[i] = getArgument(i);
762 183 : springlength[i] = difference(i, fict[i], real[i]);
763 183 : fictNoPBC[i] = real[i] - springlength[i];
764 183 : double f = -kappa[i] * springlength[i];
765 183 : ffict_measured[i] = -f;
766 183 : ene += 0.5 * kappa[i] * springlength[i] * springlength[i];
767 : setOutputForce(i, f);
768 183 : ffict[i] = -f;
769 183 : fict[i] = fictValue[i]->bringBackInPbc(fict[i]);
770 183 : fictValue[i]->set(fict[i]);
771 183 : vfictValue[i]->set(vfict_laststep[i]);
772 183 : springforceValue[i]->set(ffict_measured[i]);
773 183 : fictNoPBCValue[i]->set(fictNoPBC[i]);
774 : }
775 : setBias(ene);
776 111 : ABFGrid.store_getbias(fict, ffict_measured, fbias);
777 : } else {
778 36 : for (size_t i = 0; i < ndims; ++i) {
779 24 : real[i] = getArgument(i);
780 24 : ffict_measured[i] = externalForceValue[i]->get();
781 24 : if (withExternalFict) {
782 24 : fictNoPBC[i] = externalFictValue[i]->get();
783 : }
784 24 : springforceValue[i]->set(ffict_measured[i]);
785 24 : fictNoPBCValue[i]->set(fictNoPBC[i]);
786 : }
787 12 : ABFGrid.store_getbias(real, ffict_measured, fbias);
788 12 : if (!nobias) {
789 0 : for (size_t i = 0; i < ndims; ++i) {
790 0 : setOutputForce(i, fbias[i]);
791 : }
792 : }
793 : }
794 123 : if (useCZARestimator) {
795 123 : CZARestimator.store(real, ffict_measured);
796 : }
797 123 : if (useUIestimator) {
798 87 : eabf_UI.update_output_filename(outputprefix);
799 174 : eabf_UI.update(int(step_now), real, fictNoPBC);
800 : }
801 123 : }
802 :
803 123 : void DynamicReferenceRestraining::update() {
804 123 : if (withExternalForce == false) {
805 294 : for (size_t i = 0; i < ndims; ++i) {
806 : // consider additional forces on the fictitious particle
807 : // (e.g. MetaD stuff)
808 183 : ffict[i] += fictValue[i]->getForce();
809 183 : if (!nobias) {
810 183 : ffict[i] += fbias[i];
811 : }
812 183 : biasforceValue[i]->set(fbias[i]);
813 : // update velocity (half step)
814 183 : vfict[i] += ffict[i] * 0.5 * dt / mass[i];
815 : // thermostat (half step)
816 183 : vfict[i] = c1[i] * vfict[i] + c2[i] * rand.Gaussian();
817 : // save full step velocity to be dumped at next step
818 183 : vfict_laststep[i] = vfict[i];
819 : // thermostat (half step)
820 183 : vfict[i] = c1[i] * vfict[i] + c2[i] * rand.Gaussian();
821 : // update velocity (half step)
822 183 : vfict[i] += ffict[i] * 0.5 * dt / mass[i];
823 : // update position (full step)
824 183 : fict[i] += vfict[i] * dt;
825 : // reflecting boundary
826 183 : if (reflectingWall[i]) {
827 24 : if (fict[i]<delim[i].getMin()) {
828 1 : fict[i]=delim[i].getMin()+(delim[i].getMin()-fict[i]);
829 1 : vfict[i]*=-1.0;
830 23 : } else if (fict[i]>delim[i].getMax()) {
831 0 : fict[i]=delim[i].getMax()-(fict[i]-delim[i].getMax());
832 0 : vfict[i]*=-1.0;
833 : }
834 : }
835 : }
836 : }
837 123 : }
838 :
839 19 : void DynamicReferenceRestraining::save(const string &filename,
840 : long long int step) {
841 19 : std::ofstream out;
842 19 : out.open(filename.c_str(), std::ios::binary);
843 19 : boost::archive::binary_oarchive oa(out);
844 19 : oa << step << fict << vfict << vfict_laststep << ffict << ABFGrid
845 19 : << CZARestimator;
846 19 : out.close();
847 19 : }
848 :
849 1 : void DynamicReferenceRestraining::load(const string &rfile_prefix) {
850 1 : string replica_suffix = plumed.getSuffix();
851 : string filename;
852 1 : if (replica_suffix.empty() == true) {
853 2 : filename = rfile_prefix + ".drrstate";
854 : } else {
855 0 : filename = rfile_prefix + "." + replica_suffix + ".drrstate";
856 : }
857 1 : std::ifstream in;
858 : long long int step;
859 1 : in.open(filename.c_str(), std::ios::binary);
860 1 : log << "eABF/DRR: Read restart file: " << filename << '\n';
861 1 : boost::archive::binary_iarchive ia(in);
862 1 : ia >> step >> fict >> vfict >> vfict_laststep >> ffict >> ABFGrid >>
863 1 : CZARestimator;
864 1 : in.close();
865 1 : log << "eABF/DRR: Restart at step: " << step << '\n';
866 1 : backupFile(filename);
867 2 : }
868 :
869 1 : void DynamicReferenceRestraining::backupFile(const string &filename) {
870 : bool isSuccess = false;
871 : long int i = 0;
872 2 : while (!isSuccess) {
873 : // If libstdc++ support C++17 we can simplify following code.
874 2 : const string bckname = "bck." + filename + "." + std::to_string(i);
875 1 : if (is_file_exist(bckname.c_str())) {
876 0 : ++i;
877 : } else {
878 1 : log << "eABF/DRR: Backup original restart file to " << bckname << '\n';
879 1 : std::ifstream src(filename.c_str(), std::ios::binary);
880 1 : std::ofstream dst(bckname.c_str(), std::ios::binary);
881 1 : dst << src.rdbuf();
882 1 : src.close();
883 1 : dst.close();
884 : isSuccess = true;
885 1 : }
886 : }
887 1 : }
888 :
889 : // Copy from
890 : // stackoverflow(https://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c)
891 1 : bool DynamicReferenceRestraining::is_file_exist(const char *fileName) {
892 1 : std::ifstream infile(fileName);
893 1 : return infile.good();
894 1 : }
895 : }
896 : }
897 :
898 : #endif
|