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