SSAGES  0.1
A MetaDynamics Package
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Groups Pages
ABF.cpp
1 
21 #include "ABF.h"
22 #include "CVs/CVManager.h"
23 #include "Drivers/DriverException.h"
24 #include "Validator/ObjectRequirement.h"
25 #include "Snapshot.h"
26 #include "schema.h"
27 #include <algorithm>
28 #include <fstream>
29 #include <iostream>
30 
31 using namespace Json;
32 // "Adaptive biasing force method for scalar and vector free energy calculations"
33 // Darve, Rodriguez-Gomez, Pohorille
34 // J. Chem. Phys. (2008)
35 namespace SSAGES
36 {
37 
38  // Pre-simulation hook.
39  void ABF::PreSimulation(Snapshot* snapshot, const CVManager& cvmanager)
40  {
41 
42  // Open/close outfile to create it fresh.
43  if(world_.rank() == 0)
44  {
45  worldout_.open(filename_);
46  worldout_.close();
47  }
48 
49  // Convenience. Number of CVs.
50  auto cvs = cvmanager.GetCVs(cvmask_);
51  dim_ = cvs.size();
52 
53  // Size and initialize Fold_
54  Fold_.setZero(dim_);
55 
56  // Size and initialize Fworld_ and Nworld_
57  auto nel = 1;
58 
59  // Initialize biases.
60  biases_.resize(snapshot->GetPositions().size(), Vector3{0, 0, 0});
61 
62  // Initialize w \dot p's for finite difference.
63  wdotp1_.setZero(dim_);
64  wdotp2_.setZero(dim_);
65  }
66 
68 
76  void ABF::PostIntegration(Snapshot* snapshot, const CVManager& cvmanager)
77  {
78 
79  ++iteration_;
80 
81  // Gather information.
82  auto cvs = cvmanager.GetCVs(cvmask_);
83  auto& vels = snapshot->GetVelocities();
84  auto& mass = snapshot->GetMasses();
85  auto& forces = snapshot->GetForces();
86  auto n = snapshot->GetNumAtoms();
87 
88 
90  //int coord = histCoords(cvs);
91 
93  Eigen::MatrixXd J(dim_, 3*n);
94 
96  std::vector<double> cvVals(dim_);
97 
98  // Fill J and CV. Each column represents grad(CV) with flattened Cartesian elements.
99  for(int i = 0; i < dim_; ++i)
100  {
101  auto& grad = cvs[i]->GetGradient();
102  for(size_t j = 0; j < n; ++j)
103  J.block<1, 3>(i,3*j) = grad[j];
104  cvVals[i] = cvs[i]->GetValue();
105  }
106 
107  //* Calculate W using Darve's approach (http://mc.stanford.edu/cgi-bin/images/0/06/Darve_2008.pdf).
108  Eigen::MatrixXd Jmass = J.transpose();
109  if(massweigh_)
110  {
111  for(size_t i = 0; i < forces.size(); ++i)
112  Jmass.block(3*i, 0, 3, dim_) = Jmass.block(3*i, 0, 3, dim_)/mass[i];
113  }
114 
115  Eigen::MatrixXd Minv = J*Jmass;
116  MPI_Allreduce(MPI_IN_PLACE, Minv.data(), Minv.size(), MPI_DOUBLE, MPI_SUM, comm_);
117  Eigen::MatrixXd Wt = Minv.inverse()*Jmass.transpose();
118  // Fill momenta.
119  Eigen::VectorXd momenta(3*vels.size());
120  for(size_t i = 0; i < vels.size(); ++i)
121  momenta.segment<3>(3*i) = mass[i]*vels[i];
122 
123  // Compute dot(w,p)
124  Eigen::VectorXd wdotp = Wt*momenta;
125 
126 
127  // Reduce dot product across processors.
128  MPI_Allreduce(MPI_IN_PLACE, wdotp.data(), wdotp.size(), MPI_DOUBLE, MPI_SUM, comm_);
129 
130 
131  // Compute d(wdotp)/dt second order backwards finite difference.
132  // Adding old force removes bias.
133  Eigen::VectorXd dwdotpdt = unitconv_*(1.5*wdotp - 2.0*wdotp1_ + 0.5*wdotp2_)/timestep_ + Fold_;
134 
135  // If we are in bounds, sum force into running total.
136  if(boundsCheck(cvVals))
137  {
138  for(size_t i=0; i<dim_; ++i)
139  F_[i]->at(cvVals) += dwdotpdt[i];
140  N_->at(cvVals)++;
141  }
142 
143  // Reduce data across processors.
144  for(size_t i=0; i<dim_; ++i)
145  MPI_Allreduce(F_[i]->data(), Fworld_[i]->data(), (F_[i]->size()), MPI_DOUBLE, MPI_SUM, world_);
146  MPI_Allreduce(N_->data(), Nworld_->data(), N_->size(), MPI_INT, MPI_SUM, world_);
147 
148  // If we are in bounds, store the old summed force.
149  if(boundsCheck(cvVals))
150  {
151  for(size_t i=0; i < dim_; ++i)
152  Fold_[i] = Fworld_[i]->at(cvVals)/std::max(min_, Nworld_->at(cvVals));
153  }
154 
155  // Update finite difference time derivatives.
156  wdotp2_ = wdotp1_;
157  wdotp1_ = wdotp;
158 
159  // Write out data to file.
160  if(iteration_ % FBackupInterv_ == 0)
161  WriteData();
162 
163  // Calculate the bias from averaged F at current CV coordinates.
164  // Or apply harmonic restraints to return CVs back in bounds.
165  CalcBiasForce(snapshot, cvs, cvVals);
166 
167  // Update the forces in snapshot by adding in the force bias from each
168  // CV to each atom based on the gradient of the CV.
169  for (size_t j = 0; j < forces.size(); ++j)
170  forces[j] += biases_[j];
171  }
172 
173  // Post-simulation hook.
174  void ABF::PostSimulation(Snapshot*, const CVManager&)
175  {
176  WriteData();
177  }
178 
179  void ABF::CalcBiasForce(const Snapshot* snapshot, const CVList& cvs, const std::vector<double> &cvVals)
180  {
181  // Reset the bias.
182  biases_.resize(snapshot->GetNumAtoms(), Vector3{0,0,0});
183  for(auto& b : biases_)
184  b.setZero();
185 
186  // Compute bias if within bounds.
187  if(boundsCheck(cvVals))
188  {
189  for(int i = 0; i < dim_; ++i)
190  {
191  auto& grad = cvs[i]->GetGradient();
192  for(size_t j = 0; j < biases_.size(); ++j)
193  biases_[j] -= (Fworld_[i]->at(cvVals))*grad[j]/std::max(min_,Nworld_->at(cvVals));
194  }
195  }
196  // Otherwise, apply harmonic restraint if enabled.
197  else
198  {
199  for(int i = 0; i < dim_; ++i)
200  {
201  double cvVal = cvs[i]->GetValue();
202  auto k = 0.;
203  auto x0 = 0.;
204 
205  if(isperiodic_[i])
206  {
207  double periodsize = periodicboundaries_[i][1]-periodicboundaries_[i][0];
208  double cvRestrMidpoint = (restraint_[i][1]+restraint_[i][0])/2;
209  while((cvVal-cvRestrMidpoint) > periodsize/2)
210  cvVal -= periodsize;
211  while((cvVal-cvRestrMidpoint) < -periodsize/2)
212  cvVal += periodsize;
213  }
214 
215  if(cvVal < restraint_[i][0] && restraint_[i][2] > 0)
216  {
217  k = restraint_[i][2];
218  x0 = restraint_[i][0];
219  }
220  else if (cvVal > restraint_[i][1] && restraint_[i][2] > 0)
221  {
222  k = restraint_[i][2];
223  x0 = restraint_[i][1];
224  }
225 
226  auto& grad = cvs[i]->GetGradient();
227  for(size_t j = 0; j < biases_.size(); ++j)
228  biases_[j] -= (cvVal - x0)*k*grad[j];
229  }
230  }
231  }
232 
233  // Write out the average generalized force.
234  // Also write out Fworld and Nworld backups for restarts.
235  void ABF::WriteData()
236  {
237  // Only one processor should be performing I/O.
238  if(world_.rank() != 0)
239  return;
240 
241  // Backup Fworld and Nworld.
242  Nworld_->WriteToFile(Nworld_filename_);
243  for(size_t i = 0 ; i < dim_; ++i)
244  {
245  Fworld_[i]->WriteToFile(Fworld_filename_+std::to_string(i));
246  }
247 
248 
249  // Average the generalized force for each bin and print it out.
250  int gridPoints = 1;
251  for(size_t i = 0 ; i < dim_; ++i)
252  gridPoints = gridPoints * N_->GetNumPoints(i);
253 
254  worldout_.open(filename_,std::ios::out);
255  worldout_ << std::endl;
256  worldout_ << "Iteration: " << iteration_ << std::endl;
257  worldout_ << "Printing out the current Adaptive Biasing Vector Field." << std::endl;
258  worldout_ << "First (Nr of CVs) columns are the coordinates, the next (Nr of CVs) columns are components of the Adaptive Force vector at that point." << std::endl;
259  worldout_ << "The columns are " << gridPoints << " long, mapping out a surface of ";
260 
261  for(size_t i = 0 ; i < dim_-1; ++i)
262  worldout_ << (N_->GetNumPoints())[i] << " by " ;
263  worldout_ << (N_->GetNumPoints(dim_-1)) << " points in " << dim_ << " dimensions." << std::endl;
264  worldout_ << std::endl;
265 
266  std::vector<int> printCoords(dim_);
267  int div = 1;
268  int index = 0;
269  std::vector<double> tempcoord(dim_);
270  for(size_t i = 0; i < gridPoints; ++i)
271  {
272  printCoords[0] = i%(Nworld_->GetNumPoints(0));
273  div = 1;
274  for(size_t j = 1; j < dim_; ++j)
275  {
276  div = div * Nworld_->GetNumPoints(j);
277  printCoords[j]=i/div;
278  }
279  for(size_t j = 0; j < dim_; ++j)
280  {
281  worldout_ << std::setw(10) << (printCoords[j]+0.5)*((Nworld_->GetUpper(j)-Nworld_->GetLower(j))/Nworld_->GetNumPoints(j)) + Nworld_->GetLower(j) << " ";
282  tempcoord[j] = ((printCoords[j]+0.5)*((Nworld_->GetUpper(j)-Nworld_->GetLower(j))/Nworld_->GetNumPoints(j)) + Nworld_->GetLower(j));
283  }
284  for(size_t j = 0; j < dim_; ++j)
285  {
286  worldout_ << std::setw(10) << (Fworld_[j]->at(tempcoord))/std::max(Nworld_->at(tempcoord),min_)<< " ";
287  }
288  worldout_ << std::endl;
289 
290  }
291 
292  worldout_ << std::endl;
293  worldout_ << std::endl;
294  worldout_.close();
295  }
296 
297  // Checks whether walker is within CV bounds.
298  bool ABF::boundsCheck(const std::vector<double> &CVs)
299  {
300  for(size_t i = 0; i < dim_ ; ++i)
301  {
302  if((CVs[i] < N_->GetLower(i)) || (CVs[i] > N_->GetUpper(i)))
303  {
304  return false;
305  }
306  }
307  return true;
308  }
309 
310 
311  ABF* ABF::Build(const Value& json,
312  const MPI_Comm& world,
313  const MPI_Comm& comm,
314  const std::string& path)
315  {
316  ObjectRequirement validator;
317  Value schema;
318  Reader reader;
319 
320  reader.parse(JsonSchema::ABFMethod, schema);
321  validator.Parse(schema, path);
322 
323  // Validate inputs.
324  validator.Validate(json, path);
325  if(validator.HasErrors())
326  throw BuildException(validator.GetErrors());
327 
328  uint wid = mxx::comm(world).rank()/mxx::comm(comm).size();
329 
330  //bool multiwalkerinput = false;
331 
332  // Obtain lower bound for each CV.
333  std::vector<double> minsCV;
334  for(auto& mins : json["CV_lower_bounds"])
335  if(mins.isArray())
336  {
337  throw BuildException({"Separate inputs for multi-walker not fully implemented. Please use global entries for CV ranges"});
338  //multiwalkerinput = true;
339  //for(auto& bound : mins)
340  // minsCV.push_back(bound.asDouble());
341  }
342  else
343  minsCV.push_back(mins.asDouble());
344 
345  // Obtain upper bound for each CV.
346  std::vector<double> maxsCV;
347  for(auto& maxs : json["CV_upper_bounds"])
348  /*if(maxs.isArray())
349  {
350  for(auto& bound : maxs)
351  maxsCV.push_back(bound.asDouble());
352  }*/
353  //else
354  maxsCV.push_back(maxs.asDouble());
355 
356  // Obtain number of bins for each CV dimension.
357  std::vector<int> binsCV;
358  for(auto& bins : json["CV_bins"])
359  binsCV.push_back(bins.asInt());
360 
361  // Obtain lower bounds for restraints for each CV.
362  std::vector<double> minsrestCV;
363  for(auto& mins : json["CV_restraint_minimums"])
364  /*if(mins.isArray())
365  {
366  for(auto& bound : mins)
367  minsrestCV.push_back(bound.asDouble());
368  }*/
369  //else
370  minsrestCV.push_back(mins.asDouble());
371 
372  // Obtain upper bounds for restraints for each CV.
373  std::vector<double> maxsrestCV;
374  for(auto& maxs : json["CV_restraint_maximums"])
375  /*if(maxs.isArray())
376  {
377  for(auto& bound : maxs)
378  maxsrestCV.push_back(bound.asDouble());
379  }*/
380  //else
381  maxsrestCV.push_back(maxs.asDouble());
382 
383  // Obtain harmonic spring constant for restraints for each CV.
384  std::vector<double> springkrestCV;
385  for(auto& bins : json["CV_restraint_spring_constants"])
386  springkrestCV.push_back(bins.asDouble());
387 
388  // Obtain periodicity information for restraints for each CV for the purpose of correctly applying restraints through periodic boundaries.
389  std::vector<bool> isperiodic;
390  for(auto& isperCV : json["CV_isperiodic"])
391  isperiodic.push_back(isperCV.asBool());
392 
393  // Obtain lower periodic boundary for each CV.
394  std::vector<double> minperboundaryCV;
395  for(auto& minsperCV : json["CV_periodic_boundary_lower_bounds"])
396  minperboundaryCV.push_back(minsperCV.asDouble());
397 
398  // Obtain upper periodic boundary for each CV.
399  std::vector<double> maxperboundaryCV;
400  for(auto& maxsperCV : json["CV_periodic_boundary_upper_bounds"])
401  maxperboundaryCV.push_back(maxsperCV.asDouble());
402 
403  // Verify inputs are all correct lengths.
404  if(!(( minsCV.size() == maxsCV.size() &&
405  maxsCV.size() == minsrestCV.size() &&
406  minsrestCV.size() == maxsrestCV.size()) &&
407  (binsCV.size() == springkrestCV.size() &&
408  springkrestCV.size() == isperiodic.size())))
409  throw BuildException({"CV lower bounds, upper bounds, restrain minimums, restrains maximums must match in size. Bins, spring constants and periodicity info must match in size."});
410 
411  // Verify that all periodicity information is provided if CVs are periodic.
412  bool anyperiodic=false;
413  for(size_t i = 0; i<isperiodic.size(); ++i)
414  if(isperiodic[i])
415  {
416  anyperiodic = true;
417  if(!(isperiodic.size() == minperboundaryCV.size() &&
418  minperboundaryCV.size() == maxperboundaryCV.size()))
419  throw BuildException({"If any CV is defined as periodic, please define the full upper and lower bound vectors. They should both have the same number of entries as CV lower bounds, upper bounds... Entries corresponding to non-periodic CVs will not be used."});
420  }
421 
422  int dim = binsCV.size();
423 
424  // Read in JSON info.
425  auto FBackupInterv = json.get("output_frequency", 1000).asInt();
426  auto unitconv = json.get("unit_conversion", 0).asDouble();
427  auto timestep = json.get("timestep", 2).asDouble();
428  auto min = json.get("minimum_count", 200).asDouble();
429  auto massweigh = json.get("mass_weighing",false).asBool();
430 
431  std::vector<std::vector<double>> histdetails;
432  std::vector<std::vector<double>> restraint;
433  std::vector<std::vector<double>> periodicboundaries;
434  std::vector<double> temp1(3);
435  std::vector<double> temp2(3);
436  std::vector<double> temp3(2);
437 
438  auto freq = json.get("frequency", 1).asInt();
439  auto filename = json.get("output_file", "F_out").asString();
440  auto Nworld_filename = json.get("Nworld_output_file", "Nworld").asString();
441  auto Fworld_filename = json.get("Fworld_output_file", "Fworld_cv").asString();
442 
443  // Generate the grids based on JSON.
444  Grid<int> *N;
445  Grid<int> *Nworld;
446  std::vector<Grid<double>*> F(dim);
447  std::vector<Grid<double>*> Fworld(dim);
448 
449  // This feature is disabled for now.
450  /*if(multiwalkerinput)
451  {
452  for(size_t i=0; i<dim; ++i)
453  {
454  temp1 = {minsCV[i+wid*dim], maxsCV[i+wid*dim], binsCV[i]};
455  temp2 = {minsrestCV[i+wid*dim], maxsrestCV[i+wid*dim], springkrestCV[i]};
456  histdetails.push_back(temp1);
457  restraint.push_back(temp2);
458  if(anyperiodic)
459  {
460  temp3 = {minperboundaryCV[i], maxperboundaryCV[i]};
461  periodicboundaries.push_back(temp3);
462  }
463  }
464  for(size_t i=0; i<dim; ++i)
465  {
466  minsCVperwalker[i] = histdetails[i][0];
467  maxsCVperwalker[i] = histdetails[i][1];
468  }
469 
470  N= new Grid<int>(binsCV, minsCVperwalker, maxsCVperwalker, isperiodic);
471  Nworld= new Grid<int>(binsCV, minsCVperwalker, maxsCVperwalker, isperiodic);
472 
473  for(auto& grid : F)
474  {
475  grid= new Grid<double>(binsCV, minsCVperwalker, maxsCVperwalker, isperiodic);
476  }
477  for(auto& grid : Fworld)
478  {
479  grid= new Grid<double>(binsCV, minsCVperwalker, maxsCVperwalker, isperiodic);
480  }
481 
482  }
483  */
484  //else
485  //{
486 
487  // Appropriately size the grids.
488 
489  N= new Grid<int>(binsCV, minsCV, maxsCV, isperiodic);
490  Nworld= new Grid<int>(binsCV, minsCV, maxsCV, isperiodic);
491 
492  for(auto& grid : F)
493  {
494  grid= new Grid<double>(binsCV, minsCV, maxsCV, isperiodic);
495  }
496  for(auto& grid : Fworld)
497  {
498  grid= new Grid<double>(binsCV, minsCV, maxsCV, isperiodic);
499  }
500 
501  for(size_t i=0; i<dim; ++i)
502  {
503  temp1 = {minsCV[i], maxsCV[i], binsCV[i]};
504  temp2 = {minsrestCV[i], maxsrestCV[i], springkrestCV[i]};
505  histdetails.push_back(temp1);
506  restraint.push_back(temp2);
507  if(anyperiodic)
508  {
509  temp3 = {minperboundaryCV[i], maxperboundaryCV[i]};
510  periodicboundaries.push_back(temp3);
511  }
512  }
513  //}
514 
515  // Check if previously saved grids exist. If so, check that data match and load grids.
516  if(std::ifstream(Nworld_filename) && std::ifstream(Fworld_filename+std::to_string(0)) && wid == 0)
517  {
518  std::cout << "Attempting to load data from a previous run of ABF." << std::endl;
519  N->LoadFromFile(Nworld_filename);
520  for(size_t i=0; i<dim; ++i)
521  if(std::ifstream(Fworld_filename+std::to_string(i)))
522  F[i]->LoadFromFile(Fworld_filename+std::to_string(i));
523  else
524  throw BuildException({"Some, but not all Fworld outputs were found. Please check that these are appropriate inputs, or clean the working directory of other Fworld and Nworld inputs."});
525  }
526 
527 
528  auto* m = new ABF(world, comm, N, Nworld, F, Fworld, restraint, isperiodic, periodicboundaries, min, massweigh, filename, Nworld_filename, Fworld_filename, histdetails, FBackupInterv, unitconv, timestep, freq);
529 
530 
531  if(json.isMember("iteration"))
532  m->SetIteration(json.get("iteration",0).asInt());
533 
534  return m;
535 
536  }
537 
538 }
539 
540 
541 
542 
543 
544 
545 
546 
547 
548 
549 
550 
551 
552 
553 
554 
555 
556 
557 
558 
559 
560 
561 
562 
563 
564 
565 
566 
567 
568 
569 
570 
571 
572 
573 
bool HasErrors()
Check if errors have occured.
Definition: Requirement.h:86
Collective variable manager.
Definition: CVManager.h:40
unsigned GetNumAtoms() const
Get number of atoms in this snapshot.
Definition: Snapshot.h:200
std::vector< CollectiveVariable * > CVList
List of Collective Variables.
Definition: types.h:51
Class containing a snapshot of the current simulation in time.
Definition: Snapshot.h:43
virtual void Parse(Value json, const std::string &path) override
Parse JSON value to generate Requirement(s).
Exception to be thrown when building the Driver fails.
std::vector< std::string > GetErrors()
Get list of error messages.
Definition: Requirement.h:92
std::vector< CollectiveVariable * > GetCVs(const std::vector< uint > &mask=std::vector< uint >()) const
Get CV iterator.
Definition: CVManager.h:80
const std::vector< double > & GetMasses() const
Const access to the particle masses.
Definition: Snapshot.h:378
Adaptive Biasing Force Algorithm.
Definition: ABF.h:42
Requirements on an object.
Eigen::Vector3d Vector3
Three-dimensional vector.
Definition: types.h:33
const std::vector< Vector3 > & GetVelocities() const
Access the particle velocities.
Definition: Snapshot.h:349
void LoadFromFile(const std::string &filename)
Builds a grid from file.
Definition: Grid.h:626
const std::vector< Vector3 > & GetForces() const
Access the per-particle forces.
Definition: Snapshot.h:362
const std::vector< Vector3 > & GetPositions() const
Access the particle positions.
Definition: Snapshot.h:323
virtual void Validate(const Value &json, const std::string &path) override
Validate JSON value.