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"
16 #include "Concurrency/ParallelExecutor.hpp"
17 #include "Message/CommOperators.h"
18 #include "QMCWaveFunctions/TrialWaveFunction.h"
19 #include "QMCHamiltonians/QMCHamiltonian.h"
20 
21 namespace qmcplusplus
22 {
MCPopulation(int num_ranks,int this_rank,WalkerConfigurations & mcwc,ParticleSet * elecs,TrialWaveFunction * trial_wf,QMCHamiltonian * hamiltonian)23 MCPopulation::MCPopulation(int num_ranks,
24                            int this_rank,
25                            WalkerConfigurations& mcwc,
26                            ParticleSet* elecs,
27                            TrialWaveFunction* trial_wf,
28                            QMCHamiltonian* hamiltonian)
29     : trial_wf_(trial_wf),
30       elec_particle_set_(elecs),
31       hamiltonian_(hamiltonian),
32       num_ranks_(num_ranks),
33       rank_(this_rank),
34       walker_configs_ref_(mcwc)
35 {
36   num_global_walkers_ = mcwc.getGlobalNumWalkers();
37   num_local_walkers_  = mcwc.getActiveWalkers();
38   num_particles_      = elecs->getTotalNum();
39 
40   // MCWalkerConfiguration doesn't give actual number of groups
41   num_groups_ = elecs->groups();
42   particle_group_indexes_.resize(num_groups_);
43   for (int i = 0; i < num_groups_; ++i)
44   {
45     particle_group_indexes_[i].first  = elecs->first(i);
46     particle_group_indexes_[i].second = elecs->last(i);
47   }
48   ptclgrp_mass_.resize(num_groups_);
49   for (int ig = 0; ig < num_groups_; ++ig)
50     ptclgrp_mass_[ig] = elecs->Mass[ig];
51   ptclgrp_inv_mass_.resize(num_groups_);
52   for (int ig = 0; ig < num_groups_; ++ig)
53     ptclgrp_inv_mass_[ig] = 1.0 / ptclgrp_mass_[ig];
54   ptcl_inv_mass_.resize(num_particles_);
55   for (int ig = 0; ig < num_groups_; ++ig)
56   {
57     for (int iat = particle_group_indexes_[ig].first; iat < particle_group_indexes_[ig].second; ++iat)
58       ptcl_inv_mass_[iat] = ptclgrp_inv_mass_[ig];
59   }
60 }
61 
~MCPopulation()62 MCPopulation::~MCPopulation()
63 {
64   // if there are active walkers, save them to lightweight walker configuration list.
65   if (walkers_.size())
66     saveWalkerConfigurations();
67 }
68 
createWalkers(IndexType num_walkers,RealType reserve)69 void MCPopulation::createWalkers(IndexType num_walkers, RealType reserve)
70 {
71   IndexType num_walkers_plus_reserve = static_cast<IndexType>(num_walkers * reserve);
72 
73   // Hack to hopefully insure no truly new walkers will be made by spawn, since I suspect that
74   // doesn't capture everything that needs to make a walker + elements valid to load from a transferred
75   // buffer;
76   // Ye: need to resize walker_t and ParticleSet Properties
77   // Really MCPopulation does not own this elec_particle_set_  seems like it should be immutable
78   elec_particle_set_->Properties.resize(1, elec_particle_set_->PropertyList.size());
79 
80   // This pattern is begging for a micro benchmark, is this really better
81   // than the simpler walkers_.pushback;
82   walkers_.resize(num_walkers_plus_reserve);
83   walker_elec_particle_sets_.resize(num_walkers_plus_reserve);
84   walker_trial_wavefunctions_.resize(num_walkers_plus_reserve);
85   walker_hamiltonians_.resize(num_walkers_plus_reserve);
86 
87   outputManager.pause();
88 
89   //this part is time consuming, it must be threaded and calls should be thread-safe.
90 #pragma omp parallel for
91   for (size_t iw = 0; iw < num_walkers_plus_reserve; iw++)
92   {
93     walkers_[iw]             = std::make_unique<MCPWalker>(num_particles_);
94     walkers_[iw]->R          = elec_particle_set_->R;
95     walkers_[iw]->spins      = elec_particle_set_->spins;
96     walkers_[iw]->Properties = elec_particle_set_->Properties;
97     walkers_[iw]->registerData();
98     walkers_[iw]->DataSet.allocate();
99 
100     if (iw < walker_configs_ref_.WalkerList.size())
101       *walkers_[iw]       = *walker_configs_ref_[iw];
102 
103     walker_elec_particle_sets_[iw] = std::make_unique<ParticleSet>(*elec_particle_set_);
104     walker_trial_wavefunctions_[iw].reset(trial_wf_->makeClone(*walker_elec_particle_sets_[iw]));
105     walker_hamiltonians_[iw].reset(
106         hamiltonian_->makeClone(*walker_elec_particle_sets_[iw], *walker_trial_wavefunctions_[iw]));
107   };
108 
109   outputManager.resume();
110 
111   int num_walkers_created = 0;
112   for (auto& walker_ptr : walkers_)
113   {
114     if (walker_ptr->ID == 0)
115     {
116       // And so walker ID's start at one because 0 is magic.
117       // \todo This is C++ all indexes start at 0, make uninitialized ID = -1
118       walker_ptr->ID       = (++num_walkers_created) * num_ranks_ + rank_;
119       walker_ptr->ParentID = walker_ptr->ID;
120     }
121   }
122 
123   // kill and spawn walkers update the state variable num_local_walkers_
124   // so it must start at the number of reserved walkers
125   num_local_walkers_ = num_walkers_plus_reserve;
126 
127   IndexType extra_walkers = num_walkers_plus_reserve - num_walkers;
128   // Now we kill the extra reserve walkers and elements that we made.
129   for (int i = 0; i < extra_walkers; ++i)
130     killLastWalker();
131 }
132 
getWalkerElementsRef(const size_t index)133 WalkerElementsRef MCPopulation::getWalkerElementsRef(const size_t index)
134 {
135   return {*walkers_[index], *walker_elec_particle_sets_[index], *walker_trial_wavefunctions_[index]};
136 }
137 
get_walker_elements()138 std::vector<WalkerElementsRef> MCPopulation::get_walker_elements()
139 {
140   std::vector<WalkerElementsRef> walker_elements;
141   for (int iw = 0; iw < walkers_.size(); ++iw)
142   {
143     walker_elements.emplace_back(*walkers_[iw], *walker_elec_particle_sets_[iw], *walker_trial_wavefunctions_[iw]);
144   }
145   return walker_elements;
146 }
147 
148 /** creates a walker and returns a reference
149  *
150  *  Walkers are reused
151  *  It would be better if this could be done just by
152  *  reusing memory.
153  *  Not thread safe.
154  */
spawnWalker()155 WalkerElementsRef MCPopulation::spawnWalker()
156 {
157   ++num_local_walkers_;
158   outputManager.pause();
159 
160   if (dead_walkers_.size() > 0)
161   {
162     walkers_.push_back(std::move(dead_walkers_.back()));
163     dead_walkers_.pop_back();
164     walker_elec_particle_sets_.push_back(std::move(dead_walker_elec_particle_sets_.back()));
165     dead_walker_elec_particle_sets_.pop_back();
166     walker_trial_wavefunctions_.push_back(std::move(dead_walker_trial_wavefunctions_.back()));
167     dead_walker_trial_wavefunctions_.pop_back();
168     walker_hamiltonians_.push_back(std::move(dead_walker_hamiltonians_.back()));
169     dead_walker_hamiltonians_.pop_back();
170     // Emulating the legacy implementation valid walker elements were created with the initial walker and DataSet
171     // registration and allocation were done then so are not necessary when resurrecting walkers and elements
172     walkers_.back()->Generation         = 0;
173     walkers_.back()->Age                = 0;
174     walkers_.back()->ReleasedNodeWeight = 1.0;
175     walkers_.back()->ReleasedNodeAge    = 0;
176     walkers_.back()->Multiplicity       = 1.0;
177     walkers_.back()->Weight             = 1.0;
178   }
179   else
180   {
181     app_warning() << "Spawning walker number " << walkers_.size() + 1
182                   << " outside of reserves, this ideally should never happend." << std::endl;
183     walkers_.push_back(std::make_unique<MCPWalker>(*(walkers_.back())));
184 
185     // There is no value in doing this here because its going to be wiped out
186     // When we load from the receive buffer. It also won't necessarily be correct
187     // Because the buffer is changed by Hamiltonians and wavefunctions that
188     // Add to the dataSet.
189 
190     walker_elec_particle_sets_.emplace_back(std::make_unique<ParticleSet>(*elec_particle_set_));
191     walker_trial_wavefunctions_.emplace_back(trial_wf_->makeClone(*walker_elec_particle_sets_.back()));
192     walker_hamiltonians_.emplace_back(
193         hamiltonian_->makeClone(*walker_elec_particle_sets_.back(), *walker_trial_wavefunctions_.back()));
194     walkers_.back()->Multiplicity = 1.0;
195     walkers_.back()->Weight       = 1.0;
196   }
197 
198   outputManager.resume();
199   return {*walkers_.back().get(), *walker_elec_particle_sets_.back().get(), *walker_trial_wavefunctions_.back().get()};
200 }
201 
202 /** Kill last walker (just barely)
203  *
204  *  By kill we mean put it and all its elements in a "dead" list.
205  */
killLastWalker()206 void MCPopulation::killLastWalker()
207 {
208   --num_local_walkers_;
209   // kill the walker but just barely we need all its setup and connections to remain
210   dead_walkers_.push_back(std::move(walkers_.back()));
211   walkers_.pop_back();
212   dead_walker_elec_particle_sets_.push_back(std::move(walker_elec_particle_sets_.back()));
213   walker_elec_particle_sets_.pop_back();
214   dead_walker_trial_wavefunctions_.push_back(std::move(walker_trial_wavefunctions_.back()));
215   walker_trial_wavefunctions_.pop_back();
216   dead_walker_hamiltonians_.push_back(std::move(walker_hamiltonians_.back()));
217   walker_hamiltonians_.pop_back();
218 }
219 /** Kill a walker (just barely)
220  *
221  *  By kill we mean put it and all its elements in a "dead" list.
222  */
killWalker(MCPWalker & walker)223 void MCPopulation::killWalker(MCPWalker& walker)
224 {
225   // find the walker and move its pointer to the dead walkers vector
226   auto it_walkers = walkers_.begin();
227   auto it_psets   = walker_elec_particle_sets_.begin();
228   auto it_twfs    = walker_trial_wavefunctions_.begin();
229   auto it_hams    = walker_hamiltonians_.begin();
230   while (it_walkers != walkers_.end())
231   {
232     if (&walker == (*it_walkers).get())
233     {
234       dead_walkers_.push_back(std::move(*it_walkers));
235       walkers_.erase(it_walkers);
236       dead_walker_elec_particle_sets_.push_back(std::move(*it_psets));
237       walker_elec_particle_sets_.erase(it_psets);
238       dead_walker_trial_wavefunctions_.push_back(std::move(*it_twfs));
239       walker_trial_wavefunctions_.erase(it_twfs);
240       dead_walker_hamiltonians_.push_back(std::move(*it_hams));
241       walker_hamiltonians_.erase(it_hams);
242       --num_local_walkers_;
243       return;
244     }
245     ++it_walkers;
246     ++it_psets;
247     ++it_twfs;
248     ++it_hams;
249   }
250   throw std::runtime_error("Attempt to kill nonexistent walker in MCPopulation!");
251 }
252 
syncWalkersPerRank(Communicate * comm)253 void MCPopulation::syncWalkersPerRank(Communicate* comm)
254 {
255   std::vector<IndexType> num_local_walkers_per_rank(comm->size(), 0);
256 
257   num_local_walkers_per_rank[comm->rank()] = num_local_walkers_;
258   comm->allreduce(num_local_walkers_per_rank);
259 
260   num_global_walkers_ = std::accumulate(num_local_walkers_per_rank.begin(), num_local_walkers_per_rank.end(), 0);
261 }
262 
measureGlobalEnergyVariance(Communicate & comm,FullPrecRealType & ener,FullPrecRealType & variance) const263 void MCPopulation::measureGlobalEnergyVariance(Communicate& comm, FullPrecRealType& ener, FullPrecRealType& variance) const
264 {
265   std::vector<FullPrecRealType> weight_energy_variance(3, 0.0);
266   for (int iw = 0; iw < walker_elec_particle_sets_.size(); iw++)
267   {
268     auto w = walkers_[iw]->Weight;
269     auto e = walker_hamiltonians_[iw]->getLocalEnergy();
270     weight_energy_variance[0] += w;
271     weight_energy_variance[1] += w * e;
272     weight_energy_variance[2] += w * e * e;
273   }
274 
275   comm.allreduce(weight_energy_variance);
276   ener = weight_energy_variance[1] / weight_energy_variance[0];
277   variance = weight_energy_variance[2] / weight_energy_variance[0] - ener * ener;
278 }
279 
set_variational_parameters(const opt_variables_type & active)280 void MCPopulation::set_variational_parameters(const opt_variables_type& active)
281 {
282   for (auto it_twfs = walker_trial_wavefunctions_.begin(); it_twfs != walker_trial_wavefunctions_.end(); ++it_twfs)
283   {
284     (*it_twfs).get()->resetParameters(active);
285   }
286 }
287 
checkIntegrity() const288 void MCPopulation::checkIntegrity() const
289 {
290   // check active walkers
291   const size_t num_local_walkers_active = num_local_walkers_;
292   if (walkers_.size() != num_local_walkers_active)
293     throw std::runtime_error("walkers_ has inconsistent size");
294   if (walker_elec_particle_sets_.size() != num_local_walkers_active)
295     throw std::runtime_error("walker_elec_particle_sets_ has inconsistent size");
296   if (walker_trial_wavefunctions_.size() != num_local_walkers_active)
297     throw std::runtime_error("walker_trial_wavefunctions_ has inconsistent size");
298   if (walker_trial_wavefunctions_.size() != num_local_walkers_active)
299     throw std::runtime_error("walker_trial_wavefunctions_ has inconsistent size");
300   if (walker_hamiltonians_.size() != num_local_walkers_active)
301     throw std::runtime_error("walker_hamiltonians_ has inconsistent size");
302 
303   // check dead walkers
304   const size_t num_local_walkers_dead = dead_walkers_.size();
305   if (dead_walker_elec_particle_sets_.size() != num_local_walkers_dead)
306     throw std::runtime_error("dead_walker_elec_particle_sets_ has inconsistent size");
307   if (dead_walker_trial_wavefunctions_.size() != num_local_walkers_dead)
308     throw std::runtime_error("dead_walker_trial_wavefunctions_ has inconsistent size");
309   if (dead_walker_trial_wavefunctions_.size() != num_local_walkers_dead)
310     throw std::runtime_error("dead_walker_trial_wavefunctions_ has inconsistent size");
311   if (dead_walker_hamiltonians_.size() != num_local_walkers_dead)
312     throw std::runtime_error("dead_walker_hamiltonians_ has inconsistent size");
313 }
314 
saveWalkerConfigurations()315 void MCPopulation::saveWalkerConfigurations()
316 {
317   walker_configs_ref_.resize(walker_elec_particle_sets_.size(), elec_particle_set_->getTotalNum());
318   for (int iw = 0; iw < walker_elec_particle_sets_.size(); iw++)
319     walker_elec_particle_sets_[iw]->saveWalker(*walker_configs_ref_[iw]);
320 }
321 
322 
323 } // namespace qmcplusplus
324