Simpatico  v1.10
ReplicaMove.cpp
1 #ifdef UTIL_MPI
2 /*
3 * Simpatico - Simulation Package for Polymeric and Molecular Liquids
4 *
5 * Copyright 2010 - 2017, The Regents of the University of Minnesota
6 * Distributed under the terms of the GNU General Public License.
7 */
8 
9 #include "ReplicaMove.h"
10 
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 <simp/boundary/OrthorhombicBoundary.h>
16 #include <util/mpi/MpiSendRecv.h>
17 #include <util/archives/MemoryOArchive.h>
18 #include <util/archives/MemoryIArchive.h>
19 #include <util/archives/MemoryCounter.h>
20 #include <util/random/Random.h>
21 #include <util/misc/Observer.h>
22 
23 #include <sstream>
24 #include <cmath>
25 
26 namespace McMd
27 {
28 
29  using namespace Util;
30  using namespace Simp;
31 
32  // Define static constant MPI message Tag.
33 
34  /*
35  * Constructor.
36  */
38  systemPtr_(&system),
39  communicatorPtr_(0),
40  myId_(-1),
41  nProcs_(0),
42  outputFile_(),
43  nParameters_(0),
44  interval_(-1),
45  nSampling_(-1),
46  ptPositionPtr_(0),
47  myPositionPtr_(0),
48  swapAttempt_(0),
49  swapAccept_(0)
50  {
51  // Precondition
52  if (!system.hasPerturbation()) {
53  UTIL_THROW("Parent System has no Perturbation");
54  }
55 
56  setClassName("ReplicaMove");
57  communicatorPtr_ = &(system.simulation().communicator());
58  myId_ = communicatorPtr_->Get_rank();
59  nProcs_ = communicatorPtr_->Get_size();
60 
61  // Generate output file name and open the file.
62  //std::stringstream sMyId;
63  //std::string fName("repx");
64  //sMyId << myId_;
65  //fName += sMyId.str();
66  system.fileMaster().openOutputFile("repx", outputFile_);
67 
68  // Generate output file name and open the file.
69  //std::stringstream energysMyId;
70  //std::string energyfName("diff_energy");
71  //energysMyId << myId_;
72  //energyfName += energysMyId.str();
73 
74  // Initialize statistical accumulators.
75  nParameters_ = system.perturbation().getNParameters();
76  }
77 
78  /*
79  * Destructor.
80  */
82  {
83  if (ptPositionPtr_) delete [] ptPositionPtr_;
84  if (myPositionPtr_) delete [] myPositionPtr_;
85  }
86 
87  /*
88  * Read attempting interval.
89  */
90  void ReplicaMove::readParameters(std::istream& in)
91  {
92  read<long>(in, "interval", interval_);
93  if (interval_ <= 0) {
94  UTIL_THROW("Invalid value input for interval_");
95  }
96  read<int>(in, "nSampling", nSampling_);
97  if (nSampling_ <= 0) {
98  UTIL_THROW("Invalid value input for nSampling_");
99  }
100 
101  // Allocate memory
102  int nAtom = system().simulation().atomCapacity();
103  ptPositionPtr_ = new Vector[nAtom];
104  myPositionPtr_ = new Vector[nAtom];
105  }
106 
107  /*
108  * Load internal state from an archive.
109  */
111  {
112  // Load parameters
113  loadParameter<long>(ar, "interval", interval_);
114  loadParameter<int>(ar, "nSampling", nSampling_);
115  ar & swapAttempt_;
116  ar & swapAccept_;
117 
118  // Validate
119  if (interval_ <= 0) {
120  UTIL_THROW("Invalid value input for interval_");
121  }
122  if (nSampling_ <= 0) {
123  UTIL_THROW("Invalid value input for nSampling_");
124  }
125 
126  // Allocate memory
127  int nAtom = system().simulation().atomCapacity();
128  ptPositionPtr_ = new Vector[nAtom];
129  myPositionPtr_ = new Vector[nAtom];
130  }
131 
132  /*
133  * Save internal state to an archive.
134  */
136  {
137  ar & interval_;
138  ar & nSampling_;
139  ar & swapAttempt_;
140  ar & swapAccept_;
141 
142  int nAtom = system().simulation().atomCapacity();
143  ptPositionPtr_ = new Vector[nAtom];
144  myPositionPtr_ = new Vector[nAtom];
145  }
146 
147  /*
148  * Perform replica exchange move.
149  */
151  {
152  MPI::Request request[4];
153  MPI::Status status;
154  System::MoleculeIterator molIter;
155  Molecule::AtomIterator atomIter;
156  int iA;
157  int recvPt, sendPt;
158 
159  DArray<int> permutation;
160  permutation.allocate(nProcs_);
161 
162  // Gather all derivatives of the perturbation Hamiltonians and parameters on processor with rank 0
163  DArray<double> myDerivatives;
164  myDerivatives.allocate(nParameters_);
165  DArray<double> myParameters;
166  myParameters.allocate(nParameters_);
167 
168  for (int i=0; i< nParameters_; i++) {
169  myDerivatives[i] = system().perturbation().derivative(i);
170  myParameters[i] = system().perturbation().parameter(i);
171  }
172 
173  int size = 0;
174  size += memorySize(myDerivatives);
175  size += memorySize(myParameters);
176 
177  if (myId_ != 0) {
178 
179  MemoryOArchive sendCurrent;
180  sendCurrent.allocate(size);
181 
182  sendCurrent << myDerivatives;
183  sendCurrent << myParameters;
184 
185  sendCurrent.send(*communicatorPtr_, 0);
186  } else {
187  DArray< DArray<double> > allDerivatives;
188  DArray< DArray<double> > allParameters;
189  allDerivatives.allocate(nProcs_);
190  allParameters.allocate(nProcs_);
191 
192  allDerivatives[0].allocate(nParameters_);
193  allDerivatives[0] = myDerivatives;
194  allParameters[0].allocate(nParameters_);
195  allParameters[0] = myParameters;
196 
197  for (int i = 1; i<nProcs_; i++) {
198  MemoryIArchive recvPartner;
199  recvPartner.allocate(size);
200  recvPartner.recv(*communicatorPtr_, i);
201  allDerivatives[i].allocate(nParameters_);
202  allParameters[i].allocate(nParameters_);
203  recvPartner >> allDerivatives[i];
204  recvPartner >> allParameters[i];
205  }
206 
207  // Now we have the complete matrix U_ij = u_i(x_j), permutate nsampling steps according
208  // to acceptance criterium
209 
210  // start with identity permutation
211  for (int i = 0; i < nProcs_; i++)
212  permutation[i] = i;
213 
214  for (int n =0; n < nSampling_; n++) {
215  swapAttempt_++;
216  // choose a pair i,j, i!= j at random
217  int i = system().simulation().random().uniformInt(0,nProcs_);
218  int j = system().simulation().random().uniformInt(0,nProcs_-1);
219  if (i<=j) j++;
220 
221  // apply acceptance criterium
222  double weight = 0;
223  for (int k = 0; k < nParameters_; k++) {
224  double deltaDerivative = allDerivatives[i][k] - allDerivatives[j][k];
225  // the permutations operate on the states (the perturbation parameters)
226  weight += (allParameters[permutation[j]][k] - allParameters[permutation[i]][k])*deltaDerivative;
227  }
228  double exponential = exp(-weight);
229  int accept = system().simulation().random(). metropolis(exponential) ? 1 : 0;
230 
231  if (accept) {
232  swapAccept_++;
233  // swap states of pair i,j
234  int tmp = permutation[i];
235  permutation[i] = permutation[j];
236  permutation[j] = tmp;
237  }
238  }
239 
240  // send exchange partner information to all other processors
241  for (int i = 0; i < nProcs_; i++) {
242  if (i != 0)
243  communicatorPtr_->Send(&permutation[i], 1, MPI::INT, i, 0);
244  else
245  sendPt = permutation[i];
246 
247  if (permutation[i] != 0)
248  communicatorPtr_->Send(&i, 1, MPI::INT, permutation[i], 1);
249  else
250  recvPt = i;
251  }
252  }
253 
254 
255  if (myId_ != 0) {
256  // partner id to receive from
257  communicatorPtr_->Recv(&sendPt, 1, MPI::INT, 0, 0);
258  // partner id to send to
259  communicatorPtr_->Recv(&recvPt, 1, MPI::INT, 0, 1);
260  }
261 
262  if (recvPt == myId_ || sendPt == myId_) {
263  // no exchange necessary
264  outputFile_ << sendPt << std::endl;
265  return true;
266  }
267 
268  assert(recvPt != myId_ && sendPt != myId_);
269 
270  Vector myBoundary;
271  myBoundary = system().boundary().lengths();
272 
273  Vector ptBoundary;
274 
275  // Accomodate new boundary dimensions.
276  request[0] = communicatorPtr_->Irecv(&ptBoundary, 1,
277  MpiTraits<Vector>::type, recvPt, 1);
278 
279  // Send old boundary dimensions.
280  request[1] = communicatorPtr_->Isend(&myBoundary, 1,
281  MpiTraits<Vector>::type, sendPt, 1);
282 
283  request[0].Wait();
284  request[1].Wait();
285 
286  system().boundary().setOrthorhombic(ptBoundary);
287 
288  // Pack atomic positions and types.
289  iA = 0;
290  for (int iSpec=0; iSpec < system().simulation().nSpecies(); ++iSpec){
291  for (system().begin(iSpec, molIter); molIter.notEnd(); ++molIter){
292  for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter) {
293  myPositionPtr_[iA] = atomIter->position();
294  iA++;
295  }
296  }
297  }
298 
299  // Accomodate new configuration.
300  request[2] = communicatorPtr_->Irecv(ptPositionPtr_, iA,
301  MpiTraits<Vector>::type, recvPt, 2);
302 
303  // Send old configuration.
304  request[3] = communicatorPtr_->Isend(myPositionPtr_, iA,
305  MpiTraits<Vector>::type, sendPt, 2);
306 
307  request[2].Wait();
308  request[3].Wait();
309 
310  // Adopt the new atomic positions.
311  iA = 0;
312  for (int iSpec=0; iSpec < system().simulation().nSpecies(); ++iSpec){
313  for (system().begin(iSpec, molIter); molIter.notEnd(); ++molIter){
314  for (molIter->begin(atomIter); atomIter.notEnd(); ++atomIter){
315  atomIter->position() = ptPositionPtr_[iA];
316  ++iA;
317  }
318  }
319  }
320 
321 
322  // Notify component observers.
323  sendRecvPair partners;
324  partners[0] = sendPt;
325  partners[1] = recvPt;
327 
328  // Log information about exchange partner to file
329  outputFile_ << sendPt << std::endl;
330 
331  return true;
332 
333  }
334 
335 }
336 #endif // ifdef UTIL_MPI
void send(MPI::Intracomm &comm, int dest)
Send packed data via MPI.
virtual void readParameters(std::istream &in)
Read parameters.
Definition: ReplicaMove.cpp:90
A Vector is a Cartesian vector.
Definition: Vector.h:75
void notifyObservers(const Event &event)
Notify the list of observers about an Event.
Definition: Notifier.h:98
int memorySize(T &data)
Function template to compute memory size of one object.
FileMaster & fileMaster() const
Get the associated FileMaster by reference.
Definition: System.h:1113
void begin(int speciesId, MoleculeIterator &iterator)
Initialize an iterator for molecules of one species in this System.
Definition: System.h:1147
virtual ~ReplicaMove()
Destructor.
Definition: ReplicaMove.cpp:81
bool notEnd() const
Is the current pointer not at the end of the array?
Definition: ArrayIterator.h:83
ReplicaMove(System &system)
Constructor.
Definition: ReplicaMove.cpp:37
void setOrthorhombic(const Vector &lengths)
Set unit cell dimensions for orthorhombic boundary.
const Vector & lengths() const
Get Vector of unit cell lengths by const reference.
int atomCapacity() const
Get the total number of Atoms allocated.
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 save(Serializable::OArchive &ar)
Save internal state to an archive.
A set of interacting Molecules enclosed by a Boundary.
Definition: System.h:115
bool hasPerturbation() const
Does this system have an associated Perturbation?
Definition: System.h:1179
Classes used by all simpatico molecular simulations.
virtual bool move()
Attempt, and accept or reject a replica exchange move.
virtual double derivative(int i) const =0
Get derivative of the Boltzmann weight W(X,p) with respect to the perturbation parameter p[i] of inde...
Saving / output archive for binary ostream.
double parameter(int i, int id)
Get parameter i of system id.
void allocate(size_t capacity)
Allocate memory block.
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition: global.h:51
Simulation & simulation() const
Get the parent Simulation by reference.
Definition: System.h:1055
Save archive for packed heterogeneous binary data.
bool notEnd() const
Is the current pointer not at the end of the PArray?
Utility classes for scientific computation.
Definition: accumulators.mod:1
virtual void loadParameters(Serializable::IArchive &ar)
Load internal state from an archive.
int getNParameters() const
Gets the number of parameters per system.
Default MpiTraits class.
Definition: MpiTraits.h:39
long uniformInt(long range1, long range2)
Return random long int x uniformly distributed in range1 <= x < range2.
Definition: Random.h:224
MPI::Intracomm & communicator()
Get the MPI communicator by reference.
Forward iterator for an Array or a C array.
Definition: ArrayIterator.h:39
Forward iterator for a PArray.
Definition: ArraySet.h:19
Boundary & boundary() const
Get the Boundary by reference.
Definition: System.h:1064
Saving archive for binary istream.
Perturbation & perturbation() const
Get the associated Perturbation by reference.
Definition: System.h:1185
Single-processor Monte Carlo (MC) and molecular dynamics (MD).
int nSpecies() const
Get the number of Species in this Simulation.
This file contains templates for global functions send<T>, recv<T> and bcast<T>.
void recv(MPI::Intracomm &comm, int source)
Receive packed data via MPI.
void setClassName(const char *className)
Set class name string.
virtual void allocate(size_t capacity)
Allocate memory.
void allocate(int capacity)
Allocate the underlying C array.
Definition: DArray.h:191
System & system()
Return the associated system by reference.
Definition: ReplicaMove.h:218
Input archive for packed heterogeneous binary data.
Random & random()
Get the random number generator by reference.