QMCPACK
WalkerControlBase.cpp
Go to the documentation of this file.
1 //////////////////////////////////////////////////////////////////////////////////////
2 // This file is distributed under the University of Illinois/NCSA Open Source License.
3 // See LICENSE file in top directory for details.
4 //
5 // Copyright (c) 2020 QMCPACK developers.
6 //
7 // File developed by: Peter Doak, doakpw@ornl.gov, Oak Ridge National Laboratory
8 // Ken Esler, kpesler@gmail.com, University of Illinois at Urbana-Champaign
9 // Jeremy McMinnis, jmcminis@gmail.com, University of Illinois at Urbana-Champaign
10 // Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
11 // Ye Luo, yeluo@anl.gov, Argonne National Laboratory
12 // Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
13 //
14 // File created by: Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
15 //////////////////////////////////////////////////////////////////////////////////////
16 
17 #include <cassert>
18 #include <stdexcept>
19 #include <numeric>
20 #include <sstream>
21 
22 #include "WalkerControlBase.h"
24 #include "Particle/HDFWalkerIO.h"
25 #include "OhmmsData/ParameterSet.h"
28 
29 namespace qmcplusplus
30 {
32 
34  : MPIObjectBase(c), n_min_(1), n_max_(10), MaxCopy(2), target_sigma_(10), NumWalkersCreated(0), SwapMode(0)
35 {
36  method_ = -1; //assign invalid method
38  MyContext = myComm->rank();
39  curData.resize(LE_MAX + num_contexts_);
40  NumPerRank.resize(num_contexts_);
41  OffSet.resize(num_contexts_ + 1);
42  FairOffSet.resize(num_contexts_ + 1);
43  accumData.resize(LE_MAX);
44 }
45 
47 
48 //disable it: everything is done by a constructor
49 //void WalkerControlBase::setCommunicator(Communicate* c)
50 //{
51 // NumContexts=myComm->size();
52 // MyContext=myComm->rank();
53 // curData.resize(LE_MAX+NumContexts);
54 // NumPerRank.resize(NumContexts);
55 // OffSet.resize(NumContexts+1);
56 // FairOffSet.resize(NumContexts+1);
57 // accumData.resize(LE_MAX);
58 //}
59 
61 {
62  if (MyContext == 0)
63  {
64  std::filesystem::path hname(myComm->getName());
65  hname.concat(".dmc.dat");
66  if (hname != dmcFname)
67  {
68  dmcStream = std::make_unique<std::ofstream>(hname.c_str());
69  dmcStream->setf(std::ios::scientific, std::ios::floatfield);
70  dmcStream->precision(10);
71  (*dmcStream) << "# Index " << std::setw(20) << "LocalEnergy" << std::setw(20) << "Variance" << std::setw(20)
72  << "Weight" << std::setw(20) << "NumOfWalkers" << std::setw(20)
73  << "AvgSentWalkers"; //add the number of walkers
74  (*dmcStream) << std::setw(20) << "TrialEnergy" << std::setw(20) << "DiffEff";
75  // if (WriteRN)
76  (*dmcStream) << std::setw(20) << "LivingFraction";
77  (*dmcStream) << std::endl;
78  dmcFname = std::move(hname);
79  }
80  }
81 }
82 
84 {
85  start(); //do the normal start
86  for (const auto& walker : walkers)
87  {
88  if (walker->getWalkerID() == 0)
89  {
90  walker->setWalkerID((++NumWalkersCreated) * num_contexts_ + MyContext);
91  walker->setParentID(walker->getWalkerID());
92  }
93  }
94 }
95 
96 /** Depends on alot of state
97  *
98  * Does not depend on state refactored to PopulationAdjustment directly
99  */
101 {
102  //taking average over the walkers
103  FullPrecRealType wgtInv(1.0 / curData[WEIGHT_INDEX]);
104  FullPrecRealType eavg = curData[ENERGY_INDEX] * wgtInv;
105  ensemble_property_.Energy = eavg;
107  ensemble_property_.Variance = (curData[ENERGY_SQ_INDEX] * wgtInv - eavg * eavg);
112  static_cast<FullPrecRealType>(curData[FNSIZE_INDEX] + curData[RNONESIZE_INDEX]);
115  // \\todo If WalkerControlBase is not exclusively for dmc then this shouldn't be here.
116  // If it is it shouldn't be in QMDrivers but QMCDrivers/DMC
117  if (dmcStream)
118  {
119  (*dmcStream) << std::setw(10) << iter << std::setw(20) << ensemble_property_.Energy << std::setw(20)
120  << ensemble_property_.Variance << std::setw(20) << ensemble_property_.Weight << std::setw(20)
121  << ensemble_property_.NumSamples << std::setw(20)
122  << curData[SENTWALKERS_INDEX] / static_cast<double>(num_contexts_);
123  (*dmcStream) << std::setw(20) << trialEnergy << std::setw(20)
125  // if (WriteRN) (*dmcStream)
126  (*dmcStream) << std::setw(20) << ensemble_property_.LivingFraction;
127  // Work around for bug with deterministic scalar trace test on select compiler/architectures.
128  // While WalkerControlBase appears to have exclusive ownership of the dmcStream pointer,
129  // this is not actually true. Apparently it doesn't actually and can loose ownership then it is
130  // either leaked or not flushed before it is destroyed.
131  // \todo fix this, you don't want to flush every step since you really hope that could be very rapid.
132  (*dmcStream)
133  << std::endl; //'\n'; // this is definitely not a place to put an endl as that is also a signal for a flush.
134  }
135 }
136 
138 {
139  std::fill(accumData.begin(), accumData.end(), 0.0);
140  std::fill(curData.begin(), curData.end(), 0.0);
141 }
142 
144 {
145  FullPrecRealType esum = 0.0, e2sum = 0.0, wsum = 0.0, ecum = 0.0, besum = 0.0, bwgtsum = 0.0;
146  FullPrecRealType r2_accepted = 0.0, r2_proposed = 0.0;
147  int nrn(0), ncr(0), nfn(0), nc(0);
148  for (auto& walker : W)
149  {
150  nc = std::min(static_cast<int>(walker->Multiplicity), MaxCopy);
151  if (nc > 0)
152  nfn++;
153  else
154  ncr++;
155  r2_accepted += walker->Properties(WP::R2ACCEPTED);
156  r2_proposed += walker->Properties(WP::R2PROPOSED);
157  FullPrecRealType e(walker->Properties(WP::LOCALENERGY));
158  // This is a trick to estimate the number of walkers
159  // after the first iterration branching.
160  //RealType wgt=((*it)->Weight);
162  esum += wgt * e;
163  e2sum += wgt * e * e;
164  wsum += wgt;
165  ecum += e;
166  }
167  //temp is an array to perform reduction operations
168  std::fill(curData.begin(), curData.end(), 0.0);
169  curData[ENERGY_INDEX] = esum;
170  curData[ENERGY_SQ_INDEX] = e2sum;
171  curData[WALKERSIZE_INDEX] = W.getActiveWalkers();
172  curData[WEIGHT_INDEX] = wsum;
173  curData[EREF_INDEX] = ecum;
174  curData[R2ACCEPTED_INDEX] = r2_accepted;
175  curData[R2PROPOSED_INDEX] = r2_proposed;
176  curData[FNSIZE_INDEX] = nfn;
177  curData[RNONESIZE_INDEX] = ncr;
178  curData[RNSIZE_INDEX] = nrn;
179  curData[B_ENERGY_INDEX] = besum;
180  curData[B_WGT_INDEX] = bwgtsum;
181 
183  measureProperties(iter);
185  W.EnsembleProperty = ensemble_property_;
186  //return W.getActiveWalkers();
187  return int(curData[WEIGHT_INDEX]);
188 }
189 
191 {
192  NumPerRank[0] = sortWalkers(W);
193  measureProperties(iter);
195  //un-biased variance but we use the saimple one
196  //W.EnsembleProperty.Variance=(e2sum*wsum-esum*esum)/(wsum*wsum-w2sum);
197  ////add to the accumData for block average: REMOVE THIS
198  //accumData[ENERGY_INDEX] += curData[ENERGY_INDEX]*wgtInv;
199  //accumData[ENERGY_SQ_INDEX] += curData[ENERGY_SQ_INDEX]*wgtInv;
200  //accumData[WALKERSIZE_INDEX] += curData[WALKERSIZE_INDEX];
201  //accumData[WEIGHT_INDEX] += curData[WEIGHT_INDEX];
202  int current_population = std::accumulate(NumPerRank.begin(), NumPerRank.end(), 0);
203  applyNmaxNmin(current_population);
204  int nw_tot = copyWalkers(W);
205  //set Weight and Multiplicity to default values
206  for (auto& walker : W)
207  {
208  walker->Weight = 1.0;
209  walker->Multiplicity = 1.0;
210  }
211 
212  // Update offsets in non-MPI case, needed to ensure checkpoint outputs the correct
213  W.setWalkerOffsets({0, nw_tot});
214  return nw_tot;
215 }
216 
217 /** evaluate curData and mark the bad/good walkers.
218  *
219  * Each good walker has a counter registering the
220  * number of copies needed to be generated from this walker.
221  * Bad walkers will be either recycled or removed later.
222  */
224 {
225  std::vector<std::unique_ptr<Walker_t>> good_rn;
226  std::vector<int> ncopy_rn;
227  NumWalkers = 0;
228  FullPrecRealType esum = 0.0, e2sum = 0.0, wsum = 0.0, ecum = 0.0, besum = 0.0, bwgtsum = 0.0;
229  FullPrecRealType r2_accepted = 0.0, r2_proposed = 0.0;
230  int nrn(0), ncr(0);
231  for (auto& walker : W)
232  {
233  const int nc = std::min(static_cast<int>(walker->Multiplicity), MaxCopy);
234  if (nc == 0)
235  ncr++;
236  r2_accepted += walker->Properties(WP::R2ACCEPTED);
237  r2_proposed += walker->Properties(WP::R2PROPOSED);
238  FullPrecRealType e(walker->Properties(WP::LOCALENERGY));
239  FullPrecRealType wgt = (walker->Weight);
240  esum += wgt * e;
241  e2sum += wgt * e * e;
242  wsum += wgt;
243  ecum += e;
244  if (nc)
245  {
246  NumWalkers += nc;
247  good_w.push_back(std::move(walker));
248  ncopy_w.push_back(nc - 1);
249  }
250  else
251  bad_w.push_back(std::move(walker));
252  }
253  //temp is an array to perform reduction operations
254  std::fill(curData.begin(), curData.end(), 0.0);
255  //update curData
256  curData[ENERGY_INDEX] = esum;
257  curData[ENERGY_SQ_INDEX] = e2sum;
258  curData[WALKERSIZE_INDEX] = W.getActiveWalkers();
259  curData[WEIGHT_INDEX] = wsum;
260  curData[EREF_INDEX] = ecum;
261  curData[R2ACCEPTED_INDEX] = r2_accepted;
262  curData[R2PROPOSED_INDEX] = r2_proposed;
263  // This is really an integral type but is implicitly converted
264  curData[FNSIZE_INDEX] = good_w.size();
265  // This is really an integral type but is implicitly converted
266  curData[RNONESIZE_INDEX] = ncr;
267  // This is really an integral type but is implicitly converted
268  curData[RNSIZE_INDEX] = nrn;
269  curData[B_ENERGY_INDEX] = besum;
270  curData[B_WGT_INDEX] = bwgtsum;
271  return NumWalkers;
272 }
273 
274 /** legacy population limiting
275  */
276 int WalkerControlBase::applyNmaxNmin(int current_population)
277 {
278  // limit Nmax
279  int current_max = (current_population + num_contexts_ - 1) / num_contexts_;
280  if (current_max > n_max_)
281  {
282  app_warning() << "Exceeding Max Walkers per MPI rank : " << n_max_ << ". Ceiling is applied" << std::endl;
283  int nsub = current_population - n_max_ * num_contexts_;
284  for (int irank = 0; irank < num_contexts_; irank++)
285  if (NumPerRank[irank] > n_max_)
286  {
287  int n_remove = std::min(nsub, NumPerRank[irank] - n_max_);
288  NumPerRank[irank] -= n_remove;
289  nsub -= n_remove;
290 
291  if (irank == MyContext)
292  {
293  for (int iw = 0; iw < ncopy_w.size(); iw++)
294  {
295  int n_remove_walker = std::min(ncopy_w[iw], n_remove);
296  ncopy_w[iw] -= n_remove_walker;
297  n_remove -= n_remove_walker;
298  if (n_remove == 0)
299  break;
300  }
301 
302  if (n_remove > 0)
303  {
304  app_warning() << "Removing copies of good walkers is not enough. "
305  << "Removing good walkers." << std::endl;
306  do
307  {
308  bad_w.push_back(std::move(good_w.back()));
309  good_w.pop_back();
310  ncopy_w.pop_back();
311  --n_remove;
312  } while (n_remove > 0 && !good_w.empty());
313  }
314 
315  if (n_remove)
316  APP_ABORT("WalkerControlBase::applyNmaxNmin not able to remove sufficient walkers on a node!");
317  if (std::accumulate(ncopy_w.begin(), ncopy_w.end(), ncopy_w.size()) != NumPerRank[irank])
318  APP_ABORT("WalkerControlBase::applyNmaxNmin walker removal mismatch!");
319  }
320 
321  if (nsub == 0)
322  break;
323  }
324 
325  if (nsub)
326  APP_ABORT("WalkerControlBase::applyNmaxNmin not able to remove sufficient walkers overall!");
327  }
328 
329  // limit Nmin
330  if (current_population / num_contexts_ < n_min_)
331  {
332  app_warning() << "The number of walkers is running lower than Min Walkers per MPI rank : " << n_min_
333  << ". Floor is applied" << std::endl;
334  int nadd = n_min_ * num_contexts_ - current_population;
335  for (int irank = 0; irank < num_contexts_; irank++)
336  if (NumPerRank[irank] > 0 && NumPerRank[irank] < n_min_)
337  {
338  int n_insert = std::min(nadd, n_min_ - NumPerRank[irank]);
339  NumPerRank[irank] += n_insert;
340  nadd -= n_insert;
341 
342  if (irank == MyContext)
343  {
344  int n_avg_insert_per_walker = (n_insert + ncopy_w.size() - 1) / ncopy_w.size();
345  for (int iw = 0; iw < ncopy_w.size(); iw++)
346  {
347  int n_insert_walker = std::min(n_avg_insert_per_walker, n_insert);
348  ncopy_w[iw] += n_insert_walker;
349  n_insert -= n_insert_walker;
350  if (n_insert == 0)
351  break;
352  }
353 
354  if (std::accumulate(ncopy_w.begin(), ncopy_w.end(), ncopy_w.size()) != NumPerRank[irank])
355  APP_ABORT("WalkerControlBase::applyNmaxNmin walker insertion mismatch!");
356  }
357 
358  if (nadd == 0)
359  break;
360  }
361 
362  if (nadd)
363  app_warning() << "WalkerControlBase::applyNmaxNmin not able to add sufficient walkers overall!" << std::endl;
364  }
365 
366  // check current population
367  current_population = std::accumulate(NumPerRank.begin(), NumPerRank.end(), 0);
368  // at least one walker after load-balancing
369  if (current_population / num_contexts_ == 0)
370  {
371  app_error() << "Some MPI ranks have no walkers after load balancing. This should not happen."
372  << "Improve the trial wavefunction or adjust the simulation parameters." << std::endl;
373  APP_ABORT("WalkerControlBase::sortWalkers");
374  }
375 
376  return current_population;
377 }
378 
379 /** copy good walkers to W
380  *
381  * Good walkers are copied based on the registered number of copies
382  * Bad walkers are recycled to avoid memory allocation and deallocation.
383  */
385 {
386  // save current good walker size.
387  const int size_good_w = good_w.size();
388  std::vector<int> copy_list;
389  for (int i = 0; i < size_good_w; i++)
390  {
391  for (int j = 0; j < ncopy_w[i]; j++)
392  {
393  if (bad_w.empty())
394  {
395  good_w.push_back(nullptr);
396  }
397  else
398  {
399  good_w.push_back(std::move(bad_w.back()));
400  bad_w.pop_back();
401  }
402  copy_list.push_back(i);
403  }
404  }
405 
406 #pragma omp parallel for
407  for (int i = size_good_w; i < good_w.size(); i++)
408  {
409  auto& wRef = good_w[copy_list[i - size_good_w]];
410  auto& awalker = good_w[i];
411  if (awalker == nullptr)
412  awalker = std::make_unique<Walker_t>(*wRef);
413  else
414  *awalker = *wRef;
415  // not fully sure this is correct or even used
416  awalker->setWalkerID((i - size_good_w) * num_contexts_ + MyContext);
417  awalker->setParentID(wRef->getParentID());
418  }
419 
420  //clear the WalkerList to populate them with the good walkers
421  W.clear();
422  W.insert(W.begin(), std::make_move_iterator(good_w.begin()), std::make_move_iterator(good_w.end()));
423 
424  //clear good_w and ncopy_w for the next branch
425  good_w.clear();
426  bad_w.clear();
427  ncopy_w.clear();
428  return W.getActiveWalkers();
429 }
430 
431 bool WalkerControlBase::put(xmlNodePtr cur)
432 {
433  int nw_target = 0, nw_max = 0;
434  std::string nonblocking = "yes";
435  ParameterSet params;
436  params.add(target_sigma_, "sigmaBound");
437  params.add(MaxCopy, "maxCopy");
438  params.add(nw_target, "targetwalkers");
439  params.add(nw_max, "max_walkers");
440  params.add(nonblocking, "use_nonblocking");
441 
442  bool success = params.put(cur);
443 
444  // validating input
445  if (nonblocking == "yes")
446  {
447  use_nonblocking = true;
448  }
449  else if (nonblocking == "no")
450  {
451  use_nonblocking = false;
452  }
453  else
454  {
455  APP_ABORT("WalkerControlBase::put unknown use_nonblocking option " + nonblocking);
456  }
457 
458  setMinMax(nw_target, nw_max);
459 
460  app_log() << " WalkerControlBase parameters " << std::endl;
461  //app_log() << " energyBound = " << targetEnergyBound << std::endl;
462  //app_log() << " sigmaBound = " << targetSigma << std::endl;
463  app_log() << " maxCopy = " << MaxCopy << std::endl;
464  app_log() << " Max Walkers per MPI rank " << n_max_ << std::endl;
465  app_log() << " Min Walkers per MPI rank " << n_min_ << std::endl;
466  app_log() << " Using " << (use_nonblocking ? "non-" : "") << "blocking send/recv" << std::endl;
467  return true;
468 }
469 
470 void WalkerControlBase::setMinMax(int nw_in, int nmax_in)
471 {
472  if (nw_in > 0)
473  {
474  int npernode = nw_in / num_contexts_;
475  if (method_)
476  {
477  n_max_ = npernode;
478  n_min_ = npernode;
479  }
480  else
481  {
482  n_max_ = MaxCopy * npernode + 1;
483  n_min_ = npernode / 5 + 1;
484  if (nmax_in > 0)
485  n_max_ = nmax_in;
486  }
487  }
488 }
489 
490 } // namespace qmcplusplus
WalkerControlBase(Communicate *c)
default constructor
virtual void reset()
reset to accumulate data
std::vector< std::unique_ptr< Walker_t > > bad_w
FullPrecRealType target_sigma_
target sigma to limit fluctuations of the trial energy
IndexType NumWalkers
current number of walkers per processor
int copyWalkers(MCWalkerConfiguration &W)
legacy: copy good walkers to W
A set of walkers that are to be advanced by Metropolis Monte Carlo.
std::ostream & app_warning()
Definition: OutputManager.h:69
std::vector< int > NumPerRank
number of particle per rank
Base class for any object which needs to know about a MPI communicator.
Definition: MPIObjectBase.h:26
helper functions for EinsplineSetBuilder
Definition: Configuration.h:43
int rank() const
return the rank
Definition: Communicate.h:116
std::filesystem::path dmcFname
filename for dmc.dat
size_t getActiveWalkers() const
return the number of active walkers
std::ostream & app_log()
Definition: OutputManager.h:65
std::ostream & app_error()
Definition: OutputManager.h:67
std::vector< int > ncopy_w
temporary storage for copy counters
FullPrecRealType trialEnergy
trial energy energy
const char walkers[]
Definition: HDFVersion.h:36
std::vector< FullPrecRealType > accumData
any accumulated data over a block
int size() const
return the number of tasks
Definition: Communicate.h:118
T min(T a, T b)
bool put(std::istream &is) override
read from std::istream
Definition: ParameterSet.h:42
bool use_nonblocking
Use non-blocking isend/irecv.
Wrapping information on parallelism.
Definition: Communicate.h:68
std::vector< int > FairOffSet
offset of the particle index for a fair distribution
void allreduce(T &)
int doNotBranch(int iter, MCWalkerConfiguration &W)
legacy: return global population update properties without branching
IndexType method_
id for the method
const std::string & getName() const
Definition: Communicate.h:131
class to handle a set of parameters
Definition: ParameterSet.h:27
WalkerProperties::Indexes WP
Definition: ParticleSet.cpp:34
std::vector< int > OffSet
offset of the particle index
Communicate * myComm
pointer to Communicate
Definition: MPIObjectBase.h:62
IndexType n_min_
minimum number of walkers
void measureProperties(int iter)
take averages and writes to a file
std::vector< FullPrecRealType > curData
any temporary data includes many ridiculous conversions of integral types to and from fp ...
void setWalkerID(MCWalkerConfiguration &walkers)
start controller and initialize the IDs of walkers
IndexType NumWalkersCreated
Number of walkers created by this rank.
#define APP_ABORT(msg)
Widely used but deprecated fatal error macros from legacy code.
Definition: AppAbort.h:27
std::unique_ptr< std::ofstream > dmcStream
file to save energy histogram
IndexType num_contexts_
number of contexts
Declaration of a TrialWaveFunction.
std::vector< std::unique_ptr< Walker_t > > good_w
temporary storage for good and bad walkers
void add(PDT &aparam, const std::string &aname_in, std::vector< PDT > candidate_values={}, TagStatus status=TagStatus::OPTIONAL)
add a new parameter corresponding to an xmlNode <parameter>
void insert(iterator it, INPUT_ITER first, INPUT_ITER last)
insert elements
virtual int branch(int iter, MCWalkerConfiguration &W, FullPrecRealType trigger)
legacy: perform branch and swap walkers as required
void setMinMax(int nw_in, int nmax_in)
IndexType n_max_
maximum number of walkers
Indexes
an enum denoting index of physical properties
IndexType MaxCopy
maximum copy per walker
QMCTraits::FullPrecRealType FullPrecRealType
typedef of FullPrecRealType
virtual ~WalkerControlBase()
empty destructor to clean up the derived classes
int sortWalkers(MCWalkerConfiguration &W)
legacy: sort Walkers between good and bad and prepare branching
MCDataType< FullPrecRealType > EnsembleProperty
MCDataType< FullPrecRealType > ensemble_property_
ensemble properties
void clear()
clean up the walker list
int applyNmaxNmin(int current_population)
legacy: apply per rank limit Nmax and Nmin
iterator begin()
return the first iterator