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: Ken Esler, kpesler@gmail.com, University of Illinois at Urbana-Champaign
8 //                    Luke Shulenburger, lshulen@sandia.gov, Sandia National Laboratories
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 //                    Jaron T. Krogel, krogeljt@ornl.gov, Oak Ridge National Laboratory
12 //                    Ye Luo, yeluo@anl.gov, Argonne National Laboratory
13 //                    Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
14 //                    Mark Dewing, markdewing@gmail.com, University of Illinois at Urbana-Champaign
15 //
16 // File created by: Jeongnim Kim, jeongnim.kim@gmail.com, University of Illinois at Urbana-Champaign
17 //////////////////////////////////////////////////////////////////////////////////////
18 
19 
20 #include <numeric>
21 #include <iomanip>
22 #include "ParticleSet.h"
23 #include "Particle/DynamicCoordinatesBuilder.h"
24 #include "Particle/DistanceTableData.h"
25 #include "Particle/createDistanceTable.h"
26 #include "LongRange/StructFact.h"
27 #include "Utilities/IteratorUtility.h"
28 #include "Utilities/RandomGenerator.h"
29 #include "ParticleBase/RandomSeqGenerator.h"
30 
31 //#define PACK_DISTANCETABLES
32 
33 namespace qmcplusplus
34 {
35 using WP = WalkerProperties::Indexes;
36 
37 //using namespace particle_info;
38 
39 #ifdef QMC_CUDA
40 template<>
41 int ParticleSet::Walker_t::cuda_DataSize = 0;
42 #endif
43 
44 enum PSetTimers
45 {
46   PS_newpos,
47   PS_donePbyP,
48   PS_accept,
49   PS_loadWalker,
50   PS_update,
51   PS_dt_move,
52   PS_mw_copy
53 };
54 
generatePSetTimerNames(std::string & obj_name)55 static const TimerNameList_t<PSetTimers> generatePSetTimerNames(std::string& obj_name)
56 {
57   return {{PS_newpos, "ParticleSet:" + obj_name + "::computeNewPosDT"},
58           {PS_donePbyP, "ParticleSet:" + obj_name + "::donePbyP"},
59           {PS_accept, "ParticleSet:" + obj_name + "::acceptMove"},
60           {PS_loadWalker, "ParticleSet:" + obj_name + "::loadWalker"},
61           {PS_update, "ParticleSet:" + obj_name + "::update"},
62           {PS_dt_move, "ParticleSet:" + obj_name + "::dt_move"},
63           {PS_mw_copy, "ParticleSet:" + obj_name + "::mw_copy"}};
64 }
65 
ParticleSet(const DynamicCoordinateKind kind)66 ParticleSet::ParticleSet(const DynamicCoordinateKind kind)
67     : quantum_domain(classical),
68       IsGrouped(true),
69       SameMass(true),
70       ThreadID(0),
71       activePtcl(-1),
72       Properties(0, 0, 1, WP::MAXPROPERTIES),
73       myTwist(0.0),
74       ParentName("0"),
75       TotalNum(0),
76       coordinates_(createDynamicCoordinates(kind))
77 {
78   initPropertyList();
79   setup_timers(myTimers, generatePSetTimerNames(myName), timer_level_medium);
80 }
81 
ParticleSet(const ParticleSet & p)82 ParticleSet::ParticleSet(const ParticleSet& p)
83     : IsGrouped(p.IsGrouped),
84       SameMass(true),
85       ThreadID(0),
86       activePtcl(-1),
87       mySpecies(p.getSpeciesSet()),
88       Properties(p.Properties),
89       myTwist(0.0),
90       ParentName(p.parentName()),
91       coordinates_(p.coordinates_->makeClone())
92 {
93   set_quantum_domain(p.quantum_domain);
94   assign(p); //only the base is copied, assumes that other properties are not assignable
95   //need explicit copy:
96   Mass = p.Mass;
97   Z    = p.Z;
98   //std::ostringstream o;
99   //o<<p.getName()<<ObjectTag;
100   //this->setName(o.str());
101   //app_log() << "  Copying a particle set " << p.getName() << " to " << this->getName() << " groups=" << groups() << std::endl;
102   myName              = p.getName();
103   PropertyList.Names  = p.PropertyList.Names;
104   PropertyList.Values = p.PropertyList.Values;
105   PropertyHistory     = p.PropertyHistory;
106   Collectables        = p.Collectables;
107   //construct the distance tables with the same order
108   for (int i = 0; i < p.DistTables.size(); ++i)
109     addTable(p.DistTables[i]->origin(), p.DistTables[i]->getFullTableNeeds());
110   if (p.SK)
111   {
112     LRBox = p.LRBox;                             //copy LRBox
113     SK    = std::make_unique<StructFact>(*p.SK); //safe to use the copy constructor
114     //R.InUnit=p.R.InUnit;
115     //createSK();
116     //SK->DoUpdate=p.SK->DoUpdate;
117   }
118   setup_timers(myTimers, generatePSetTimerNames(myName), timer_level_medium);
119   myTwist = p.myTwist;
120 
121   G = p.G;
122   L = p.L;
123 }
124 
~ParticleSet()125 ParticleSet::~ParticleSet()
126 {
127   DEBUG_MEMORY("ParticleSet::~ParticleSet");
128   delete_iter(DistTables.begin(), DistTables.end());
129 }
130 
create(int numPtcl)131 void ParticleSet::create(int numPtcl)
132 {
133   resize(numPtcl);
134   SubPtcl.resize(2);
135   SubPtcl[0] = 0;
136   SubPtcl[1] = numPtcl;
137 }
138 
create(const std::vector<int> & agroup)139 void ParticleSet::create(const std::vector<int>& agroup)
140 {
141   SubPtcl.resize(agroup.size() + 1);
142   SubPtcl[0] = 0;
143   for (int is = 0; is < agroup.size(); is++)
144     SubPtcl[is + 1] = SubPtcl[is] + agroup[is];
145   size_t nsum = SubPtcl[agroup.size()];
146   resize(nsum);
147   TotalNum = nsum;
148   int loc  = 0;
149   for (int i = 0; i < agroup.size(); i++)
150   {
151     for (int j = 0; j < agroup[i]; j++, loc++)
152       GroupID[loc] = i;
153   }
154 }
155 
set_quantum_domain(quantum_domains qdomain)156 void ParticleSet::set_quantum_domain(quantum_domains qdomain)
157 {
158   if (quantum_domain_valid(qdomain))
159     quantum_domain = qdomain;
160   else
161     APP_ABORT("ParticleSet::set_quantum_domain\n  input quantum domain is not valid for particles");
162 }
163 
resetGroups()164 void ParticleSet::resetGroups()
165 {
166   int nspecies = mySpecies.getTotalNum();
167   // Usually an empty ParticleSet indicates an error in the input file,
168   // but in some cases it is useful.  Allow an empty ParticleSet if it
169   // has the special name "empty".
170   if (nspecies == 0 && getName() != "empty")
171   {
172     APP_ABORT("ParticleSet::resetGroups() Failed. No species exisits");
173   }
174   int natt = mySpecies.numAttributes();
175   int qind = mySpecies.addAttribute("charge");
176   if (natt == qind)
177   {
178     app_log() << " Missing charge attribute of the SpeciesSet " << myName << " particleset" << std::endl;
179     app_log() << " Assume neutral particles Z=0.0 " << std::endl;
180     for (int ig = 0; ig < nspecies; ig++)
181       mySpecies(qind, ig) = 0.0;
182   }
183   for (int iat = 0; iat < Z.size(); iat++)
184     Z[iat] = mySpecies(qind, GroupID[iat]);
185   natt        = mySpecies.numAttributes();
186   int massind = mySpecies.addAttribute("mass");
187   if (massind == natt)
188   {
189     for (int ig = 0; ig < nspecies; ig++)
190       mySpecies(massind, ig) = 1.0;
191   }
192   SameMass  = true;
193   double m0 = mySpecies(massind, 0);
194   for (int ig = 1; ig < nspecies; ig++)
195     SameMass &= (mySpecies(massind, ig) == m0);
196   if (SameMass)
197     app_log() << "  All the species have the same mass " << m0 << std::endl;
198   else
199     app_log() << "  Distinctive masses for each species " << std::endl;
200   for (int iat = 0; iat < Mass.size(); iat++)
201     Mass[iat] = mySpecies(massind, GroupID[iat]);
202   std::vector<int> ng(nspecies, 0);
203   for (int iat = 0; iat < GroupID.size(); iat++)
204   {
205     if (GroupID[iat] < nspecies)
206       ng[GroupID[iat]]++;
207     else
208       APP_ABORT("ParticleSet::resetGroups() Failed. GroupID is out of bound.");
209   }
210   // safety check if any group of particles has size 0, instruct users to fix the input.
211   for (int group_id = 0; group_id < nspecies; group_id++)
212     if (ng[group_id] == 0 && getName() != "empty")
213     {
214       std::ostringstream err_msg;
215       err_msg << "ParticleSet::resetGroups() Failed. ParticleSet '" << myName << "' "
216               << "has group '" << mySpecies.speciesName[group_id] << "' containing 0 particles. "
217               << "Remove this group from input!" << std::endl;
218       APP_ABORT(err_msg.str());
219     }
220   SubPtcl.resize(nspecies + 1);
221   SubPtcl[0] = 0;
222   for (int i = 0; i < nspecies; ++i)
223     SubPtcl[i + 1] = SubPtcl[i] + ng[i];
224   int membersize = mySpecies.addAttribute("membersize");
225   for (int ig = 0; ig < nspecies; ++ig)
226     mySpecies(membersize, ig) = ng[ig];
227   //orgID=ID;
228   //orgGroupID=GroupID;
229   int new_id = 0;
230   for (int i = 0; i < nspecies; ++i)
231     for (int iat = 0; iat < GroupID.size(); ++iat)
232       if (GroupID[iat] == i)
233         IndirectID[new_id++] = ID[iat];
234   IsGrouped = true;
235   for (int iat = 0; iat < ID.size(); ++iat)
236     IsGrouped &= (IndirectID[iat] == ID[iat]);
237 }
238 
randomizeFromSource(ParticleSet & src)239 void ParticleSet::randomizeFromSource(ParticleSet& src)
240 {
241   SpeciesSet& srcSpSet(src.getSpeciesSet());
242   SpeciesSet& spSet(getSpeciesSet());
243   int srcChargeIndx = srcSpSet.addAttribute("charge");
244   int srcMemberIndx = srcSpSet.addAttribute("membersize");
245   int ChargeIndex   = spSet.addAttribute("charge");
246   int MemberIndx    = spSet.addAttribute("membersize");
247   int Nsrc          = src.getTotalNum();
248   int Nptcl         = getTotalNum();
249   int NumSpecies    = spSet.TotalNum;
250   int NumSrcSpecies = srcSpSet.TotalNum;
251   //Store information about charges and number of each species
252   std::vector<int> Zat, Zspec, NofSpecies, NofSrcSpecies, CurElec;
253   Zat.resize(Nsrc);
254   Zspec.resize(NumSrcSpecies);
255   NofSpecies.resize(NumSpecies);
256   CurElec.resize(NumSpecies);
257   NofSrcSpecies.resize(NumSrcSpecies);
258   for (int spec = 0; spec < NumSrcSpecies; spec++)
259   {
260     Zspec[spec]         = (int)round(srcSpSet(srcChargeIndx, spec));
261     NofSrcSpecies[spec] = (int)round(srcSpSet(srcMemberIndx, spec));
262   }
263   for (int spec = 0; spec < NumSpecies; spec++)
264   {
265     NofSpecies[spec] = (int)round(spSet(MemberIndx, spec));
266     CurElec[spec]    = first(spec);
267   }
268   int totQ = 0;
269   for (int iat = 0; iat < Nsrc; iat++)
270     totQ += Zat[iat] = Zspec[src.GroupID[iat]];
271   app_log() << "  Total ion charge    = " << totQ << std::endl;
272   totQ -= Nptcl;
273   app_log() << "  Total system charge = " << totQ << std::endl;
274   // Now, loop over ions, attaching electrons to them to neutralize
275   // charge
276   int spToken = 0;
277   // This is decremented when we run out of electrons in each species
278   int spLeft = NumSpecies;
279   std::vector<PosType> gaussRand(Nptcl);
280   makeGaussRandom(gaussRand);
281   for (int iat = 0; iat < Nsrc; iat++)
282   {
283     // Loop over electrons to add, selecting round-robin from the
284     // electron species
285     int z = Zat[iat];
286     while (z > 0 && spLeft)
287     {
288       int sp = spToken++ % NumSpecies;
289       if (NofSpecies[sp])
290       {
291         NofSpecies[sp]--;
292         z--;
293         int elec = CurElec[sp]++;
294         app_log() << "  Assigning " << (sp ? "down" : "up  ") << " electron " << elec << " to ion " << iat
295                   << " with charge " << z << std::endl;
296         double radius = 0.5 * std::sqrt((double)Zat[iat]);
297         R[elec]       = src.R[iat] + radius * gaussRand[elec];
298       }
299       else
300         spLeft--;
301     }
302   }
303   // Assign remaining electrons
304   int ion = 0;
305   for (int sp = 0; sp < NumSpecies; sp++)
306   {
307     for (int ie = 0; ie < NofSpecies[sp]; ie++)
308     {
309       int iat       = ion++ % Nsrc;
310       double radius = std::sqrt((double)Zat[iat]);
311       int elec      = CurElec[sp]++;
312       R[elec]       = src.R[iat] + radius * gaussRand[elec];
313     }
314   }
315 }
316 
317 ///write to a std::ostream
get(std::ostream & os) const318 bool ParticleSet::get(std::ostream& os) const
319 {
320   os << "  ParticleSet '" << getName() << "' contains " << TotalNum << " particles : ";
321   if (SubPtcl.size() > 0)
322     for (int i = 0; i < SubPtcl.size() - 1; i++)
323       os << " " << mySpecies.speciesName[i] << "(" << SubPtcl[i + 1] - SubPtcl[i] << ")";
324   os << std::endl;
325   if (!IsGrouped)
326     os << "    Particles are not grouped by species in the input file. Algorithms may not be optimal!" << std::endl;
327   os << std::endl;
328 
329   const size_t maxParticlesToPrint = 10;
330   size_t numToPrint                = std::min(TotalNum, maxParticlesToPrint);
331 
332   for (int i = 0; i < numToPrint; i++)
333   {
334     os << "    " << mySpecies.speciesName[GroupID[i]] << R[i] << std::endl;
335   }
336   if (numToPrint < TotalNum)
337   {
338     os << "    (... and " << (TotalNum - numToPrint) << " more particle positions ...)" << std::endl;
339   }
340   os << std::endl;
341 
342   for (const std::string& description : distTableDescriptions)
343     os << description;
344   os << std::endl;
345   return true;
346 }
347 
348 ///read from std::istream
put(std::istream & is)349 bool ParticleSet::put(std::istream& is) { return true; }
350 
351 ///reset member data
reset()352 void ParticleSet::reset() { app_log() << "<<<< going to set properties >>>> " << std::endl; }
353 
354 ///read the particleset
put(xmlNodePtr cur)355 bool ParticleSet::put(xmlNodePtr cur) { return true; }
356 
addTable(const ParticleSet & psrc,bool need_full_table)357 int ParticleSet::addTable(const ParticleSet& psrc, bool need_full_table)
358 {
359   if (myName == "none" || psrc.getName() == "none")
360     APP_ABORT("ParticleSet::addTable needs proper names for both source and target particle sets.");
361 
362   int tid;
363   std::map<std::string, int>::iterator tit(myDistTableMap.find(psrc.getName()));
364   if (tit == myDistTableMap.end())
365   {
366     std::ostringstream description;
367     tid = DistTables.size();
368     if (myName == psrc.getName())
369       DistTables.push_back(createDistanceTable(*this, description));
370     else
371       DistTables.push_back(createDistanceTable(psrc, *this, description));
372     distTableDescriptions.push_back(description.str());
373     myDistTableMap[psrc.getName()] = tid;
374     app_debug() << "  ... ParticleSet::addTable Create Table #" << tid << " " << DistTables[tid]->getName()
375                 << std::endl;
376   }
377   else
378   {
379     tid = (*tit).second;
380     app_debug() << "  ... ParticleSet::addTable Reuse Table #" << tid << " " << DistTables[tid]->getName() << std::endl;
381   }
382 
383   DistTables[tid]->setFullTableNeeds(DistTables[tid]->getFullTableNeeds() || need_full_table);
384 
385   app_log().flush();
386   return tid;
387 }
388 
update(bool skipSK)389 void ParticleSet::update(bool skipSK)
390 {
391   ScopedTimer update_scope(myTimers[PS_update]);
392 
393   coordinates_->setAllParticlePos(R);
394   for (int i = 0; i < DistTables.size(); i++)
395     DistTables[i]->evaluate(*this);
396   if (!skipSK && SK)
397     SK->UpdateAllPart(*this);
398 
399   activePtcl = -1;
400 }
401 
mw_update(const RefVectorWithLeader<ParticleSet> & p_list,bool skipSK)402 void ParticleSet::mw_update(const RefVectorWithLeader<ParticleSet>& p_list, bool skipSK)
403 {
404   auto& p_leader = p_list.getLeader();
405   ScopedTimer update_scope(p_leader.myTimers[PS_update]);
406 
407   for (ParticleSet& pset : p_list)
408     pset.setCoordinates(pset.R);
409 
410   auto& dts = p_leader.DistTables;
411   for (int i = 0; i < dts.size(); ++i)
412   {
413     const auto dt_list(extractDTRefList(p_list, i));
414     dts[i]->mw_evaluate(dt_list, p_list);
415   }
416 
417   if (!skipSK && p_leader.SK)
418   {
419 #pragma omp parallel for
420     for (int iw = 0; iw < p_list.size(); iw++)
421       p_list[iw].SK->UpdateAllPart(p_list[iw]);
422   }
423 }
424 
makeMove(Index_t iat,const SingleParticlePos_t & displ,bool maybe_accept)425 void ParticleSet::makeMove(Index_t iat, const SingleParticlePos_t& displ, bool maybe_accept)
426 {
427   activePtcl    = iat;
428   activePos     = R[iat] + displ;
429   activeSpinVal = spins[iat];
430   computeNewPosDistTablesAndSK(iat, activePos, maybe_accept);
431 }
432 
makeMoveWithSpin(Index_t iat,const SingleParticlePos_t & displ,const Scalar_t & sdispl)433 void ParticleSet::makeMoveWithSpin(Index_t iat, const SingleParticlePos_t& displ, const Scalar_t& sdispl)
434 {
435   makeMove(iat, displ);
436   activeSpinVal += sdispl;
437 }
438 
mw_makeMove(const RefVectorWithLeader<ParticleSet> & p_list,Index_t iat,const std::vector<SingleParticlePos_t> & displs)439 void ParticleSet::mw_makeMove(const RefVectorWithLeader<ParticleSet>& p_list,
440                               Index_t iat,
441                               const std::vector<SingleParticlePos_t>& displs)
442 {
443   std::vector<SingleParticlePos_t> new_positions;
444   new_positions.reserve(displs.size());
445 
446   for (int iw = 0; iw < p_list.size(); iw++)
447   {
448     p_list[iw].activePtcl = iat;
449     p_list[iw].activePos  = p_list[iw].R[iat] + displs[iw];
450     new_positions.push_back(p_list[iw].activePos);
451   }
452 
453   mw_computeNewPosDistTablesAndSK(p_list, iat, new_positions);
454 }
455 
makeMoveAndCheck(Index_t iat,const SingleParticlePos_t & displ)456 bool ParticleSet::makeMoveAndCheck(Index_t iat, const SingleParticlePos_t& displ)
457 {
458   activePtcl    = iat;
459   activePos     = R[iat] + displ;
460   activeSpinVal = spins[iat];
461   bool is_valid = true;
462   if (Lattice.explicitly_defined)
463   {
464     if (Lattice.outOfBound(Lattice.toUnit(displ)))
465       is_valid = false;
466     else
467     {
468       newRedPos = Lattice.toUnit(activePos);
469       if (!Lattice.isValid(newRedPos))
470         is_valid = false;
471     }
472   }
473   computeNewPosDistTablesAndSK(iat, activePos, true);
474   return is_valid;
475 }
476 
makeMoveAndCheckWithSpin(Index_t iat,const SingleParticlePos_t & displ,const Scalar_t & sdispl)477 bool ParticleSet::makeMoveAndCheckWithSpin(Index_t iat, const SingleParticlePos_t& displ, const Scalar_t& sdispl)
478 {
479   bool is_valid = makeMoveAndCheck(iat, displ);
480   activeSpinVal += sdispl;
481   return is_valid;
482 }
483 
computeNewPosDistTablesAndSK(Index_t iat,const SingleParticlePos_t & newpos,bool maybe_accept)484 void ParticleSet::computeNewPosDistTablesAndSK(Index_t iat, const SingleParticlePos_t& newpos, bool maybe_accept)
485 {
486   ScopedTimer compute_newpos_scope(myTimers[PS_newpos]);
487 
488   for (int i = 0; i < DistTables.size(); ++i)
489     DistTables[i]->move(*this, newpos, iat, maybe_accept);
490   //Do not change SK: 2007-05-18
491   //Change SK only if DoUpdate is true: 2008-09-12
492   if (SK && SK->DoUpdate)
493     SK->makeMove(iat, newpos);
494 }
495 
mw_computeNewPosDistTablesAndSK(const RefVectorWithLeader<ParticleSet> & p_list,Index_t iat,const std::vector<SingleParticlePos_t> & new_positions,bool maybe_accept)496 void ParticleSet::mw_computeNewPosDistTablesAndSK(const RefVectorWithLeader<ParticleSet>& p_list,
497                                                   Index_t iat,
498                                                   const std::vector<SingleParticlePos_t>& new_positions,
499                                                   bool maybe_accept)
500 {
501   ParticleSet& p_leader = p_list.getLeader();
502   ScopedTimer compute_newpos_scope(p_leader.myTimers[PS_newpos]);
503 
504   {
505     ScopedTimer copy_scope(p_leader.myTimers[PS_mw_copy]);
506     const auto coords_list(extractCoordsRefList(p_list));
507     p_leader.coordinates_->mw_copyActivePos(coords_list, iat, new_positions);
508   }
509 
510   {
511     ScopedTimer dt_scope(p_leader.myTimers[PS_dt_move]);
512     const int dist_tables_size = p_leader.DistTables.size();
513     for (int i = 0; i < dist_tables_size; ++i)
514     {
515       const auto dt_list(extractDTRefList(p_list, i));
516       p_leader.DistTables[i]->mw_move(dt_list, p_list, new_positions, iat, maybe_accept);
517     }
518   }
519   auto& SK = p_leader.SK;
520   if (SK && SK->DoUpdate)
521   {
522 #pragma omp parallel for
523     for (int iw = 0; iw < p_list.size(); iw++)
524       p_list[iw].SK->makeMove(iat, new_positions[iw]);
525   }
526 }
527 
528 
makeMoveAllParticles(const Walker_t & awalker,const ParticlePos_t & deltaR,RealType dt)529 bool ParticleSet::makeMoveAllParticles(const Walker_t& awalker, const ParticlePos_t& deltaR, RealType dt)
530 {
531   activePtcl = -1;
532   if (Lattice.explicitly_defined)
533   {
534     for (int iat = 0; iat < deltaR.size(); ++iat)
535     {
536       SingleParticlePos_t displ(dt * deltaR[iat]);
537       if (Lattice.outOfBound(Lattice.toUnit(displ)))
538         return false;
539       SingleParticlePos_t newpos(awalker.R[iat] + displ);
540       if (!Lattice.isValid(Lattice.toUnit(newpos)))
541         return false;
542       R[iat] = newpos;
543     }
544   }
545   else
546   {
547     for (int iat = 0; iat < deltaR.size(); ++iat)
548       R[iat] = awalker.R[iat] + dt * deltaR[iat];
549   }
550   coordinates_->setAllParticlePos(R);
551   for (int i = 0; i < DistTables.size(); i++)
552     DistTables[i]->evaluate(*this);
553   if (SK)
554     SK->UpdateAllPart(*this);
555   //every move is valid
556   return true;
557 }
558 
makeMoveAllParticles(const Walker_t & awalker,const ParticlePos_t & deltaR,const std::vector<RealType> & dt)559 bool ParticleSet::makeMoveAllParticles(const Walker_t& awalker,
560                                        const ParticlePos_t& deltaR,
561                                        const std::vector<RealType>& dt)
562 {
563   activePtcl = -1;
564   if (Lattice.explicitly_defined)
565   {
566     for (int iat = 0; iat < deltaR.size(); ++iat)
567     {
568       SingleParticlePos_t displ(dt[iat] * deltaR[iat]);
569       if (Lattice.outOfBound(Lattice.toUnit(displ)))
570         return false;
571       SingleParticlePos_t newpos(awalker.R[iat] + displ);
572       if (!Lattice.isValid(Lattice.toUnit(newpos)))
573         return false;
574       R[iat] = newpos;
575     }
576   }
577   else
578   {
579     for (int iat = 0; iat < deltaR.size(); ++iat)
580       R[iat] = awalker.R[iat] + dt[iat] * deltaR[iat];
581   }
582   coordinates_->setAllParticlePos(R);
583   for (int i = 0; i < DistTables.size(); i++)
584     DistTables[i]->evaluate(*this);
585   if (SK)
586     SK->UpdateAllPart(*this);
587   //every move is valid
588   return true;
589 }
590 
591 /** move a walker by dt*deltaR + drift
592  * @param awalker initial walker configuration
593  * @param drift drift vector
594  * @param deltaR random displacement
595  * @param dt timestep
596  * @return true, if all the particle moves are legal under the boundary conditions
597  */
makeMoveAllParticlesWithDrift(const Walker_t & awalker,const ParticlePos_t & drift,const ParticlePos_t & deltaR,RealType dt)598 bool ParticleSet::makeMoveAllParticlesWithDrift(const Walker_t& awalker,
599                                                 const ParticlePos_t& drift,
600                                                 const ParticlePos_t& deltaR,
601                                                 RealType dt)
602 {
603   activePtcl = -1;
604   if (Lattice.explicitly_defined)
605   {
606     for (int iat = 0; iat < deltaR.size(); ++iat)
607     {
608       SingleParticlePos_t displ(dt * deltaR[iat] + drift[iat]);
609       if (Lattice.outOfBound(Lattice.toUnit(displ)))
610         return false;
611       SingleParticlePos_t newpos(awalker.R[iat] + displ);
612       if (!Lattice.isValid(Lattice.toUnit(newpos)))
613         return false;
614       R[iat] = newpos;
615     }
616   }
617   else
618   {
619     for (int iat = 0; iat < deltaR.size(); ++iat)
620       R[iat] = awalker.R[iat] + dt * deltaR[iat] + drift[iat];
621   }
622   coordinates_->setAllParticlePos(R);
623   for (int i = 0; i < DistTables.size(); i++)
624     DistTables[i]->evaluate(*this);
625   if (SK)
626     SK->UpdateAllPart(*this);
627   //every move is valid
628   return true;
629 }
630 
makeMoveAllParticlesWithDrift(const Walker_t & awalker,const ParticlePos_t & drift,const ParticlePos_t & deltaR,const std::vector<RealType> & dt)631 bool ParticleSet::makeMoveAllParticlesWithDrift(const Walker_t& awalker,
632                                                 const ParticlePos_t& drift,
633                                                 const ParticlePos_t& deltaR,
634                                                 const std::vector<RealType>& dt)
635 {
636   activePtcl = -1;
637   if (Lattice.explicitly_defined)
638   {
639     for (int iat = 0; iat < deltaR.size(); ++iat)
640     {
641       SingleParticlePos_t displ(dt[iat] * deltaR[iat] + drift[iat]);
642       if (Lattice.outOfBound(Lattice.toUnit(displ)))
643         return false;
644       SingleParticlePos_t newpos(awalker.R[iat] + displ);
645       if (!Lattice.isValid(Lattice.toUnit(newpos)))
646         return false;
647       R[iat] = newpos;
648     }
649   }
650   else
651   {
652     for (int iat = 0; iat < deltaR.size(); ++iat)
653       R[iat] = awalker.R[iat] + dt[iat] * deltaR[iat] + drift[iat];
654   }
655   coordinates_->setAllParticlePos(R);
656 
657   for (int i = 0; i < DistTables.size(); i++)
658     DistTables[i]->evaluate(*this);
659   if (SK)
660     SK->UpdateAllPart(*this);
661   //every move is valid
662   return true;
663 }
664 
665 /** update the particle attribute by the proposed move
666  *
667  * When the activePtcl is equal to iat, overwrite the position and update the
668  * content of the distance tables.
669  */
acceptMove_impl(Index_t iat,bool forward_mode)670 void ParticleSet::acceptMove_impl(Index_t iat, bool forward_mode)
671 {
672   if (iat == activePtcl)
673   {
674     //Update position + distance-table
675     for (int i = 0; i < DistTables.size(); i++)
676       if (forward_mode)
677         DistTables[i]->updatePartial(iat, true);
678       else
679         DistTables[i]->update(iat);
680 
681     //Do not change SK: 2007-05-18
682     if (SK && SK->DoUpdate)
683       SK->acceptMove(iat, GroupID[iat], R[iat]);
684 
685     R[iat]     = activePos;
686     spins[iat] = activeSpinVal;
687     activePtcl = -1;
688   }
689   else
690   {
691     std::ostringstream o;
692     o << "  Illegal acceptMove " << iat << " != " << activePtcl;
693     APP_ABORT(o.str());
694   }
695 }
696 
acceptMove(Index_t iat)697 void ParticleSet::acceptMove(Index_t iat)
698 {
699   ScopedTimer update_scope(myTimers[PS_accept]);
700   coordinates_->setOneParticlePos(activePos, iat);
701   acceptMove_impl(iat, false);
702 }
703 
accept_rejectMove(Index_t iat,bool accepted,bool forward_mode)704 void ParticleSet::accept_rejectMove(Index_t iat, bool accepted, bool forward_mode)
705 {
706   if (accepted)
707   {
708     ScopedTimer update_scope(myTimers[PS_accept]);
709     coordinates_->setOneParticlePos(activePos, iat);
710     acceptMove_impl(iat, forward_mode);
711   }
712   else if (forward_mode)
713     rejectMoveForwardMode(iat);
714   else
715     rejectMove(iat);
716 }
717 
rejectMove(Index_t iat)718 void ParticleSet::rejectMove(Index_t iat)
719 {
720 #ifndef NDEBUG
721   if (iat != activePtcl)
722     throw std::runtime_error("Bug detected by rejectMove! Request electron is not active!");
723 #endif
724   activePtcl = -1;
725 }
726 
rejectMoveForwardMode(Index_t iat)727 void ParticleSet::rejectMoveForwardMode(Index_t iat)
728 {
729   assert(iat == activePtcl);
730   //Update distance-table
731   for (int i = 0; i < DistTables.size(); i++)
732     DistTables[i]->updatePartial(iat, false);
733   activePtcl = -1;
734 }
735 
mw_accept_rejectMove(const RefVectorWithLeader<ParticleSet> & p_list,Index_t iat,const std::vector<bool> & isAccepted,bool forward_mode)736 void ParticleSet::mw_accept_rejectMove(const RefVectorWithLeader<ParticleSet>& p_list,
737                                        Index_t iat,
738                                        const std::vector<bool>& isAccepted,
739                                        bool forward_mode)
740 {
741   ParticleSet& leader = p_list.getLeader();
742   ScopedTimer update_scope(leader.myTimers[PS_accept]);
743 
744   const auto coords_list(extractCoordsRefList(p_list));
745   std::vector<SingleParticlePos_t> new_positions;
746   new_positions.reserve(p_list.size());
747   for (const ParticleSet& pset : p_list)
748     new_positions.push_back(pset.activePos);
749   leader.coordinates_->mw_acceptParticlePos(coords_list, iat, new_positions, isAccepted);
750 
751 #pragma omp parallel for
752   for (int iw = 0; iw < p_list.size(); iw++)
753   {
754     if (isAccepted[iw])
755       p_list[iw].acceptMove_impl(iat, forward_mode);
756     else if (forward_mode)
757       p_list[iw].rejectMoveForwardMode(iat);
758     else
759       p_list[iw].rejectMove(iat);
760     assert(p_list[iw].R[iat] == p_list[iw].coordinates_->getAllParticlePos()[iat]);
761   }
762 }
763 
donePbyP()764 void ParticleSet::donePbyP()
765 {
766   ScopedTimer donePbyP_scope(myTimers[PS_donePbyP]);
767   coordinates_->donePbyP();
768   if (SK && !SK->DoUpdate)
769     SK->UpdateAllPart(*this);
770   activePtcl = -1;
771 }
772 
mw_donePbyP(const RefVectorWithLeader<ParticleSet> & p_list)773 void ParticleSet::mw_donePbyP(const RefVectorWithLeader<ParticleSet>& p_list)
774 {
775 // Leaving bare omp pragma here. It can potentially be improved with cleaner abstraction.
776 #pragma omp parallel for
777   for (int iw = 0; iw < p_list.size(); iw++)
778     p_list[iw].donePbyP();
779 }
780 
makeVirtualMoves(const SingleParticlePos_t & newpos)781 void ParticleSet::makeVirtualMoves(const SingleParticlePos_t& newpos)
782 {
783   activePtcl = -1;
784   activePos  = newpos;
785   for (size_t i = 0; i < DistTables.size(); ++i)
786     DistTables[i]->move(*this, newpos);
787 }
788 
loadWalker(Walker_t & awalker,bool pbyp)789 void ParticleSet::loadWalker(Walker_t& awalker, bool pbyp)
790 {
791   ScopedTimer update_scope(myTimers[PS_loadWalker]);
792   R     = awalker.R;
793   spins = awalker.spins;
794   coordinates_->setAllParticlePos(R);
795 #if !defined(SOA_MEMORY_OPTIMIZED)
796   G = awalker.G;
797   L = awalker.L;
798 #endif
799   if (pbyp)
800   {
801     // in certain cases, full tables must be ready
802     for (int i = 0; i < DistTables.size(); i++)
803       if (DistTables[i]->getFullTableNeeds())
804         DistTables[i]->evaluate(*this);
805     //computed so that other objects can use them, e.g., kSpaceJastrow
806     if (SK && SK->DoUpdate)
807       SK->UpdateAllPart(*this);
808   }
809 
810   activePtcl = -1;
811 }
812 
mw_loadWalker(const RefVectorWithLeader<ParticleSet> & p_list,const RefVector<Walker_t> & walkers,const std::vector<bool> & recompute,bool pbyp)813 void ParticleSet::mw_loadWalker(const RefVectorWithLeader<ParticleSet>& p_list,
814                                 const RefVector<Walker_t>& walkers,
815                                 const std::vector<bool>& recompute,
816                                 bool pbyp)
817 {
818   auto& p_leader = p_list.getLeader();
819   ScopedTimer load_scope(p_leader.myTimers[PS_loadWalker]);
820 
821   auto loadWalkerConfig = [](ParticleSet& pset, Walker_t& awalker) {
822     pset.R     = awalker.R;
823     pset.spins = awalker.spins;
824     pset.coordinates_->setAllParticlePos(pset.R);
825   };
826 #pragma omp parallel for
827   for (int iw = 0; iw < p_list.size(); ++iw)
828     if (recompute[iw])
829       loadWalkerConfig(p_list[iw], walkers[iw]);
830 
831   if (pbyp)
832   {
833     auto& dts = p_leader.DistTables;
834     for (int i = 0; i < dts.size(); ++i)
835     {
836       const auto dt_list(extractDTRefList(p_list, i));
837       dts[i]->mw_recompute(dt_list, p_list, recompute);
838     }
839 
840     if (p_leader.SK && p_leader.SK->DoUpdate)
841     {
842 #pragma omp parallel for
843       for (int iw = 0; iw < p_list.size(); iw++)
844         p_list[iw].SK->UpdateAllPart(p_list[iw]);
845     }
846   }
847 }
848 
saveWalker(Walker_t & awalker)849 void ParticleSet::saveWalker(Walker_t& awalker)
850 {
851   awalker.R     = R;
852   awalker.spins = spins;
853 #if !defined(SOA_MEMORY_OPTIMIZED)
854   awalker.G = G;
855   awalker.L = L;
856 #endif
857 }
858 
mw_saveWalker(const RefVectorWithLeader<ParticleSet> & psets,const RefVector<Walker_t> & walkers)859 void ParticleSet::mw_saveWalker(const RefVectorWithLeader<ParticleSet>& psets, const RefVector<Walker_t>& walkers)
860 {
861   for (int iw = 0; iw < psets.size(); ++iw)
862     psets[iw].saveWalker(walkers[iw]);
863 }
864 
865 
initPropertyList()866 void ParticleSet::initPropertyList()
867 {
868   PropertyList.clear();
869   //Need to add the default Properties according to the enumeration
870   PropertyList.add("LogPsi");
871   PropertyList.add("SignPsi");
872   PropertyList.add("UmbrellaWeight");
873   PropertyList.add("R2Accepted");
874   PropertyList.add("R2Proposed");
875   PropertyList.add("DriftScale");
876   PropertyList.add("AltEnergy");
877   PropertyList.add("LocalEnergy");
878   PropertyList.add("LocalPotential");
879 
880   // There is no point in checking this, its quickly not consistent as other objects update property list.
881   // if (PropertyList.size() != WP::NUMPROPERTIES)
882   // {
883   //   app_error() << "The number of default properties for walkers  is not consistent." << std::endl;
884   //   app_error() << "NUMPROPERTIES " << WP::NUMPROPERTIES << " size of PropertyList " << PropertyList.size() << std::endl;
885   //   APP_ABORT("ParticleSet::initPropertyList");
886   // }
887 }
888 
clearDistanceTables()889 void ParticleSet::clearDistanceTables()
890 {
891   //Physically remove the tables
892   delete_iter(DistTables.begin(), DistTables.end());
893   DistTables.clear();
894   //for(int i=0; i< DistTables.size(); i++) DistanceTable::removeTable(DistTables[i]->getName());
895   //DistTables.erase(DistTables.begin(),DistTables.end());
896 }
897 
addPropertyHistory(int leng)898 int ParticleSet::addPropertyHistory(int leng)
899 {
900   int newL                                    = PropertyHistory.size();
901   std::vector<FullPrecRealType> newVecHistory = std::vector<FullPrecRealType>(leng, 0.0);
902   PropertyHistory.push_back(newVecHistory);
903   PHindex.push_back(0);
904   return newL;
905 }
906 
907 //      void ParticleSet::resetPropertyHistory( )
908 //     {
909 //       for(int i=0;i<PropertyHistory.size();i++)
910 //       {
911 //         PHindex[i]=0;
912 //  for(int k=0;k<PropertyHistory[i].size();k++)
913 //  {
914 //    PropertyHistory[i][k]=0.0;
915 //  }
916 //       }
917 //     }
918 
919 //      void ParticleSet::addPropertyHistoryPoint(int index, RealType data)
920 //     {
921 //       PropertyHistory[index][PHindex[index]]=(data);
922 //       PHindex[index]++;
923 //       if (PHindex[index]==PropertyHistory[index].size()) PHindex[index]=0;
924 // //       PropertyHistory[index].pop_back();
925 //     }
926 
927 //      void ParticleSet::rejectedMove()
928 //     {
929 //       for(int dindex=0;dindex<PropertyHistory.size();dindex++){
930 //         int lastIndex=PHindex[dindex]-1;
931 //         if (lastIndex<0) lastIndex+=PropertyHistory[dindex].size();
932 //         PropertyHistory[dindex][PHindex[dindex]]=PropertyHistory[dindex][lastIndex];
933 //         PHindex[dindex]++;
934 //         if (PHindex[dindex]==PropertyHistory[dindex].size()) PHindex[dindex]=0;
935 // //       PropertyHistory[dindex].push_front(PropertyHistory[dindex].front());
936 // //       PropertyHistory[dindex].pop_back();
937 //       }
938 //     }
939 
940 
createResource(ResourceCollection & collection) const941 void ParticleSet::createResource(ResourceCollection& collection) const
942 {
943   coordinates_->createResource(collection);
944   for (int i = 0; i < DistTables.size(); i++)
945     DistTables[i]->createResource(collection);
946 }
947 
acquireResource(ResourceCollection & collection,const RefVectorWithLeader<ParticleSet> & p_list)948 void ParticleSet::acquireResource(ResourceCollection& collection, const RefVectorWithLeader<ParticleSet>& p_list)
949 {
950   auto& ps_leader = p_list.getLeader();
951   ps_leader.coordinates_->acquireResource(collection, extractCoordsRefList(p_list));
952   for (int i = 0; i < ps_leader.DistTables.size(); i++)
953     ps_leader.DistTables[i]->acquireResource(collection, extractDTRefList(p_list, i));
954 }
955 
releaseResource(ResourceCollection & collection,const RefVectorWithLeader<ParticleSet> & p_list)956 void ParticleSet::releaseResource(ResourceCollection& collection, const RefVectorWithLeader<ParticleSet>& p_list)
957 {
958   auto& ps_leader = p_list.getLeader();
959   ps_leader.coordinates_->releaseResource(collection, extractCoordsRefList(p_list));
960   for (int i = 0; i < ps_leader.DistTables.size(); i++)
961     ps_leader.DistTables[i]->releaseResource(collection, extractDTRefList(p_list, i));
962 }
963 
extractDTRefList(const RefVectorWithLeader<ParticleSet> & p_list,int id)964 RefVectorWithLeader<DistanceTableData> ParticleSet::extractDTRefList(const RefVectorWithLeader<ParticleSet>& p_list,
965                                                                      int id)
966 {
967   RefVectorWithLeader<DistanceTableData> dt_list(*p_list.getLeader().DistTables[id]);
968   dt_list.reserve(p_list.size());
969   for (ParticleSet& p : p_list)
970     dt_list.push_back(*p.DistTables[id]);
971   return dt_list;
972 }
973 
extractCoordsRefList(const RefVectorWithLeader<ParticleSet> & p_list)974 RefVectorWithLeader<DynamicCoordinates> ParticleSet::extractCoordsRefList(
975     const RefVectorWithLeader<ParticleSet>& p_list)
976 {
977   RefVectorWithLeader<DynamicCoordinates> coords_list(*p_list.getLeader().coordinates_);
978   coords_list.reserve(p_list.size());
979   for (ParticleSet& p : p_list)
980     coords_list.push_back(*p.coordinates_);
981   return coords_list;
982 }
983 
984 } // namespace qmcplusplus
985