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 
12 
13 #ifndef QMCPLUSPLUS_SOA_SPHERICAL_CARTESIAN_TENSOR_H
14 #define QMCPLUSPLUS_SOA_SPHERICAL_CARTESIAN_TENSOR_H
15 
16 #include "OhmmsSoA/VectorSoaContainer.h"
17 #include "OhmmsPETE/Tensor.h"
18 
19 namespace qmcplusplus
20 {
21 /** SoaSphericalTensor that evaluates the Real Spherical Harmonics
22  *
23  * The template parameters
24  * - T, the value_type, e.g. double
25  * - Point_t, a vector type to provide xyz coordinate.
26  * Point_t must have the operator[] defined, e.g., TinyVector\<double,3\>.
27  *
28  * Real Spherical Harmonics Ylm\f$=r^l S_l^m(x,y,z) \f$ is stored
29  * in an array ordered as [0,-1 0 1,-2 -1 0 1 2, -Lmax,-Lmax+1,..., Lmax-1,Lmax]
30  * where Lmax is the maximum angular momentum of a center.
31  * All the data members, e.g, Ylm and pre-calculated factors,
32  * can be accessed by index(l,m) which returns the
33  * locator of the combination for l and m.
34  */
35 template<typename T>
36 struct SoaSphericalTensor
37 {
38   ///maximum angular momentum for the center
39   int Lmax;
40   /// Normalization factors
41   aligned_vector<T> NormFactor;
42   ///pre-evaluated factor \f$1/\sqrt{(l+m)\times(l+1-m)}\f$
43   aligned_vector<T> FactorLM;
44   ///pre-evaluated factor \f$\sqrt{(2l+1)/(4\pi)}\f$
45   aligned_vector<T> FactorL;
46   ///pre-evaluated factor \f$(2l+1)/(2l-1)\f$
47   aligned_vector<T> Factor2L;
48   ///composite
49   VectorSoaContainer<T, 5> cYlm;
50 
51   explicit SoaSphericalTensor(const int l_max, bool addsign = false);
52 
53   SoaSphericalTensor(const SoaSphericalTensor& rhs) = default;
54 
55   ///compute Ylm
56   void evaluate_bare(T x, T y, T z, T* Ylm) const;
57 
58   ///compute Ylm
evaluateVSoaSphericalTensor59   inline void evaluateV(T x, T y, T z, T* Ylm) const
60   {
61     evaluate_bare(x, y, z, Ylm);
62     for (int i = 0, nl = cYlm.size(); i < nl; i++)
63       Ylm[i] *= NormFactor[i];
64   }
65 
66   ///compute Ylm
evaluateVSoaSphericalTensor67   inline void evaluateV(T x, T y, T z)
68   {
69     T* restrict Ylm = cYlm.data(0);
70     evaluate_bare(x, y, z, Ylm);
71     for (int i = 0, nl = cYlm.size(); i < nl; i++)
72       Ylm[i] *= NormFactor[i];
73   }
74 
75   ///makes a table of \f$ r^l S_l^m \f$ and their gradients up to Lmax.
76   void evaluateVGL(T x, T y, T z);
77 
78   ///makes a table of \f$ r^l S_l^m \f$ and their gradients up to Lmax.
79   void evaluateVGH(T x, T y, T z);
80 
81   ///makes a table of \f$ r^l S_l^m \f$ and their gradients up to Lmax.
82   void evaluateVGHGH(T x, T y, T z);
83 
84   ///returns the index/locator for (\f$l,m\f$) combo, \f$ l(l+1)+m \f$
indexSoaSphericalTensor85   inline int index(int l, int m) const { return (l * (l + 1)) + m; }
86 
87   /** return the starting address of the component
88    *
89    * component=0(V), 1(dx), 2(dy), 3(dz), 4(Lap)
90    */
91   inline const T* operator[](size_t component) const { return cYlm.data(component); }
92 
sizeSoaSphericalTensor93   inline size_t size() const { return cYlm.size(); }
94 
lmaxSoaSphericalTensor95   inline int lmax() const { return Lmax; }
96 };
97 
98 /** constructor
99  * @param l_max maximum angular momentum
100  * @param addsign flag to determine what convention to use
101  *
102  * Evaluate all the constants and prefactors.
103  * The spherical harmonics is defined as
104  * \f[ Y_l^m (\theta,\phi) = \sqrt{\frac{(2l+1)(l-m)!}{4\pi(l+m)!}} P_l^m(\cos\theta)e^{im\phi}\f]
105  * Note that the data member Ylm is a misnomer and should not be confused with "spherical harmonics"
106  * \f$Y_l^m\f$.
107  - When addsign == true, e.g., Gaussian packages
108  \f{eqnarray*}
109  S_l^m &=& (-1)^m \sqrt{2}\Re(Y_l^{|m|}), \;\;\;m > 0 \\
110  &=& Y_l^0, \;\;\;m = 0 \\
111  &=& (-1)^m \sqrt{2}\Im(Y_l^{|m|}),\;\;\;m < 0
112  \f}
113  - When addsign == false, e.g., SIESTA package,
114  \f{eqnarray*}
115  S_l^m &=& \sqrt{2}\Re(Y_l^{|m|}), \;\;\;m > 0 \\
116  &=& Y_l^0, \;\;\;m = 0 \\
117  &=&\sqrt{2}\Im(Y_l^{|m|}),\;\;\;m < 0
118  \f}
119  */
120 template<typename T>
SoaSphericalTensor(const int l_max,bool addsign)121 inline SoaSphericalTensor<T>::SoaSphericalTensor(const int l_max, bool addsign) : Lmax(l_max)
122 {
123   constexpr T czero(0);
124   constexpr T cone(1);
125   const int ntot = (Lmax + 1) * (Lmax + 1);
126   cYlm.resize(ntot);
127   cYlm = czero;
128   NormFactor.resize(ntot, cone);
129   const T sqrt2 = std::sqrt(2.0);
130   if (addsign)
131   {
132     for (int l = 0; l <= Lmax; l++)
133     {
134       NormFactor[index(l, 0)] = cone;
135       for (int m = 1; m <= l; m++)
136       {
137         NormFactor[index(l, m)]  = std::pow(-cone, m) * sqrt2;
138         NormFactor[index(l, -m)] = std::pow(-cone, -m) * sqrt2;
139       }
140     }
141   }
142   else
143   {
144     for (int l = 0; l <= Lmax; l++)
145     {
146       for (int m = 1; m <= l; m++)
147       {
148         NormFactor[index(l, m)]  = sqrt2;
149         NormFactor[index(l, -m)] = sqrt2;
150       }
151     }
152   }
153   FactorL.resize(Lmax + 1);
154   const T omega = 1.0 / std::sqrt(16.0 * std::atan(1.0));
155   for (int l = 1; l <= Lmax; l++)
156     FactorL[l] = std::sqrt(static_cast<T>(2 * l + 1)) * omega;
157   Factor2L.resize(Lmax + 1);
158   for (int l = 1; l <= Lmax; l++)
159     Factor2L[l] = static_cast<T>(2 * l + 1) / static_cast<T>(2 * l - 1);
160   FactorLM.resize(ntot);
161   for (int l = 1; l <= Lmax; l++)
162     for (int m = 1; m <= l; m++)
163     {
164       T fac2                 = 1.0 / std::sqrt(static_cast<T>((l + m) * (l + 1 - m)));
165       FactorLM[index(l, m)]  = fac2;
166       FactorLM[index(l, -m)] = fac2;
167     }
168 }
169 
170 template<typename T>
evaluate_bare(T x,T y,T z,T * restrict Ylm)171 inline void SoaSphericalTensor<T>::evaluate_bare(T x, T y, T z, T* restrict Ylm) const
172 {
173   constexpr T czero(0);
174   constexpr T cone(1);
175   const T pi       = 4.0 * std::atan(1.0);
176   const T omega    = 1.0 / std::sqrt(4.0 * pi);
177   constexpr T eps2 = std::numeric_limits<T>::epsilon() * std::numeric_limits<T>::epsilon();
178 
179   /*  Calculate r, cos(theta), sin(theta), cos(phi), sin(phi) from input
180       coordinates. Check here the coordinate singularity at cos(theta) = +-1.
181       This also takes care of r=0 case. */
182   T cphi, sphi, ctheta;
183   T r2xy = x * x + y * y;
184   T r    = std::sqrt(r2xy + z * z);
185   if (r2xy < eps2)
186   {
187     cphi   = czero;
188     sphi   = cone;
189     ctheta = (z < czero) ? -cone : cone;
190   }
191   else
192   {
193     ctheta = z / r;
194     //protect ctheta, when ctheta is slightly >1 or <-1
195     if (ctheta > cone)
196       ctheta = cone;
197     if (ctheta < -cone)
198       ctheta = -cone;
199     T rxyi = cone / std::sqrt(r2xy);
200     cphi   = x * rxyi;
201     sphi   = y * rxyi;
202   }
203   T stheta = std::sqrt(cone - ctheta * ctheta);
204   /* Now to calculate the associated legendre functions P_lm from the
205      recursion relation from l=0 to Lmax. Conventions of J.D. Jackson,
206      Classical Electrodynamics are used. */
207   Ylm[0] = cone;
208   // calculate P_ll and P_l,l-1
209   T fac = cone;
210   int j = -1;
211   for (int l = 1; l <= Lmax; l++)
212   {
213     j += 2;
214     fac *= -j * stheta;
215     int ll  = index(l, l);
216     int l1  = index(l, l - 1);
217     int l2  = index(l - 1, l - 1);
218     Ylm[ll] = fac;
219     Ylm[l1] = j * ctheta * Ylm[l2];
220   }
221   // Use recurence to get other plm's //
222   for (int m = 0; m < Lmax - 1; m++)
223   {
224     int j = 2 * m + 1;
225     for (int l = m + 2; l <= Lmax; l++)
226     {
227       j += 2;
228       int lm  = index(l, m);
229       int l1  = index(l - 1, m);
230       int l2  = index(l - 2, m);
231       Ylm[lm] = (ctheta * j * Ylm[l1] - (l + m - 1) * Ylm[l2]) / (l - m);
232     }
233   }
234   // Now to calculate r^l Y_lm. //
235   T sphim, cphim, temp;
236   Ylm[0] = omega; //1.0/sqrt(pi4);
237   T rpow = 1.0;
238   for (int l = 1; l <= Lmax; l++)
239   {
240     rpow *= r;
241     //fac = rpow*sqrt(static_cast<T>(2*l+1))*omega;//rpow*sqrt((2*l+1)/pi4);
242     //FactorL[l] = sqrt(2*l+1)/sqrt(4*pi)
243     fac    = rpow * FactorL[l];
244     int l0 = index(l, 0);
245     Ylm[l0] *= fac;
246     cphim = cone;
247     sphim = czero;
248     for (int m = 1; m <= l; m++)
249     {
250       temp   = cphim * cphi - sphim * sphi;
251       sphim  = sphim * cphi + cphim * sphi;
252       cphim  = temp;
253       int lm = index(l, m);
254       fac *= FactorLM[lm];
255       temp    = fac * Ylm[lm];
256       Ylm[lm] = temp * cphim;
257       lm      = index(l, -m);
258       Ylm[lm] = temp * sphim;
259     }
260   }
261   //for (int i=0; i<Ylm.size(); i++)
262   //  Ylm[i]*= NormFactor[i];
263 }
264 
265 template<typename T>
evaluateVGL(T x,T y,T z)266 inline void SoaSphericalTensor<T>::evaluateVGL(T x, T y, T z)
267 {
268   T* restrict Ylm = cYlm.data(0);
269   evaluate_bare(x, y, z, Ylm);
270 
271   constexpr T czero(0);
272   constexpr T ahalf(0.5);
273   T* restrict gYlmX = cYlm.data(1);
274   T* restrict gYlmY = cYlm.data(2);
275   T* restrict gYlmZ = cYlm.data(3);
276 
277   // Calculating Gradient now//
278   for (int l = 1; l <= Lmax; l++)
279   {
280     //T fac = ((T) (2*l+1))/(2*l-1);
281     T fac = Factor2L[l];
282     for (int m = -l; m <= l; m++)
283     {
284       int lm = index(l - 1, 0);
285       T gx, gy, gz, dpr, dpi, dmr, dmi;
286       const int ma = std::abs(m);
287       const T cp   = std::sqrt(fac * (l - ma - 1) * (l - ma));
288       const T cm   = std::sqrt(fac * (l + ma - 1) * (l + ma));
289       const T c0   = std::sqrt(fac * (l - ma) * (l + ma));
290       gz           = (l > ma) ? c0 * Ylm[lm + m] : czero;
291       if (l > ma + 1)
292       {
293         dpr = cp * Ylm[lm + ma + 1];
294         dpi = cp * Ylm[lm - ma - 1];
295       }
296       else
297       {
298         dpr = czero;
299         dpi = czero;
300       }
301       if (l > 1)
302       {
303         switch (ma)
304         {
305         case 0:
306           dmr = -cm * Ylm[lm + 1];
307           dmi = cm * Ylm[lm - 1];
308           break;
309         case 1:
310           dmr = cm * Ylm[lm];
311           dmi = czero;
312           break;
313         default:
314           dmr = cm * Ylm[lm + ma - 1];
315           dmi = cm * Ylm[lm - ma + 1];
316         }
317       }
318       else
319       {
320         dmr = cm * Ylm[lm];
321         dmi = czero;
322         //dmr = (l==1) ? cm*Ylm[lm]:0.0;
323         //dmi = 0.0;
324       }
325       if (m < 0)
326       {
327         gx = ahalf * (dpi - dmi);
328         gy = -ahalf * (dpr + dmr);
329       }
330       else
331       {
332         gx = ahalf * (dpr - dmr);
333         gy = ahalf * (dpi + dmi);
334       }
335       lm = index(l, m);
336       if (ma)
337       {
338         gYlmX[lm] = NormFactor[lm] * gx;
339         gYlmY[lm] = NormFactor[lm] * gy;
340         gYlmZ[lm] = NormFactor[lm] * gz;
341       }
342       else
343       {
344         gYlmX[lm] = gx;
345         gYlmY[lm] = gy;
346         gYlmZ[lm] = gz;
347       }
348     }
349   }
350   for (int i = 0; i < cYlm.size(); i++)
351     Ylm[i] *= NormFactor[i];
352   //for (int i=0; i<Ylm.size(); i++) gradYlm[i]*= NormFactor[i];
353 }
354 
355 template<typename T>
evaluateVGH(T x,T y,T z)356 inline void SoaSphericalTensor<T>::evaluateVGH(T x, T y, T z)
357 {
358   APP_ABORT("SoaSphericalTensor<T>::evaluateVGH(x,y,z):  Not implemented\n");
359 }
360 
361 template<typename T>
evaluateVGHGH(T x,T y,T z)362 inline void SoaSphericalTensor<T>::evaluateVGHGH(T x, T y, T z)
363 {
364   APP_ABORT("SoaSphericalTensor<T>::evaluateVGHGH(x,y,z):  Not implemented\n");
365 }
366 
367 } // namespace qmcplusplus
368 #endif
369