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) 2016 Jeongnim Kim and QMCPACK developers.
6 //
7 // File developed by:
8 //
9 // File created by: Jeongnim Kim, jeongnim.kim@intel.com, Intel Corp.
10 //////////////////////////////////////////////////////////////////////////////////////
11 // -*- C++ -*-
12 #ifndef QMCPLUSPLUS_ONEBODYJASTROW_OPTIMIZED_SOA_H
13 #define QMCPLUSPLUS_ONEBODYJASTROW_OPTIMIZED_SOA_H
14 #include "Configuration.h"
15 #include "QMCWaveFunctions/WaveFunctionComponent.h"
16 #include "QMCWaveFunctions/Jastrow/DiffOneBodyJastrowOrbital.h"
17 #include "Utilities/qmc_common.h"
18 #include "CPU/SIMD/aligned_allocator.hpp"
19 #include "CPU/SIMD/algorithm.hpp"
20 #include <map>
21 #include <numeric>
22 
23 namespace qmcplusplus
24 {
25 /** @ingroup WaveFunctionComponent
26  *  @brief Specialization for one-body Jastrow function using multiple functors
27  */
28 template<class FT>
29 struct J1OrbitalSoA : public WaveFunctionComponent
30 {
31   ///alias FuncType
32   using FuncType = FT;
33   ///type of each component U, dU, d2U;
34   using valT = typename FT::real_type;
35   ///element position type
36   using posT = TinyVector<valT, OHMMS_DIM>;
37   ///use the same container
38   using DistRow  = DistanceTableData::DistRow;
39   using DisplRow = DistanceTableData::DisplRow;
40   ///table index
41   const int myTableID;
42   ///number of ions
43   int Nions;
44   ///number of electrons
45   int Nelec;
46   ///number of groups
47   int NumGroups;
48   ///reference to the sources (ions)
49   const ParticleSet& Ions;
50 
51   valT curAt;
52   valT curLap;
53   posT curGrad;
54 
55   ///\f$Vat[i] = sum_(j) u_{i,j}\f$
56   Vector<valT> Vat;
57   aligned_vector<valT> U, dU, d2U, d3U;
58   aligned_vector<valT> DistCompressed;
59   aligned_vector<int> DistIndice;
60   Vector<posT> Grad;
61   Vector<valT> Lap;
62   ///Container for \f$F[ig*NumGroups+jg]\f$
63   std::vector<FT*> F;
64 
J1OrbitalSoAJ1OrbitalSoA65   J1OrbitalSoA(const std::string& obj_name, const ParticleSet& ions, ParticleSet& els)
66       : WaveFunctionComponent("J1OrbitalSoA", obj_name), myTableID(els.addTable(ions)), Ions(ions)
67   {
68     if (myName.empty())
69       throw std::runtime_error("J1OrbitalSoA object name cannot be empty!");
70     initialize(els);
71   }
72 
73   J1OrbitalSoA(const J1OrbitalSoA& rhs) = delete;
74 
~J1OrbitalSoAJ1OrbitalSoA75   ~J1OrbitalSoA()
76   {
77     for (int i = 0; i < F.size(); ++i)
78       if (F[i] != nullptr)
79         delete F[i];
80   }
81 
82   /* initialize storage */
initializeJ1OrbitalSoA83   void initialize(const ParticleSet& els)
84   {
85     Nions     = Ions.getTotalNum();
86     NumGroups = Ions.getSpeciesSet().getTotalNum();
87     F.resize(std::max(NumGroups, 4), nullptr);
88     if (NumGroups > 1 && !Ions.IsGrouped)
89     {
90       NumGroups = 0;
91     }
92     Nelec = els.getTotalNum();
93     Vat.resize(Nelec);
94     Grad.resize(Nelec);
95     Lap.resize(Nelec);
96 
97     U.resize(Nions);
98     dU.resize(Nions);
99     d2U.resize(Nions);
100     d3U.resize(Nions);
101     DistCompressed.resize(Nions);
102     DistIndice.resize(Nions);
103   }
104 
105   void addFunc(int source_type, FT* afunc, int target_type = -1)
106   {
107     if (F[source_type] != nullptr)
108       delete F[source_type];
109     F[source_type] = afunc;
110   }
111 
recomputeJ1OrbitalSoA112   void recompute(const ParticleSet& P)
113   {
114     const DistanceTableData& d_ie(P.getDistTable(myTableID));
115     for (int iat = 0; iat < Nelec; ++iat)
116     {
117       computeU3(P, iat, d_ie.getDistRow(iat));
118       Vat[iat] = simd::accumulate_n(U.data(), Nions, valT());
119       Lap[iat] = accumulateGL(dU.data(), d2U.data(), d_ie.getDisplRow(iat), Grad[iat]);
120     }
121   }
122 
evaluateLogJ1OrbitalSoA123   LogValueType evaluateLog(const ParticleSet& P, ParticleSet::ParticleGradient_t& G, ParticleSet::ParticleLaplacian_t& L)
124   {
125     return evaluateGL(P, G, L, true);
126   }
127 
evaluateHessianJ1OrbitalSoA128   void evaluateHessian(ParticleSet& P, HessVector_t& grad_grad_psi)
129   {
130     const DistanceTableData& d_ie(P.getDistTable(myTableID));
131     valT dudr, d2udr2;
132 
133     Tensor<valT, DIM> ident;
134     grad_grad_psi = 0.0;
135     ident.diagonal(1.0);
136 
137     for (int iel = 0; iel < Nelec; ++iel)
138     {
139       const auto& dist  = d_ie.getDistRow(iel);
140       const auto& displ = d_ie.getDisplRow(iel);
141       for (int iat = 0; iat < Nions; iat++)
142       {
143         int gid    = Ions.GroupID[iat];
144         auto* func = F[gid];
145         if (func != nullptr)
146         {
147           RealType r    = dist[iat];
148           RealType rinv = 1.0 / r;
149           PosType dr    = displ[iat];
150           func->evaluate(r, dudr, d2udr2);
151           grad_grad_psi[iel] -= rinv * rinv * outerProduct(dr, dr) * (d2udr2 - dudr * rinv) + ident * dudr * rinv;
152         }
153       }
154     }
155   }
156 
ratioJ1OrbitalSoA157   PsiValueType ratio(ParticleSet& P, int iat)
158   {
159     UpdateMode = ORB_PBYP_RATIO;
160     curAt      = computeU(P.getDistTable(myTableID).getTempDists());
161     return std::exp(static_cast<PsiValueType>(Vat[iat] - curAt));
162   }
163 
evaluateRatiosJ1OrbitalSoA164   inline void evaluateRatios(const VirtualParticleSet& VP, std::vector<ValueType>& ratios)
165   {
166     for (int k = 0; k < ratios.size(); ++k)
167       ratios[k] = std::exp(Vat[VP.refPtcl] - computeU(VP.getDistTable(myTableID).getDistRow(k)));
168   }
169 
computeUJ1OrbitalSoA170   inline valT computeU(const DistRow& dist)
171   {
172     valT curVat(0);
173     if (NumGroups > 0)
174     {
175       for (int jg = 0; jg < NumGroups; ++jg)
176       {
177         if (F[jg] != nullptr)
178           curVat += F[jg]->evaluateV(-1, Ions.first(jg), Ions.last(jg), dist.data(), DistCompressed.data());
179       }
180     }
181     else
182     {
183       for (int c = 0; c < Nions; ++c)
184       {
185         int gid = Ions.GroupID[c];
186         if (F[gid] != nullptr)
187           curVat += F[gid]->evaluate(dist[c]);
188       }
189     }
190     return curVat;
191   }
192 
evaluateRatiosAlltoOneJ1OrbitalSoA193   void evaluateRatiosAlltoOne(ParticleSet& P, std::vector<ValueType>& ratios)
194   {
195     const auto& dist = P.getDistTable(myTableID).getTempDists();
196     curAt            = valT(0);
197     if (NumGroups > 0)
198     {
199       for (int jg = 0; jg < NumGroups; ++jg)
200       {
201         if (F[jg] != nullptr)
202           curAt += F[jg]->evaluateV(-1, Ions.first(jg), Ions.last(jg), dist.data(), DistCompressed.data());
203       }
204     }
205     else
206     {
207       for (int c = 0; c < Nions; ++c)
208       {
209         int gid = Ions.GroupID[c];
210         if (F[gid] != nullptr)
211           curAt += F[gid]->evaluate(dist[c]);
212       }
213     }
214 
215     for (int i = 0; i < Nelec; ++i)
216       ratios[i] = std::exp(Vat[i] - curAt);
217   }
218 
219   inline LogValueType evaluateGL(const ParticleSet& P,
220                                  ParticleSet::ParticleGradient_t& G,
221                                  ParticleSet::ParticleLaplacian_t& L,
222                                  bool fromscratch = false)
223   {
224     if (fromscratch)
225       recompute(P);
226 
227     for (size_t iat = 0; iat < Nelec; ++iat)
228       G[iat] += Grad[iat];
229     for (size_t iat = 0; iat < Nelec; ++iat)
230       L[iat] -= Lap[iat];
231     return LogValue = -simd::accumulate_n(Vat.data(), Nelec, valT());
232   }
233 
234   /** compute gradient and lap
235    * @return lap
236    */
accumulateGLJ1OrbitalSoA237   inline valT accumulateGL(const valT* restrict du, const valT* restrict d2u, const DisplRow& displ, posT& grad) const
238   {
239     valT lap(0);
240     constexpr valT lapfac = OHMMS_DIM - RealType(1);
241     //#pragma omp simd reduction(+:lap)
242     for (int jat = 0; jat < Nions; ++jat)
243       lap += d2u[jat] + lapfac * du[jat];
244     for (int idim = 0; idim < OHMMS_DIM; ++idim)
245     {
246       const valT* restrict dX = displ.data(idim);
247       valT s                  = valT();
248       //#pragma omp simd reduction(+:s)
249       for (int jat = 0; jat < Nions; ++jat)
250         s += du[jat] * dX[jat];
251       grad[idim] = s;
252     }
253     return lap;
254   }
255 
256   /** compute U, dU and d2U
257    * @param P quantum particleset
258    * @param iat the moving particle
259    * @param dist starting address of the distances of the ions wrt the iat-th particle
260    */
computeU3J1OrbitalSoA261   inline void computeU3(const ParticleSet& P, int iat, const DistRow& dist)
262   {
263     if (NumGroups > 0)
264     { //ions are grouped
265       constexpr valT czero(0);
266       std::fill_n(U.data(), Nions, czero);
267       std::fill_n(dU.data(), Nions, czero);
268       std::fill_n(d2U.data(), Nions, czero);
269 
270       for (int jg = 0; jg < NumGroups; ++jg)
271       {
272         if (F[jg] == nullptr)
273           continue;
274         F[jg]->evaluateVGL(-1, Ions.first(jg), Ions.last(jg), dist.data(), U.data(), dU.data(), d2U.data(),
275                            DistCompressed.data(), DistIndice.data());
276       }
277     }
278     else
279     {
280       for (int c = 0; c < Nions; ++c)
281       {
282         int gid = Ions.GroupID[c];
283         if (F[gid] != nullptr)
284         {
285           U[c] = F[gid]->evaluate(dist[c], dU[c], d2U[c]);
286           dU[c] /= dist[c];
287         }
288       }
289     }
290   }
291 
292   /** compute the gradient during particle-by-particle update
293    * @param P quantum particleset
294    * @param iat particle index
295    */
evalGradJ1OrbitalSoA296   GradType evalGrad(ParticleSet& P, int iat) { return GradType(Grad[iat]); }
297 
298   /** compute the gradient during particle-by-particle update
299    * @param P quantum particleset
300    * @param iat particle index
301    *
302    * Using getTempDists(). curAt, curGrad and curLap are computed.
303    */
ratioGradJ1OrbitalSoA304   PsiValueType ratioGrad(ParticleSet& P, int iat, GradType& grad_iat)
305   {
306     UpdateMode = ORB_PBYP_PARTIAL;
307 
308     computeU3(P, iat, P.getDistTable(myTableID).getTempDists());
309     curLap = accumulateGL(dU.data(), d2U.data(), P.getDistTable(myTableID).getTempDispls(), curGrad);
310     curAt  = simd::accumulate_n(U.data(), Nions, valT());
311     grad_iat += curGrad;
312     return std::exp(static_cast<PsiValueType>(Vat[iat] - curAt));
313   }
314 
315   /** Rejected move. Nothing to do */
restoreJ1OrbitalSoA316   inline void restore(int iat) {}
317 
318   /** Accpted move. Update Vat[iat],Grad[iat] and Lap[iat] */
319   void acceptMove(ParticleSet& P, int iat, bool safe_to_delay = false)
320   {
321     if (UpdateMode == ORB_PBYP_RATIO)
322     {
323       computeU3(P, iat, P.getDistTable(myTableID).getTempDists());
324       curLap = accumulateGL(dU.data(), d2U.data(), P.getDistTable(myTableID).getTempDispls(), curGrad);
325     }
326 
327     LogValue += Vat[iat] - curAt;
328     Vat[iat]  = curAt;
329     Grad[iat] = curGrad;
330     Lap[iat]  = curLap;
331   }
332 
333 
registerDataJ1OrbitalSoA334   inline void registerData(ParticleSet& P, WFBufferType& buf)
335   {
336     if (Bytes_in_WFBuffer == 0)
337     {
338       Bytes_in_WFBuffer = buf.current();
339       buf.add(Vat.begin(), Vat.end());
340       buf.add(Grad.begin(), Grad.end());
341       buf.add(Lap.begin(), Lap.end());
342       Bytes_in_WFBuffer = buf.current() - Bytes_in_WFBuffer;
343       // free local space
344       Vat.free();
345       Grad.free();
346       Lap.free();
347     }
348     else
349     {
350       buf.forward(Bytes_in_WFBuffer);
351     }
352   }
353 
354   inline LogValueType updateBuffer(ParticleSet& P, WFBufferType& buf, bool fromscratch = false)
355   {
356     evaluateGL(P, P.G, P.L, false);
357     buf.forward(Bytes_in_WFBuffer);
358     return LogValue;
359   }
360 
copyFromBufferJ1OrbitalSoA361   inline void copyFromBuffer(ParticleSet& P, WFBufferType& buf)
362   {
363     Vat.attachReference(buf.lendReference<valT>(Nelec), Nelec);
364     Grad.attachReference(buf.lendReference<posT>(Nelec), Nelec);
365     Lap.attachReference(buf.lendReference<valT>(Nelec), Nelec);
366   }
367 
makeCloneJ1OrbitalSoA368   WaveFunctionComponentPtr makeClone(ParticleSet& tqp) const
369   {
370     J1OrbitalSoA<FT>* j1copy = new J1OrbitalSoA<FT>(myName, Ions, tqp);
371     j1copy->Optimizable      = Optimizable;
372     for (size_t i = 0, n = F.size(); i < n; ++i)
373     {
374       if (F[i] != nullptr)
375         j1copy->addFunc(i, new FT(*F[i]));
376     }
377     if (dPsi)
378     {
379       j1copy->dPsi = dPsi->makeClone(tqp);
380     }
381     return j1copy;
382   }
383 
384   /**@{ WaveFunctionComponent virtual functions that are not essential for the development */
reportStatusJ1OrbitalSoA385   void reportStatus(std::ostream& os)
386   {
387     for (size_t i = 0, n = F.size(); i < n; ++i)
388     {
389       if (F[i] != nullptr)
390         F[i]->myVars.print(os);
391     }
392   }
393 
checkInVariablesJ1OrbitalSoA394   void checkInVariables(opt_variables_type& active)
395   {
396     myVars.clear();
397     for (size_t i = 0, n = F.size(); i < n; ++i)
398     {
399       if (F[i] != nullptr)
400       {
401         F[i]->checkInVariables(active);
402         F[i]->checkInVariables(myVars);
403       }
404     }
405   }
checkOutVariablesJ1OrbitalSoA406   void checkOutVariables(const opt_variables_type& active)
407   {
408     myVars.getIndex(active);
409     Optimizable = myVars.is_optimizable();
410     for (size_t i = 0, n = F.size(); i < n; ++i)
411       if (F[i] != nullptr)
412         F[i]->checkOutVariables(active);
413     if (dPsi)
414       dPsi->checkOutVariables(active);
415   }
416 
resetParametersJ1OrbitalSoA417   void resetParameters(const opt_variables_type& active)
418   {
419     if (!Optimizable)
420       return;
421     for (size_t i = 0, n = F.size(); i < n; ++i)
422       if (F[i] != nullptr)
423         F[i]->resetParameters(active);
424 
425     for (int i = 0; i < myVars.size(); ++i)
426     {
427       int ii = myVars.Index[i];
428       if (ii >= 0)
429         myVars[i] = active[ii];
430     }
431     if (dPsi)
432       dPsi->resetParameters(active);
433   }
434   /**@} */
435 
evalGradSourceJ1OrbitalSoA436   inline GradType evalGradSource(ParticleSet& P, ParticleSet& source, int isrc)
437   {
438     GradType g_return(0.0);
439     const DistanceTableData& d_ie(P.getDistTable(myTableID));
440     for (int iat = 0; iat < Nelec; ++iat)
441     {
442       const auto& dist  = d_ie.getDistRow(iat);
443       const auto& displ = d_ie.getDisplRow(iat);
444       int gid           = source.GroupID[isrc];
445       RealType r        = dist[isrc];
446       RealType rinv     = 1.0 / r;
447       PosType dr        = displ[isrc];
448 
449       if (F[gid] != nullptr)
450       {
451         U[isrc] = F[gid]->evaluate(dist[isrc], dU[isrc], d2U[isrc], d3U[isrc]);
452         g_return -= dU[isrc] * rinv * dr;
453       }
454     }
455     return g_return;
456   }
457 
evalGradSourceJ1OrbitalSoA458   inline GradType evalGradSource(ParticleSet& P,
459                                  ParticleSet& source,
460                                  int isrc,
461                                  TinyVector<ParticleSet::ParticleGradient_t, OHMMS_DIM>& grad_grad,
462                                  TinyVector<ParticleSet::ParticleLaplacian_t, OHMMS_DIM>& lapl_grad)
463   {
464     GradType g_return(0.0);
465     const DistanceTableData& d_ie(P.getDistTable(myTableID));
466     for (int iat = 0; iat < Nelec; ++iat)
467     {
468       const auto& dist  = d_ie.getDistRow(iat);
469       const auto& displ = d_ie.getDisplRow(iat);
470       int gid           = source.GroupID[isrc];
471       RealType r        = dist[isrc];
472       RealType rinv     = 1.0 / r;
473       PosType dr        = displ[isrc];
474 
475       if (F[gid] != nullptr)
476       {
477         U[isrc] = F[gid]->evaluate(dist[isrc], dU[isrc], d2U[isrc], d3U[isrc]);
478       }
479       else
480       {
481         APP_ABORT("J1OrbitalSoa::evaluateGradSource:  F[gid]==nullptr")
482       }
483 
484       g_return -= dU[isrc] * rinv * dr;
485 
486       //The following terms depend only on the radial component r.  Thus,
487       //we compute them and mix with position vectors to acquire the full
488       //cartesian vector objects.
489       valT grad_component = (d2U[isrc] - dU[isrc] * rinv);
490       valT lapl_component = d3U[isrc] + 2 * rinv * grad_component;
491 
492       for (int idim = 0; idim < OHMMS_DIM; idim++)
493       {
494         grad_grad[idim][iat] += dr[idim] * dr * rinv * rinv * grad_component;
495         grad_grad[idim][iat][idim] += rinv * dU[isrc];
496 
497         lapl_grad[idim][iat] -= lapl_component * rinv * dr[idim];
498       }
499     }
500     return g_return;
501   }
502 };
503 
504 
505 } // namespace qmcplusplus
506 #endif
507