Simpatico  v1.10
BennettsMethod.cpp
1 #ifdef MCMD_PERTURB
2 #ifdef UTIL_MPI
3 /*
4 * Simpatico - Simulation Package for Polymeric and Molecular Liquids
5 *
6 * Copyright 2010, The Regents of the University of Minnesota
7 * Distributed under the terms of the GNU General Public License.
8 */
9 
10 #include "BennettsMethod.h" // class header
11 #include <mcMd/perturb/Perturbation.h>
12 #include <mcMd/perturb/LinearPerturbation.h>
13 #include <mcMd/simulation/Simulation.h>
14 #include <mcMd/simulation/System.h>
15 #include <util/mpi/MpiSendRecv.h>
16 #include <util/format/Dbl.h>
17 #ifdef UTIL_MPI
18 #include <util/containers/DArray.h> // member
19 #endif
20 
21 #include <cstdio>
22 #include <sstream>
23 
24 namespace McMd
25 {
26 
27  using namespace Util;
28 
29  const int BennettsMethod::TagDerivative[2] = {1, 2};
30  const int BennettsMethod::TagFermi[2] = {8, 9};
31 
32  /*
33  * Constructor.
34  */
36  : SystemAnalyzer<System>(system),
37  shift_(0.0),
38  lowerShift_(0.0),
39  shifts_(),
40  communicatorPtr_(0),
41  myId_(-1),
42  nProcs_(0),
43  lowerId_(-1),
44  upperId_(-1),
45  nParameter_(0),
46  myParam_(),
47  lowerParam_(),
48  upperParam_(),
49  nSamplePerBlock_(1),
50  myAccumulator_(),
51  upperAccumulator_(),
52  myArg_(0.0),
53  lowerArg_(0.0),
54  myFermi_(0.0),
55  lowerFermi_(0.0),
56  upperFermi_(0.0),
57  outputFile_(),
58  isInitialized_(false)
59  {
60  setClassName("BennettsMethod");
61  communicatorPtr_ = &(system.simulation().communicator());
62  myId_ = communicatorPtr_->Get_rank();
63  nProcs_ = communicatorPtr_->Get_size();
64  if (myId_ != 0) {
65  lowerId_ = myId_ - 1;
66  } else {
67  lowerId_ = myId_;
68  }
69  if (myId_ != nProcs_-1) {
70  upperId_ = myId_ + 1;
71  } else {
72  upperId_ = myId_;
73  }
74  nParameter_ = system.perturbation().getNParameters();
75  myParam_.allocate(nParameter_);
76  lowerParam_.allocate(nParameter_);
77  upperParam_.allocate(nParameter_);
78  }
79 
80  /*
81  * Read parameters and initialize.
82  */
83  void BennettsMethod::readParameters(std::istream& in)
84  {
85  readInterval(in);
87 
88  read<int>(in,"nSamplePerBlock", nSamplePerBlock_);
89 
90  #ifdef UTIL_MPI
91  if (hasIoCommunicator()) {
92  shifts_.allocate(nProcs_);
93  shifts_[nProcs_-1]=0.0;
94  readDArray<double>(in, "shifts", shifts_, nProcs_-1);
95  shift_ = shifts_[myId_];
96  } else {
97  read<double>(in, "shift", shift_);
98  }
99  #else
100  read<double>(in, "shift", shift_);
101  #endif
102 
103  myAccumulator_.setNSamplePerBlock(nSamplePerBlock_);
104  upperAccumulator_.setNSamplePerBlock(nSamplePerBlock_);
105 
106  //If nSamplePerBlock != 0, open an output file for block averages.
107  if (myAccumulator_.nSamplePerBlock()) {
108  fileMaster().openOutputFile(outputFileName(".dat"), outputFile_);
109  }
110 
111  isInitialized_ = true;
112  }
113 
114  /*
115  * Load internal state from archive.
116  */
118  {
120 
121  loadParameter<int>(ar,"nSamplePerBlock", nSamplePerBlock_);
122 
123  #ifdef UTIL_MPI
124  if (hasIoCommunicator()) {
125  shifts_.allocate(nProcs_);
126  shifts_[nProcs_-1] = 0.0;
127  loadDArray<double>(ar, "shifts", shifts_, nProcs_-1);
128  shift_ = shifts_[myId_];
129  } else {
130  loadParameter<double>(ar, "shift", shift_);
131  }
132  #else
133  loadParameter<double>(ar, "shift", shift_);
134  #endif
135 
136  ar & myAccumulator_;
137  ar & upperAccumulator_;
138 
139  // ar & myId_;
140  // ar & nProcs_;
141  // ar & lowerId_;
142  // ar & upperId_;
143  // ar & nParameter_;
144  // ar & myParam_;
145 
146  ar & lowerParam_;
147  ar & upperParam_;
148  ar & myArg_;
149  ar & lowerArg_;
150  ar & myFermi_;
151  ar & lowerFermi_;
152  ar & upperFermi_;
153 
154 
155  //If nSamplePerBlock != 0, open an output file for block averages.
156  if (myAccumulator_.nSamplePerBlock()) {
157  fileMaster().openOutputFile(outputFileName(".dat"), outputFile_);
158  }
159 
160  isInitialized_ = true;
161  }
162 
163  /*
164  * Save internal state to archive.
165  */
167  { ar & *this; }
168 
170  {
171  if (!isInitialized_) {
172  UTIL_THROW("Object is not initialized");
173  }
174 
175  communicatorPtr_->Bcast((void*)&shifts_[0], nProcs_, MPI::DOUBLE, 0);
176  if ( myId_ != 0 ) {
177  lowerShift_ = shifts_[lowerId_];
178  } else {}
179 
180  myAccumulator_.clear();
181  upperAccumulator_.clear();
182  }
183 
184  void BennettsMethod::sample(long iStep)
185  {
186  if (!isAtInterval(iStep)) return;
187 
188  int myPort, upperPort;
189  MPI::Request requestFermi[2];
190  MPI::Status status;
191 
192  // Exchange perturbation parameters and differences
193  if (myId_ != 0 && myId_ != nProcs_ - 1) {
194 
195  myPort = myId_%2;
196  upperPort = upperId_%2;
197 
198  for (int i = 0; i < nParameter_; ++i) {
199  myParam_[i] = system().perturbation().parameter(i);
200  lowerParam_[i] = system().perturbation().parameter(i,lowerId_);
201  upperParam_[i] = system().perturbation().parameter(i,upperId_);
202  }
203 
204  myArg_ = system().perturbation().difference(upperParam_);
205  lowerArg_ = system().perturbation().difference(lowerParam_);
206 
207  myArg_ -= shift_;
208  lowerArg_ += lowerShift_;
209 
210  myFermi_ = 1/(1 + exp(myArg_));
211  lowerFermi_ = 1/(1 + exp(lowerArg_));
212 
213  requestFermi[0] = communicatorPtr_->Irecv(&upperFermi_, 1, MPI::DOUBLE, upperId_,
214  TagFermi[upperPort]);
215 
216  requestFermi[1] = communicatorPtr_->Isend(&lowerFermi_, 1, MPI::DOUBLE, lowerId_,
217  TagFermi[myPort]);
218 
219  // Synchronizing
220  requestFermi[0].Wait();
221  requestFermi[1].Wait();
222 
223  upperAccumulator_.sample(upperFermi_);
224  myAccumulator_.sample(myFermi_);
225 
226  outputFile_ << Dbl(myFermi_) << " " << Dbl(upperFermi_) << " ";
227  outputFile_ << std::endl;
228 
229  } else if (myId_ == 0) {
230 
231  myPort = myId_%2;
232  upperPort = upperId_%2;
233 
234  for (int i = 0; i < nParameter_; ++i) {
235  myParam_[i] = system().perturbation().parameter(i);
236  upperParam_[i] = system().perturbation().parameter(i, upperId_);
237  }
238 
239  myArg_ = system().perturbation().difference(upperParam_);
240  myArg_ -= shift_;
241 
242  myFermi_ = 1/(1 + exp(myArg_));
243 
244  requestFermi[0] = communicatorPtr_->Irecv(&upperFermi_, 1, MPI::DOUBLE, upperId_,
245  TagFermi[upperPort]);
246 
247  // Synchronizing
248  requestFermi[0].Wait();
249 
250  upperAccumulator_.sample(upperFermi_);
251  myAccumulator_.sample(myFermi_);
252 
253  outputFile_ << Dbl(myFermi_) << " " << Dbl(upperFermi_) << " ";
254  outputFile_ << std::endl;
255 
256  } else if (myId_ == nProcs_ - 1) {
257 
258  myPort = myId_%2;
259 
260  for (int i = 0; i < nParameter_; ++i) {
261  myParam_[i] = system().perturbation().parameter(i);
262  lowerParam_[i] = system().perturbation().parameter(i, lowerId_);
263  }
264 
265  lowerArg_ = system().perturbation().difference(lowerParam_);
266  lowerArg_ += lowerShift_;
267 
268  lowerFermi_ = 1/(1 + exp(lowerArg_));
269 
270  requestFermi[1] = communicatorPtr_->Isend(&lowerFermi_, 1, MPI::DOUBLE, lowerId_,
271  TagFermi[myPort]);
272 
273  // Synchronizing
274  requestFermi[1].Wait();
275  }
276  }
277 
278  void BennettsMethod::analyze()
279  {
280  double EnergyDiff, ratio, improvedShift;
281  if (myId_ != nProcs_ - 1) {
282 
283  ratio = upperAccumulator_.average()/myAccumulator_.average();
284 
285  EnergyDiff = log(ratio)+shift_;
286 
287  improvedShift = EnergyDiff;
288 
289  shifts_[myId_] = improvedShift;
290  shift_ = improvedShift;
291  } else {}
292 
293  }
294 
295 
296  // Output results to file after simulation is completed.
298  {
299  if (myAccumulator_.nSamplePerBlock()) {
300  outputFile_.close();
301  }
302 
303  fileMaster().openOutputFile(outputFileName(".ave"), outputFile_);
304  if (myId_ != nProcs_ - 1) {
305  myAccumulator_.output(outputFile_);
306  outputFile_ << std::endl;
307  outputFile_ << std::endl;
308  upperAccumulator_.output(outputFile_);
309  outputFile_ << std::endl;
310  outputFile_ << std::endl;
311  }
312  outputFile_.close();
313 
314  analyze();
315 
316  communicatorPtr_->Gather((const void *) &shift_, 1, MPI::DOUBLE, (void *) &shifts_[0], 1, MPI::DOUBLE, 0);
317  int i;
318  if (myId_ == 0) {
319  fileMaster().openOutputFile(outputFileName("_all.dat"), outputFile_);
320  for (i = 0; i < nProcs_-1; ++i) {
321  outputFile_ << Dbl(shifts_[i]);
322  outputFile_ << std::endl;
323  }
324  outputFile_.close();
325 
326  } else {}
327  }
328 }
329 #endif // ifdef UTIL_MPI
330 #endif // ifdef MCMD_PERTURB
void clear()
Clear all accumulators, set to empty initial state.
Definition: Average.cpp:42
double average() const
Return the average of all sampled values.
void openOutputFile(const std::string &filename, std::ofstream &out, std::ios_base::openmode mode=std::ios_base::out) const
Open an output file.
Definition: FileMaster.cpp:290
virtual void loadParameters(Serializable::IArchive &ar)
Load parameters from archive.
double shift_
Value of shift constant for associated system.
A set of interacting Molecules enclosed by a Boundary.
Definition: System.h:115
System & system()
Return reference to parent system.
Wrapper for a double precision number, for formatted ostream output.
Definition: Dbl.h:39
void readOutputFileName(std::istream &in)
Read outputFileName from file.
virtual void save(Serializable::OArchive &ar)
Save internal state to an archive.
Saving / output archive for binary ostream.
double parameter(int i, int id)
Get parameter i of system id.
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition: global.h:51
double lowerShift_
Value of shift constant for lower replica system.
Simulation & simulation() const
Get the parent Simulation by reference.
Definition: System.h:1055
virtual void sample(long iStep)
Calculate, analyze and/or output a physical quantity.
void readInterval(std::istream &in)
Read interval from file, with error checking.
Utility classes for scientific computation.
Definition: accumulators.mod:1
int getNParameters() const
Gets the number of parameters per system.
virtual void loadParameters(Serializable::IArchive &ar)
Load internal state from an archive.
MPI::Intracomm & communicator()
Get the MPI communicator by reference.
virtual double difference(DArray< double > iPartnerParameter) const =0
Returns the difference W(X, p&#39;) - W(X, p).
virtual void setup()
Clear accumulators.
Template for Analyzer associated with one System.
void output(std::ostream &out) const
Output final statistical properties to file.
Definition: Average.cpp:178
void setNSamplePerBlock(int nSamplePerBlock)
Set nSamplePerBlock.
Definition: Average.cpp:63
bool hasIoCommunicator() const
Does this object have an associated MPI communicator?
Definition: MpiFileIo.h:99
Saving archive for binary istream.
Perturbation & perturbation() const
Get the associated Perturbation by reference.
Definition: System.h:1185
void sample(double value)
Add a sampled value to the ensemble.
Definition: Average.cpp:94
virtual void output()
Output results at end of simulation.
Single-processor Monte Carlo (MC) and molecular dynamics (MD).
int nSamplePerBlock() const
Get number of samples per block average.
Definition: Average.h:220
This file contains templates for global functions send<T>, recv<T> and bcast<T>.
void setClassName(const char *className)
Set class name string.
FileMaster & fileMaster()
Get the FileMaster by reference.
bool isAtInterval(long counter) const
Return true iff counter is a multiple of the interval.
const std::string & outputFileName() const
Return outputFileName string.
void allocate(int capacity)
Allocate the underlying C array.
Definition: DArray.h:191
BennettsMethod(System &system)
Constructor.
virtual void readParameters(std::istream &in)
Read parameters from file.