QMCPACK
MCPopulation.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 //
9 // File refactored from: MCWalkerConfiguration.cpp, QMCUpdate.cpp
10 //////////////////////////////////////////////////////////////////////////////////////
11 
12 #include <numeric>
13 
14 #include "MCPopulation.h"
15 #include "Configuration.h"
17 #include "Message/CommOperators.h"
20 
21 namespace qmcplusplus
22 {
24  int this_rank,
25  ParticleSet* elecs,
26  TrialWaveFunction* trial_wf,
27  QMCHamiltonian* hamiltonian)
28  : trial_wf_(trial_wf), elec_particle_set_(elecs), hamiltonian_(hamiltonian), num_ranks_(num_ranks), rank_(this_rank)
29 {
30  const auto num_groups = elecs->groups();
31  ptclgrp_mass_.resize(num_groups);
32  ptclgrp_inv_mass_.resize(num_groups);
33  for (int ig = 0; ig < num_groups; ++ig)
34  {
35  ptclgrp_mass_[ig] = elecs->Mass[elecs->first(ig)];
36  ptclgrp_inv_mass_[ig] = 1.0 / ptclgrp_mass_[ig];
37  }
38 
39  ptcl_inv_mass_.resize(elecs->getTotalNum());
40  for (int ig = 0; ig < num_groups; ++ig)
41  for (int iat = elecs->first(ig); iat < elecs->last(ig); ++iat)
43 }
44 
45 MCPopulation::~MCPopulation() = default;
46 
48 {
49  IndexType num_walkers_plus_reserve = static_cast<IndexType>(num_walkers * reserve);
50 
51  // Hack to hopefully insure no truly new walkers will be made by spawn, since I suspect that
52  // doesn't capture everything that needs to make a walker + elements valid to load from a transferred
53  // buffer;
54  // Ye: need to resize walker_t and ParticleSet Properties
55  // Really MCPopulation does not own this elec_particle_set_ seems like it should be immutable
57 
58  // This pattern is begging for a micro benchmark, is this really better
59  // than the simpler walkers_.pushback;
60  walkers_.resize(num_walkers_plus_reserve);
61  walker_elec_particle_sets_.resize(num_walkers_plus_reserve);
62  walker_trial_wavefunctions_.resize(num_walkers_plus_reserve);
63  walker_hamiltonians_.resize(num_walkers_plus_reserve);
64 
66 
67  //this part is time consuming, it must be threaded and calls should be thread-safe.
68 #pragma omp parallel for
69  for (size_t iw = 0; iw < num_walkers_plus_reserve; iw++)
70  {
71  walkers_[iw] = std::make_unique<MCPWalker>(elec_particle_set_->getTotalNum());
72  walkers_[iw]->Properties = elec_particle_set_->Properties;
73 
74  // initialize coord
75  if (const auto num_existing_walkers = walker_configs.getActiveWalkers())
76  *walkers_[iw] = *walker_configs[iw % num_existing_walkers];
77  else
78  {
79  walkers_[iw]->R = elec_particle_set_->R;
80  walkers_[iw]->spins = elec_particle_set_->spins;
81  }
82 
83  walkers_[iw]->registerData();
84  walkers_[iw]->DataSet.allocate();
85 
86  walker_elec_particle_sets_[iw] = std::make_unique<ParticleSet>(*elec_particle_set_);
90  };
91 
93 
94  int num_walkers_created = 0;
95  for (auto& walker_ptr : walkers_)
96  {
97  if (walker_ptr->getWalkerID() == 0)
98  {
99  // And so walker ID's start at one because 0 is magic.
100  // \todo This is C++ all indexes start at 0, make uninitialized ID = -1
101  walker_ptr->setWalkerID((num_walkers_created++) * num_ranks_ + rank_ + 1);
102  walker_ptr->setParentID(walker_ptr->getWalkerID());
103  }
104  }
105 
106  // kill and spawn walkers update the state variable num_local_walkers_
107  // so it must start at the number of reserved walkers
108  num_local_walkers_ = num_walkers_plus_reserve;
109 
110  IndexType extra_walkers = num_walkers_plus_reserve - num_walkers;
111  // Now we kill the extra reserve walkers and elements that we made.
112  for (int i = 0; i < extra_walkers; ++i)
113  killLastWalker();
114 }
115 
117 {
118  return {*walkers_[index], *walker_elec_particle_sets_[index], *walker_trial_wavefunctions_[index]};
119 }
120 
121 std::vector<WalkerElementsRef> MCPopulation::get_walker_elements()
122 {
123  std::vector<WalkerElementsRef> walker_elements;
124  for (int iw = 0; iw < walkers_.size(); ++iw)
125  {
126  walker_elements.emplace_back(*walkers_[iw], *walker_elec_particle_sets_[iw], *walker_trial_wavefunctions_[iw]);
127  }
128  return walker_elements;
129 }
130 
131 /** creates a walker and returns a reference
132  *
133  * Walkers are reused
134  * It would be better if this could be done just by
135  * reusing memory.
136  * Not thread safe.
137  */
139 {
142 
143  if (dead_walkers_.size() > 0)
144  {
145  walkers_.push_back(std::move(dead_walkers_.back()));
146  dead_walkers_.pop_back();
147  walker_elec_particle_sets_.push_back(std::move(dead_walker_elec_particle_sets_.back()));
151  walker_hamiltonians_.push_back(std::move(dead_walker_hamiltonians_.back()));
152  dead_walker_hamiltonians_.pop_back();
153  // Emulating the legacy implementation valid walker elements were created with the initial walker and DataSet
154  // registration and allocation were done then so are not necessary when resurrecting walkers and elements
155  walkers_.back()->Generation = 0;
156  walkers_.back()->Age = 0;
157  walkers_.back()->Multiplicity = 1.0;
158  walkers_.back()->Weight = 1.0;
159  }
160  else
161  {
162  app_warning() << "Spawning walker number " << walkers_.size() + 1
163  << " outside of reserves, this ideally should never happened." << std::endl;
164  walkers_.push_back(std::make_unique<MCPWalker>(*(walkers_.back())));
165 
166  // There is no value in doing this here because its going to be wiped out
167  // When we load from the receive buffer. It also won't necessarily be correct
168  // Because the buffer is changed by Hamiltonians and wavefunctions that
169  // Add to the dataSet.
170 
171  walker_elec_particle_sets_.emplace_back(std::make_unique<ParticleSet>(*elec_particle_set_));
173  walker_hamiltonians_.emplace_back(
175  walkers_.back()->Multiplicity = 1.0;
176  walkers_.back()->Weight = 1.0;
177  walkers_.back()->setWalkerID((walkers_.size() + 1) * num_ranks_ + rank_ + 1);
178  }
179 
181  return {*walkers_.back().get(), *walker_elec_particle_sets_.back().get(), *walker_trial_wavefunctions_.back().get()};
182 }
183 
184 /** Kill last walker (just barely)
185  *
186  * By kill we mean put it and all its elements in a "dead" list.
187  */
189 {
191  // kill the walker but just barely we need all its setup and connections to remain
192  dead_walkers_.push_back(std::move(walkers_.back()));
193  walkers_.pop_back();
194  dead_walker_elec_particle_sets_.push_back(std::move(walker_elec_particle_sets_.back()));
195  walker_elec_particle_sets_.pop_back();
197  walker_trial_wavefunctions_.pop_back();
198  dead_walker_hamiltonians_.push_back(std::move(walker_hamiltonians_.back()));
199  walker_hamiltonians_.pop_back();
200 }
201 /** Kill a walker (just barely)
202  *
203  * By kill we mean put it and all its elements in a "dead" list.
204  */
206 {
207  // find the walker and move its pointer to the dead walkers vector
208  auto it_walkers = walkers_.begin();
209  auto it_psets = walker_elec_particle_sets_.begin();
210  auto it_twfs = walker_trial_wavefunctions_.begin();
211  auto it_hams = walker_hamiltonians_.begin();
212  while (it_walkers != walkers_.end())
213  {
214  if (&walker == (*it_walkers).get())
215  {
216  dead_walkers_.push_back(std::move(*it_walkers));
217  walkers_.erase(it_walkers);
218  dead_walker_elec_particle_sets_.push_back(std::move(*it_psets));
219  walker_elec_particle_sets_.erase(it_psets);
220  dead_walker_trial_wavefunctions_.push_back(std::move(*it_twfs));
221  walker_trial_wavefunctions_.erase(it_twfs);
222  dead_walker_hamiltonians_.push_back(std::move(*it_hams));
223  walker_hamiltonians_.erase(it_hams);
225  return;
226  }
227  ++it_walkers;
228  ++it_psets;
229  ++it_twfs;
230  ++it_hams;
231  }
232  throw std::runtime_error("Attempt to kill nonexistent walker in MCPopulation!");
233 }
234 
236 {
237  std::vector<IndexType> num_local_walkers_per_rank(comm->size(), 0);
238 
239  num_local_walkers_per_rank[comm->rank()] = num_local_walkers_;
240  comm->allreduce(num_local_walkers_per_rank);
241 
242  num_global_walkers_ = std::accumulate(num_local_walkers_per_rank.begin(), num_local_walkers_per_rank.end(), 0);
243 }
244 
246  FullPrecRealType& ener,
247  FullPrecRealType& variance) const
248 {
249  std::vector<FullPrecRealType> weight_energy_variance(3, 0.0);
250  for (int iw = 0; iw < walker_elec_particle_sets_.size(); iw++)
251  {
252  auto w = walkers_[iw]->Weight;
253  auto e = walker_hamiltonians_[iw]->getLocalEnergy();
254  weight_energy_variance[0] += w;
255  weight_energy_variance[1] += w * e;
256  weight_energy_variance[2] += w * e * e;
257  }
258 
259  comm.allreduce(weight_energy_variance);
260  ener = weight_energy_variance[1] / weight_energy_variance[0];
261  variance = weight_energy_variance[2] / weight_energy_variance[0] - ener * ener;
262 }
263 
265 {
266  // check active walkers
267  const size_t num_local_walkers_active = num_local_walkers_;
268  if (walkers_.size() != num_local_walkers_active)
269  throw std::runtime_error("walkers_ has inconsistent size");
270  if (walker_elec_particle_sets_.size() != num_local_walkers_active)
271  throw std::runtime_error("walker_elec_particle_sets_ has inconsistent size");
272  if (walker_trial_wavefunctions_.size() != num_local_walkers_active)
273  throw std::runtime_error("walker_trial_wavefunctions_ has inconsistent size");
274  if (walker_trial_wavefunctions_.size() != num_local_walkers_active)
275  throw std::runtime_error("walker_trial_wavefunctions_ has inconsistent size");
276  if (walker_hamiltonians_.size() != num_local_walkers_active)
277  throw std::runtime_error("walker_hamiltonians_ has inconsistent size");
278 
279  // check dead walkers
280  const size_t num_local_walkers_dead = dead_walkers_.size();
281  if (dead_walker_elec_particle_sets_.size() != num_local_walkers_dead)
282  throw std::runtime_error("dead_walker_elec_particle_sets_ has inconsistent size");
283  if (dead_walker_trial_wavefunctions_.size() != num_local_walkers_dead)
284  throw std::runtime_error("dead_walker_trial_wavefunctions_ has inconsistent size");
285  if (dead_walker_trial_wavefunctions_.size() != num_local_walkers_dead)
286  throw std::runtime_error("dead_walker_trial_wavefunctions_ has inconsistent size");
287  if (dead_walker_hamiltonians_.size() != num_local_walkers_dead)
288  throw std::runtime_error("dead_walker_hamiltonians_ has inconsistent size");
289 }
290 
292 {
294  for (int iw = 0; iw < walker_elec_particle_sets_.size(); iw++)
295  {
296  walker_elec_particle_sets_[iw]->saveWalker(*walker_configs[iw]);
297  walker_configs[iw]->Weight = walkers_[iw]->Weight;
298  }
299 }
300 } // namespace qmcplusplus
std::vector< WalkerElementsRef > get_walker_elements()
As long as walker WalkerElements is used we need this for unit tests.
QMCTraits::RealType RealType
Definition: MCPopulation.h:43
UPtrVector< TrialWaveFunction > walker_trial_wavefunctions_
Definition: MCPopulation.h:77
WalkerElementsRef spawnWalker()
State Requirement:
void pause()
Pause the summary and log streams.
std::ostream & app_warning()
Definition: OutputManager.h:69
std::vector< RealType > ptcl_inv_mass_
1/Mass per particle
Definition: MCPopulation.h:67
helper functions for EinsplineSetBuilder
Definition: Configuration.h:43
QMCTraits::FullPrecRealType FullPrecRealType
Definition: MCPopulation.h:46
int rank() const
return the rank
Definition: Communicate.h:116
ParticleScalar spins
internal spin variables for dynamical spin calculations
Definition: ParticleSet.h:81
PropertyContainer_t Properties
properties of the current walker
Definition: ParticleSet.h:119
size_t getActiveWalkers() const
return the number of active walkers
size_t getTotalNum() const
Definition: ParticleSet.h:493
type for returning the walker and its elements from MCPopulation
A set of light weight walkers that are carried between driver sections and restart.
UPtrVector< QMCHamiltonian > dead_walker_hamiltonians_
Definition: MCPopulation.h:84
const char num_walkers[]
Definition: HDFVersion.h:37
Collection of Local Energy Operators.
QMCHamiltonian * hamiltonian_
Definition: MCPopulation.h:74
int first(int igroup) const
return the first index of a group i
Definition: ParticleSet.h:514
MCPopulation(int num_ranks, int this_rank, ParticleSet *elecs, TrialWaveFunction *trial_wf, QMCHamiltonian *hamiltonian_)
Temporary constructor to deal with MCWalkerConfiguration be the only source of some information in QM...
int size() const
return the number of tasks
Definition: Communicate.h:118
std::unique_ptr< TrialWaveFunction > makeClone(ParticleSet &tqp) const
OutputManagerClass outputManager(Verbosity::HIGH)
UPtrVector< QMCHamiltonian > walker_hamiltonians_
Definition: MCPopulation.h:78
PropertySetType PropertyList
name-value map of Walker Properties
Definition: ParticleSet.h:112
void measureGlobalEnergyVariance(Communicate &comm, FullPrecRealType &ener, FullPrecRealType &variance) const
void checkIntegrity() const
}@
Wrapping information on parallelism.
Definition: Communicate.h:68
int groups() const
return the number of groups
Definition: ParticleSet.h:511
void allreduce(T &)
UPtrVector< TrialWaveFunction > dead_walker_trial_wavefunctions_
Definition: MCPopulation.h:83
void resume()
Resume the summary and log streams.
Specialized paritlce class for atomistic simulations.
Definition: ParticleSet.h:55
std::vector< RealType > ptclgrp_inv_mass_
1/Mass per species
Definition: MCPopulation.h:65
UPtrVector< ParticleSet > dead_walker_elec_particle_sets_
Definition: MCPopulation.h:82
void syncWalkersPerRank(Communicate *comm)
std::vector< RealType > ptclgrp_mass_
Definition: MCPopulation.h:63
ParticlePos R
Position.
Definition: ParticleSet.h:79
int last(int igroup) const
return the last index of a group i
Definition: ParticleSet.h:517
QMCTraits::IndexType IndexType
Definition: MCPopulation.h:45
void createWalkers(IndexType num_walkers, const WalkerConfigurations &walker_configs, RealType reserve=1.0)
}@
void saveWalkerConfigurations(WalkerConfigurations &walker_configs)
save walker configurations to walker_configs_ref_
Declaration of a TrialWaveFunction.
void resize(int numWalkers, size_t numPtcls)
clean up the walker list and make a new list
Class to represent a many-body trial wave function.
ParticleScalar Mass
mass of each particle
Definition: ParticleSet.h:87
TrialWaveFunction * trial_wf_
Definition: MCPopulation.h:72
UPtrVector< ParticleSet > walker_elec_particle_sets_
Definition: MCPopulation.h:76
UPtrVector< MCPWalker > walkers_
Definition: MCPopulation.h:61
WalkerElementsRef getWalkerElementsRef(const size_t walker_index)
Non threadsafe access to walkers and their elements.
UPtrVector< MCPWalker > dead_walkers_
Definition: MCPopulation.h:62
std::unique_ptr< QMCHamiltonian > makeClone(ParticleSet &qp, TrialWaveFunction &psi) const
return a clone
void killWalker(MCPWalker &)
Kill a walker (just barely)
A container class to represent a walker.
Definition: Walker.h:49
void killLastWalker()
Kill last walker (just barely)
ParticleSet * elec_particle_set_
Definition: MCPopulation.h:73
Declaration of QMCHamiltonian.