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