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