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 VMC.h
10 //////////////////////////////////////////////////////////////////////////////////////
11 
12 #ifndef QMCPLUSPLUS_MCPOPULATION_H
13 #define QMCPLUSPLUS_MCPOPULATION_H
14 
15 #include <iterator>
16 #include <vector>
17 #include <memory>
18 
19 #include "Configuration.h"
20 #include "type_traits/TypeRequire.hpp"
21 #include "Particle/ParticleSet.h"
22 #include "ParticleBase/ParticleAttrib.h"
23 #include "Particle/MCWalkerConfiguration.h"
24 #include "Particle/Walker.h"
25 #include "QMCWaveFunctions/TrialWaveFunction.h"
26 #include "QMCDrivers/WalkerElementsRef.h"
27 #include "OhmmsPETE/OhmmsVector.h"
28 #include "Utilities/FairDivide.h"
29 
30 // forward declaration
31 namespace optimize
32 {
33 struct VariableSet;
34 }
35 
36 namespace qmcplusplus
37 {
38 // forward declaration
39 class QMCHamiltonian;
40 class MCPopulation
41 {
42 public:
43   using MCPWalker          = Walker<QMCTraits, PtclOnLatticeTraits>;
44   using WFBuffer           = MCPWalker::WFBuffer_t;
45   using RealType           = QMCTraits::RealType;
46   using Properties         = MCPWalker::PropertyContainer_t;
47   using IndexType          = QMCTraits::IndexType;
48   using FullPrecRealType   = QMCTraits::FullPrecRealType;
49   using opt_variables_type = optimize::VariableSet;
50 
51 private:
52   // Potential thread safety issue
53   MCDataType<QMCTraits::FullPrecRealType> ensemble_property_;
54 
55   IndexType num_global_walkers_ = 0;
56   IndexType num_local_walkers_  = 0;
57   IndexType num_particles_      = 0;
58   IndexType num_groups_         = 0;
59   IndexType max_samples_        = 0;
60   IndexType target_population_  = 0;
61   IndexType target_samples_     = 0;
62   //Properties properties_;
63   ParticleSet ions_;
64 
65   // By making this a linked list and creating the crowds at the same time we could get first touch.
66   UPtrVector<MCPWalker> walkers_;
67   UPtrVector<MCPWalker> dead_walkers_;
68   std::vector<std::pair<int, int>> particle_group_indexes_;
69   SpeciesSet species_set_;
70   std::vector<RealType> ptclgrp_mass_;
71   ///1/Mass per species
72   std::vector<RealType> ptclgrp_inv_mass_;
73   ///1/Mass per particle
74   std::vector<RealType> ptcl_inv_mass_;
75 
76   // This is necessary MCPopulation is constructed in a simple call scope in QMCDriverFactory from the legacy MCWalkerConfiguration
77   // MCPopulation should have QMCMain scope eventually and the driver will just have a reference to it.
78   TrialWaveFunction* trial_wf_;
79   ParticleSet* elec_particle_set_;
80   QMCHamiltonian* hamiltonian_;
81   // At the moment these are "clones" but I think this design pattern smells.
82   UPtrVector<ParticleSet> walker_elec_particle_sets_;
83   UPtrVector<TrialWaveFunction> walker_trial_wavefunctions_;
84   UPtrVector<QMCHamiltonian> walker_hamiltonians_;
85 
86   // We still haven't cleaned up the dependence between different walker elements so they all need to be tracked
87   // as in the legacy implementation.
88   UPtrVector<ParticleSet> dead_walker_elec_particle_sets_;
89   UPtrVector<TrialWaveFunction> dead_walker_trial_wavefunctions_;
90   UPtrVector<QMCHamiltonian> dead_walker_hamiltonians_;
91 
92   // MCPopulation immutables
93   // would be nice if they were const but we'd lose the default move assignment
94   int num_ranks_;
95   int rank_;
96 
97   // reference to the captured WalkerConfigurations
98   WalkerConfigurations& walker_configs_ref_;
99 
100 public:
101   /** Temporary constructor to deal with MCWalkerConfiguration be the only source of some information
102    *  in QMCDriverFactory.
103    */
104   MCPopulation(int num_ranks,
105                int this_rank,
106                WalkerConfigurations& mcwc,
107                ParticleSet* elecs,
108                TrialWaveFunction* trial_wf,
109                QMCHamiltonian* hamiltonian_);
110 
111   ~MCPopulation();
112   MCPopulation(MCPopulation&) = delete;
113   MCPopulation& operator=(MCPopulation&) = delete;
114   MCPopulation(MCPopulation&&)           = default;
115 
116   /** @ingroup PopulationControl
117    *
118    *  State Requirement:
119    *   * createWalkers must have been called
120    *  @{
121    */
122   WalkerElementsRef spawnWalker();
123   void killWalker(MCPWalker&);
124   void killLastWalker();
125   /** }@ */
126 
127   /** Creates walkers with a clone of the golden electron particle set and golden trial wavefunction
128    *
129    *  \param[in] num_walkers number of living walkers in initial population
130    *  \param[in] reserve multiple above that to reserve >=1.0
131    */
132   void createWalkers(IndexType num_walkers, RealType reserve = 1.0);
133 
134   /** distributes walkers and their "cloned" elements to the elements of a vector
135    *  of unique_ptr to "walker_consumers".
136    *
137    *  a valid "walker_consumer" has a member function of
138    *  void addWalker(MCPWalker& walker, ParticleSet& elecs, TrialWaveFunction& twf, QMCHamiltonian& hamiltonian);
139    */
140   template<typename WTTV>
redistributeWalkers(WTTV & walker_consumers)141   void redistributeWalkers(WTTV& walker_consumers)
142   {
143     // The type returned here is dependent on the integral type that the walker_consumers
144     // use to return there size.
145     auto walkers_per_crowd = fairDivide(walkers_.size(), walker_consumers.size());
146 
147     auto walker_index = 0;
148     for (int i = 0; i < walker_consumers.size(); ++i)
149     {
150       walker_consumers[i]->clearWalkers();
151       for (int j = 0; j < walkers_per_crowd[i]; ++j)
152       {
153         walker_consumers[i]->addWalker(*walkers_[walker_index], *walker_elec_particle_sets_[walker_index],
154                                        *walker_trial_wavefunctions_[walker_index], *walker_hamiltonians_[walker_index]);
155         ++walker_index;
156       }
157     }
158   }
159 
160   void syncWalkersPerRank(Communicate* comm);
161   void measureGlobalEnergyVariance(Communicate& comm, FullPrecRealType& ener, FullPrecRealType& variance) const;
162 
163   /**@ingroup Accessors
164    * @{
165    */
166 
167   /** The number of cases in which this and get_num_local_walkers is so few that
168    *  I strongly suspect it is a design issue.
169    *
170    *  get_active_walkers is onlyh useful between the setting of num_local_walkers_ and
171    *  creation of walkers.  I would reason this is actually a time over which the MCPopulation object
172    *  is invalid. Ideally MCPopulation not process any calls in this state, next best would be to only
173    *  process calls to become valid.
174    */
175   //IndexType get_active_walkers() const { return walkers_.size(); }
get_num_ranks()176   int get_num_ranks() const { return num_ranks_; }
get_rank()177   int get_rank() const { return rank_; }
get_num_global_walkers()178   IndexType get_num_global_walkers() const { return num_global_walkers_; }
get_num_local_walkers()179   IndexType get_num_local_walkers() const { return num_local_walkers_; }
get_num_particles()180   IndexType get_num_particles() const { return num_particles_; }
get_max_samples()181   IndexType get_max_samples() const { return max_samples_; }
get_target_population()182   IndexType get_target_population() const { return target_population_; }
get_target_samples()183   IndexType get_target_samples() const { return target_samples_; }
184   //const Properties& get_properties() const { return properties_; }
get_species_set()185   const SpeciesSet& get_species_set() const { return species_set_; }
get_ions()186   const ParticleSet& get_ions() const { return ions_; }
187 
188   // accessor to the gold copy
get_golden_electrons()189   const ParticleSet* get_golden_electrons() const { return elec_particle_set_; }
get_golden_electrons()190   ParticleSet* get_golden_electrons() { return elec_particle_set_; }
get_golden_twf()191   const TrialWaveFunction& get_golden_twf() const { return *trial_wf_; }
get_golden_twf()192   TrialWaveFunction& get_golden_twf() { return *trial_wf_; }
193   // TODO: the fact this is needed is sad remove need for its existence.
get_golden_hamiltonian()194   QMCHamiltonian& get_golden_hamiltonian() { return *hamiltonian_; }
195 
set_num_global_walkers(IndexType num_global_walkers)196   void set_num_global_walkers(IndexType num_global_walkers) { num_global_walkers_ = num_global_walkers; }
set_num_local_walkers(IndexType num_local_walkers)197   void set_num_local_walkers(IndexType num_local_walkers) { num_local_walkers_ = num_local_walkers; }
198 
set_target(IndexType pop)199   void set_target(IndexType pop) { target_population_ = pop; }
set_target_samples(IndexType samples)200   void set_target_samples(IndexType samples) { target_samples_ = samples; }
201 
set_ensemble_property(const MCDataType<QMCTraits::FullPrecRealType> & ensemble_property)202   void set_ensemble_property(const MCDataType<QMCTraits::FullPrecRealType>& ensemble_property)
203   {
204     ensemble_property_ = ensemble_property;
205   }
206 
get_walkers()207   UPtrVector<MCPWalker>& get_walkers() { return walkers_; }
get_walkers()208   const UPtrVector<MCPWalker>& get_walkers() const { return walkers_; }
get_dead_walkers()209   const UPtrVector<MCPWalker>& get_dead_walkers() const { return dead_walkers_; }
210 
get_elec_particle_sets()211   UPtrVector<ParticleSet>& get_elec_particle_sets() { return walker_elec_particle_sets_; }
212 
get_twfs()213   UPtrVector<TrialWaveFunction>& get_twfs() { return walker_trial_wavefunctions_; }
get_dead_twfs()214   UPtrVector<TrialWaveFunction>& get_dead_twfs() { return dead_walker_trial_wavefunctions_; }
215 
get_hamiltonians()216   UPtrVector<QMCHamiltonian>& get_hamiltonians() { return walker_hamiltonians_; }
get_dead_hamiltonians()217   UPtrVector<QMCHamiltonian>& get_dead_hamiltonians() { return dead_walker_hamiltonians_; }
218 
219   /** Non threadsafe access to walkers and their elements
220    *
221    *  Prefer to distribute the walker elements and access
222    *  through a crowd to support the concurrency design.
223    *
224    *  You should not use this unless absolutely necessary.
225    *  That doesn't include that you would rather just use
226    *  omp parallel and ignore concurrency.
227    */
228   WalkerElementsRef getWalkerElementsRef(const size_t walker_index);
229 
230   /** As long as walker WalkerElements is used we need this for unit tests
231    *
232    *  As operator[] don't use it to ignore the concurrency design.
233    */
234   std::vector<WalkerElementsRef> get_walker_elements();
235 
get_particle_group_indexes()236   const std::vector<std::pair<int, int>>& get_particle_group_indexes() const { return particle_group_indexes_; }
get_ptclgrp_mass()237   const std::vector<RealType>& get_ptclgrp_mass() const { return ptclgrp_mass_; }
get_ptclgrp_inv_mass()238   const std::vector<RealType>& get_ptclgrp_inv_mass() const { return ptclgrp_inv_mass_; }
get_ptcl_inv_mass()239   const std::vector<RealType>& get_ptcl_inv_mass() const { return ptcl_inv_mass_; }
240 
241   /** }@ */
242 
243 
244   /// Set variational parameters for the per-walker copies of the wavefunction.
245   void set_variational_parameters(const opt_variables_type& active);
246 
247   /// check if all the internal vector contain consistent sizes;
248   void checkIntegrity() const;
249 
getWalkerConfigsRef()250   WalkerConfigurations& getWalkerConfigsRef() { return walker_configs_ref_; }
251 
252   // save walker configurations to walker_configs_ref_
253   void saveWalkerConfigurations();
254 };
255 
256 } // namespace qmcplusplus
257 
258 #endif
259