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: Jordan E. Vincent, University of Illinois at Urbana-Champaign
8 //                    Miguel Morales, moralessilva2@llnl.gov, Lawrence Livermore National Laboratory
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 //                    Mark A. Berrill, berrillma@ornl.gov, Oak Ridge National Laboratory
12 //
13 // File created by: Jordan E. Vincent, University of Illinois at Urbana-Champaign
14 //////////////////////////////////////////////////////////////////////////////////////
15 
16 
17 #ifndef QMCPLUSPLUS_SPHERICAL_CARTESIAN_TENSOR_H
18 #define QMCPLUSPLUS_SPHERICAL_CARTESIAN_TENSOR_H
19 
20 #include "OhmmsPETE/Tensor.h"
21 
22 /** SphericalTensor that evaluates the Real Spherical Harmonics
23  *
24  * The template parameters
25  * - T, the value_type, e.g. double
26  * - Point_t, a vector type to provide xyz coordinate.
27  * Point_t must have the operator[] defined, e.g., TinyVector\<double,3\>.
28  *
29  * Real Spherical Harmonics Ylm\f$=r^l S_l^m(x,y,z) \f$ is stored
30  * in an array ordered as [0,-1 0 1,-2 -1 0 1 2, -Lmax,-Lmax+1,..., Lmax-1,Lmax]
31  * where Lmax is the maximum angular momentum of a center.
32  * All the data members, e.g, Ylm and pre-calculated factors,
33  * can be accessed by index(l,m) which returns the
34  * locator of the combination for l and m.
35  */
36 template<class T,
37          class Point_t,
38          class Tensor_t = qmcplusplus::Tensor<T, 3>,
39          class GGG_t    = qmcplusplus::TinyVector<Tensor_t, 3>>
40 class SphericalTensor
41 {
42 public:
43   typedef T value_type;
44   typedef Point_t pos_type;
45   typedef Tensor_t hess_type;
46   typedef GGG_t ggg_type;
47   typedef SphericalTensor<T, Point_t> This_t;
48 
49   /** constructor
50    * @param l_max maximum angular momentum
51    * @param addsign flag to determine what convention to use
52    *
53    * Evaluate all the constants and prefactors.
54    * The spherical harmonics is defined as
55    * \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]
56    * Note that the data member Ylm is a misnomer and should not be confused with "spherical harmonics"
57    * \f$Y_l^m\f$.
58    - When addsign == true, e.g., Gaussian packages
59    \f{eqnarray*}
60      S_l^m &=& (-1)^m \sqrt{2}\Re(Y_l^{|m|}), \;\;\;m > 0 \\
61      &=& Y_l^0, \;\;\;m = 0 \\
62      &=& (-1)^m \sqrt{2}\Im(Y_l^{|m|}),\;\;\;m < 0
63    \f}
64    - When addsign == false, e.g., SIESTA package,
65    \f{eqnarray*}
66      S_l^m &=& \sqrt{2}\Re(Y_l^{|m|}), \;\;\;m > 0 \\
67      &=& Y_l^0, \;\;\;m = 0 \\
68      &=&\sqrt{2}\Im(Y_l^{|m|}),\;\;\;m < 0
69    \f}
70   */
71   explicit SphericalTensor(const int l_max, bool addsign = false);
72 
73   ///makes a table of \f$ r^l S_l^m \f$ and their gradients up to Lmax.
74   void evaluate(const Point_t& p);
75 
76   ///makes a table of \f$ r^l S_l^m \f$ and their gradients up to Lmax.
77   void evaluateAll(const Point_t& p);
78 
79   ///makes a table of \f$ r^l S_l^m \f$ and their gradients up to Lmax.
80   void evaluateTest(const Point_t& p);
81 
82   ///makes a table of \f$ r^l S_l^m \f$ and their gradients and hessians up to Lmax.
83   void evaluateWithHessian(const Point_t& p);
84 
85   ///makes a table of \f$ r^l S_l^m \f$ and their gradients and hessians and third derivatives up to Lmax.
86   void evaluateThirdDerivOnly(const Point_t& p);
87 
88   ///returns the index/locator for (\f$l,m\f$) combo, \f$ l(l+1)+m \f$
index(int l,int m)89   inline int index(int l, int m) const { return (l * (l + 1)) + m; }
90 
91   ///returns the value of \f$ r^l S_l^m \f$ given \f$ (l,m) \f$
getYlm(int l,int m)92   inline value_type getYlm(int l, int m) const { return Ylm[index(l, m)]; }
93 
94   ///returns the gradient of \f$ r^l S_l^m \f$ given \f$ (l,m) \f$
getGradYlm(int l,int m)95   inline Point_t getGradYlm(int l, int m) const { return gradYlm[index(l, m)]; }
96 
97   ///returns the hessian of \f$ r^l S_l^m \f$ given \f$ (l,m) \f$
getHessYlm(int l,int m)98   inline Tensor_t getHessYlm(int l, int m) const { return hessYlm[index(l, m)]; }
99 
100   ///returns the matrix of third derivatives of \f$ r^l S_l^m \f$ given \f$ (l,m) \f$
getGGGYlm(int lm)101   inline GGG_t getGGGYlm(int lm) const { return gggYlm[lm]; }
102 
103   ///returns the value of \f$ r^l S_l^m \f$ given \f$ (l,m) \f$
getYlm(int lm)104   inline value_type getYlm(int lm) const { return Ylm[lm]; }
105 
106   ///returns the gradient of \f$ r^l S_l^m \f$ given \f$ (l,m) \f$
getGradYlm(int lm)107   inline Point_t getGradYlm(int lm) const { return gradYlm[lm]; }
108 
109   ///returns the hessian of \f$ r^l S_l^m \f$ given \f$ (l,m) \f$
getHessYlm(int lm)110   inline Tensor_t getHessYlm(int lm) const { return hessYlm[lm]; }
111 
size()112   inline int size() const { return Ylm.size(); }
113 
lmax()114   inline int lmax() const { return Lmax; }
115 
116   ///maximum angular momentum for the center
117   int Lmax;
118 
119   ///values  Ylm\f$=r^l S_l^m(x,y,z)\f$
120   std::vector<value_type> Ylm;
121   /// Normalization factors
122   std::vector<value_type> NormFactor;
123   ///pre-evaluated factor \f$1/\sqrt{(l+m)\times(l+1-m)}\f$
124   std::vector<value_type> FactorLM;
125   ///pre-evaluated factor \f$\sqrt{(2l+1)/(4\pi)}\f$
126   std::vector<value_type> FactorL;
127   ///pre-evaluated factor \f$(2l+1)/(2l-1)\f$
128   std::vector<value_type> Factor2L;
129   ///gradients gradYlm\f$=\nabla r^l S_l^m(x,y,z)\f$
130   std::vector<Point_t> gradYlm;
131   ///hessian hessYlm\f$=(\nabla X \nabla) r^l S_l^m(x,y,z)\f$
132   std::vector<hess_type> hessYlm;
133   /// mmorales: HACK HACK HACK, to avoid having to rewrite
134   /// QMCWaveFunctions/SphericalBasisSet.h
135   std::vector<value_type> laplYlm; //(always zero)
136 
137   std::vector<ggg_type> gggYlm;
138 };
139 
140 template<class T, class Point_t, class Tensor_t, class GGG_t>
SphericalTensor(const int l_max,bool addsign)141 SphericalTensor<T, Point_t, Tensor_t, GGG_t>::SphericalTensor(const int l_max, bool addsign) : Lmax(l_max)
142 {
143   int ntot            = (Lmax + 1) * (Lmax + 1);
144   const value_type pi = 4.0 * atan(1.0);
145   Ylm.resize(ntot);
146   gradYlm.resize(ntot);
147   gradYlm[0] = 0.0;
148   laplYlm.resize(ntot);
149   for (int i = 0; i < ntot; i++)
150     laplYlm[i] = 0.0;
151   hessYlm.resize(ntot);
152   gggYlm.resize(ntot);
153   hessYlm[0] = 0.0;
154   if (ntot >= 4)
155   {
156     hessYlm[1] = 0.0;
157     hessYlm[2] = 0.0;
158     hessYlm[3] = 0.0;
159   }
160   if (ntot >= 9)
161   {
162     // m=-2
163     hessYlm[4]       = 0.0;
164     hessYlm[4](1, 0) = std::sqrt(15.0 / 4.0 / pi);
165     hessYlm[4](0, 1) = hessYlm[4](1, 0);
166     // m=-1
167     hessYlm[5]       = 0.0;
168     hessYlm[5](1, 2) = std::sqrt(15.0 / 4.0 / pi);
169     hessYlm[5](2, 1) = hessYlm[5](1, 2);
170     // m=0
171     hessYlm[6]       = 0.0;
172     hessYlm[6](0, 0) = -std::sqrt(5.0 / 4.0 / pi);
173     hessYlm[6](1, 1) = hessYlm[6](0, 0);
174     hessYlm[6](2, 2) = -2.0 * hessYlm[6](0, 0);
175     // m=1
176     hessYlm[7]       = 0.0;
177     hessYlm[7](0, 2) = std::sqrt(15.0 / 4.0 / pi);
178     hessYlm[7](2, 0) = hessYlm[7](0, 2);
179     // m=2
180     hessYlm[8]       = 0.0;
181     hessYlm[8](0, 0) = std::sqrt(15.0 / 4.0 / pi);
182     hessYlm[8](1, 1) = -hessYlm[8](0, 0);
183   }
184   for (int i = 0; i < ntot; i++)
185   {
186     gggYlm[i][0] = 0.0;
187     gggYlm[i][1] = 0.0;
188     gggYlm[i][2] = 0.0;
189   }
190   NormFactor.resize(ntot, 1);
191   const value_type sqrt2 = sqrt(2.0);
192   if (addsign)
193   {
194     for (int l = 0; l <= Lmax; l++)
195     {
196       NormFactor[index(l, 0)] = 1.0;
197       for (int m = 1; m <= l; m++)
198       {
199         NormFactor[index(l, m)]  = std::pow(-1.0e0, m) * sqrt2;
200         NormFactor[index(l, -m)] = std::pow(-1.0e0, -m) * sqrt2;
201       }
202     }
203   }
204   else
205   {
206     for (int l = 0; l <= Lmax; l++)
207     {
208       for (int m = 1; m <= l; m++)
209       {
210         NormFactor[index(l, m)]  = sqrt2;
211         NormFactor[index(l, -m)] = sqrt2;
212       }
213     }
214   }
215   FactorL.resize(Lmax + 1);
216   const value_type omega = 1.0 / sqrt(16.0 * atan(1.0));
217   for (int l = 1; l <= Lmax; l++)
218     FactorL[l] = sqrt(static_cast<T>(2 * l + 1)) * omega;
219   Factor2L.resize(Lmax + 1);
220   for (int l = 1; l <= Lmax; l++)
221     Factor2L[l] = static_cast<T>(2 * l + 1) / static_cast<T>(2 * l - 1);
222   FactorLM.resize(ntot);
223   for (int l = 1; l <= Lmax; l++)
224     for (int m = 1; m <= l; m++)
225     {
226       T fac2                 = 1.0 / sqrt(static_cast<T>((l + m) * (l + 1 - m)));
227       FactorLM[index(l, m)]  = fac2;
228       FactorLM[index(l, -m)] = fac2;
229     }
230 }
231 
232 template<class T, class Point_t, class Tensor_t, class GGG_t>
evaluate(const Point_t & p)233 void SphericalTensor<T, Point_t, Tensor_t, GGG_t>::evaluate(const Point_t& p)
234 {
235   value_type x = p[0], y = p[1], z = p[2];
236   const value_type pi    = 4.0 * atan(1.0);
237   const value_type pi4   = 4.0 * pi;
238   const value_type omega = 1.0 / sqrt(pi4);
239   const value_type sqrt2 = sqrt(2.0);
240   /*  Calculate r, cos(theta), sin(theta), cos(phi), sin(phi) from input
241       coordinates. Check here the coordinate singularity at cos(theta) = +-1.
242       This also takes care of r=0 case. */
243   value_type cphi, sphi, ctheta;
244   value_type r2xy = x * x + y * y;
245   value_type r    = sqrt(r2xy + z * z);
246   if (r2xy < std::numeric_limits<T>::epsilon())
247   {
248     cphi   = 0.0;
249     sphi   = 1.0;
250     ctheta = (z < 0) ? -1.0 : 1.0;
251   }
252   else
253   {
254     ctheta          = z / r;
255     value_type rxyi = 1.0 / sqrt(r2xy);
256     cphi            = x * rxyi;
257     sphi            = y * rxyi;
258   }
259   value_type stheta = sqrt(1.0 - ctheta * ctheta);
260   /* Now to calculate the associated legendre functions P_lm from the
261      recursion relation from l=0 to Lmax. Conventions of J.D. Jackson,
262      Classical Electrodynamics are used. */
263   Ylm[0] = 1.0;
264   // calculate P_ll and P_l,l-1
265   value_type fac = 1.0;
266   int j          = -1;
267   for (int l = 1; l <= Lmax; l++)
268   {
269     j += 2;
270     fac *= -j * stheta;
271     int ll  = index(l, l);
272     int l1  = index(l, l - 1);
273     int l2  = index(l - 1, l - 1);
274     Ylm[ll] = fac;
275     Ylm[l1] = j * ctheta * Ylm[l2];
276   }
277   // Use recurence to get other plm's //
278   for (int m = 0; m < Lmax - 1; m++)
279   {
280     int j = 2 * m + 1;
281     for (int l = m + 2; l <= Lmax; l++)
282     {
283       j += 2;
284       int lm  = index(l, m);
285       int l1  = index(l - 1, m);
286       int l2  = index(l - 2, m);
287       Ylm[lm] = (ctheta * j * Ylm[l1] - (l + m - 1) * Ylm[l2]) / (l - m);
288     }
289   }
290   // Now to calculate r^l Y_lm. //
291   value_type sphim, cphim, temp;
292   Ylm[0]          = omega; //1.0/sqrt(pi4);
293   value_type rpow = 1.0;
294   for (int l = 1; l <= Lmax; l++)
295   {
296     rpow *= r;
297     //fac = rpow*sqrt(static_cast<T>(2*l+1))*omega;//rpow*sqrt((2*l+1)/pi4);
298     //FactorL[l] = sqrt(2*l+1)/sqrt(4*pi)
299     fac    = rpow * FactorL[l];
300     int l0 = index(l, 0);
301     Ylm[l0] *= fac;
302     cphim = 1.0;
303     sphim = 0.0;
304     for (int m = 1; m <= l; m++)
305     {
306       temp   = cphim * cphi - sphim * sphi;
307       sphim  = sphim * cphi + cphim * sphi;
308       cphim  = temp;
309       int lm = index(l, m);
310       fac *= FactorLM[lm];
311       temp    = fac * Ylm[lm];
312       Ylm[lm] = temp * cphim;
313       lm      = index(l, -m);
314       Ylm[lm] = temp * sphim;
315     }
316   }
317   for (int i = 0; i < Ylm.size(); i++)
318     Ylm[i] *= NormFactor[i];
319 }
320 
321 template<class T, class Point_t, class Tensor_t, class GGG_t>
evaluateAll(const Point_t & p)322 void SphericalTensor<T, Point_t, Tensor_t, GGG_t>::evaluateAll(const Point_t& p)
323 {
324   value_type x = p[0], y = p[1], z = p[2];
325   const value_type pi    = 4.0 * atan(1.0);
326   const value_type pi4   = 4.0 * pi;
327   const value_type omega = 1.0 / sqrt(pi4);
328   const value_type sqrt2 = sqrt(2.0);
329   /*  Calculate r, cos(theta), sin(theta), cos(phi), sin(phi) from input
330       coordinates. Check here the coordinate singularity at cos(theta) = +-1.
331       This also takes care of r=0 case. */
332   value_type cphi, sphi, ctheta;
333   value_type r2xy = x * x + y * y;
334   value_type r    = sqrt(r2xy + z * z);
335   if (r2xy < std::numeric_limits<T>::epsilon())
336   {
337     cphi   = 0.0;
338     sphi   = 1.0;
339     ctheta = (z < 0) ? -1.0 : 1.0;
340   }
341   else
342   {
343     ctheta          = z / r;
344     value_type rxyi = 1.0 / sqrt(r2xy);
345     cphi            = x * rxyi;
346     sphi            = y * rxyi;
347   }
348   value_type stheta = sqrt(1.0 - ctheta * ctheta);
349   /* Now to calculate the associated legendre functions P_lm from the
350      recursion relation from l=0 to Lmax. Conventions of J.D. Jackson,
351      Classical Electrodynamics are used. */
352   Ylm[0] = 1.0;
353   // calculate P_ll and P_l,l-1
354   value_type fac = 1.0;
355   int j          = -1;
356   for (int l = 1; l <= Lmax; l++)
357   {
358     j += 2;
359     fac *= -j * stheta;
360     int ll  = index(l, l);
361     int l1  = index(l, l - 1);
362     int l2  = index(l - 1, l - 1);
363     Ylm[ll] = fac;
364     Ylm[l1] = j * ctheta * Ylm[l2];
365   }
366   // Use recurence to get other plm's //
367   for (int m = 0; m < Lmax - 1; m++)
368   {
369     int j = 2 * m + 1;
370     for (int l = m + 2; l <= Lmax; l++)
371     {
372       j += 2;
373       int lm  = index(l, m);
374       int l1  = index(l - 1, m);
375       int l2  = index(l - 2, m);
376       Ylm[lm] = (ctheta * j * Ylm[l1] - (l + m - 1) * Ylm[l2]) / (l - m);
377     }
378   }
379   // Now to calculate r^l Y_lm. //
380   value_type sphim, cphim, temp;
381   Ylm[0]          = omega; //1.0/sqrt(pi4);
382   value_type rpow = 1.0;
383   for (int l = 1; l <= Lmax; l++)
384   {
385     rpow *= r;
386     //fac = rpow*sqrt(static_cast<T>(2*l+1))*omega;//rpow*sqrt((2*l+1)/pi4);
387     //FactorL[l] = sqrt(2*l+1)/sqrt(4*pi)
388     fac    = rpow * FactorL[l];
389     int l0 = index(l, 0);
390     Ylm[l0] *= fac;
391     cphim = 1.0;
392     sphim = 0.0;
393     for (int m = 1; m <= l; m++)
394     {
395       //fac2 = (l+m)*(l+1-m);
396       //fac = fac/sqrt(fac2);
397       temp   = cphim * cphi - sphim * sphi;
398       sphim  = sphim * cphi + cphim * sphi;
399       cphim  = temp;
400       int lm = index(l, m);
401       //fac2,fac use precalculated FactorLM
402       fac *= FactorLM[lm];
403       temp    = fac * Ylm[lm];
404       Ylm[lm] = temp * cphim;
405       lm      = index(l, -m);
406       Ylm[lm] = temp * sphim;
407     }
408   }
409   // Calculating Gradient now//
410   for (int l = 1; l <= Lmax; l++)
411   {
412     //value_type fac = ((value_type) (2*l+1))/(2*l-1);
413     value_type fac = Factor2L[l];
414     for (int m = -l; m <= l; m++)
415     {
416       int lm = index(l - 1, 0);
417       value_type gx, gy, gz, dpr, dpi, dmr, dmi;
418       int ma        = std::abs(m);
419       value_type cp = sqrt(fac * (l - ma - 1) * (l - ma));
420       value_type cm = sqrt(fac * (l + ma - 1) * (l + ma));
421       value_type c0 = sqrt(fac * (l - ma) * (l + ma));
422       gz            = (l > ma) ? c0 * Ylm[lm + m] : 0.0;
423       if (l > ma + 1)
424       {
425         dpr = cp * Ylm[lm + ma + 1];
426         dpi = cp * Ylm[lm - ma - 1];
427       }
428       else
429       {
430         dpr = 0.0;
431         dpi = 0.0;
432       }
433       if (l > 1)
434       {
435         switch (ma)
436         {
437         case 0:
438           dmr = -cm * Ylm[lm + 1];
439           dmi = cm * Ylm[lm - 1];
440           break;
441         case 1:
442           dmr = cm * Ylm[lm];
443           dmi = 0.0;
444           break;
445         default:
446           dmr = cm * Ylm[lm + ma - 1];
447           dmi = cm * Ylm[lm - ma + 1];
448         }
449       }
450       else
451       {
452         dmr = cm * Ylm[lm];
453         dmi = 0.0;
454         //dmr = (l==1) ? cm*Ylm[lm]:0.0;
455         //dmi = 0.0;
456       }
457       if (m < 0)
458       {
459         gx = 0.5 * (dpi - dmi);
460         gy = -0.5 * (dpr + dmr);
461       }
462       else
463       {
464         gx = 0.5 * (dpr - dmr);
465         gy = 0.5 * (dpi + dmi);
466       }
467       lm = index(l, m);
468       if (ma)
469         gradYlm[lm] = NormFactor[lm] * Point_t(gx, gy, gz);
470       else
471         gradYlm[lm] = Point_t(gx, gy, gz);
472     }
473   }
474   for (int i = 0; i < Ylm.size(); i++)
475     Ylm[i] *= NormFactor[i];
476   //for (int i=0; i<Ylm.size(); i++) gradYlm[i]*= NormFactor[i];
477 }
478 
479 
480 template<class T, class Point_t, class Tensor_t, class GGG_t>
evaluateWithHessian(const Point_t & p)481 void SphericalTensor<T, Point_t, Tensor_t, GGG_t>::evaluateWithHessian(const Point_t& p)
482 {
483   value_type x = p[0], y = p[1], z = p[2];
484   const value_type pi    = 4.0 * atan(1.0);
485   const value_type pi4   = 4.0 * pi;
486   const value_type omega = 1.0 / sqrt(pi4);
487   const value_type sqrt2 = sqrt(2.0);
488   /*  Calculate r, cos(theta), sin(theta), cos(phi), sin(phi) from input
489       coordinates. Check here the coordinate singularity at cos(theta) = +-1.
490       This also takes care of r=0 case. */
491   value_type cphi, sphi, ctheta;
492   value_type r2xy = x * x + y * y;
493   value_type r    = sqrt(r2xy + z * z);
494   if (r2xy < std::numeric_limits<T>::epsilon())
495   {
496     cphi   = 0.0;
497     sphi   = 1.0;
498     ctheta = (z < 0) ? -1.0 : 1.0;
499   }
500   else
501   {
502     ctheta          = z / r;
503     value_type rxyi = 1.0 / sqrt(r2xy);
504     cphi            = x * rxyi;
505     sphi            = y * rxyi;
506   }
507   value_type stheta = sqrt(1.0 - ctheta * ctheta);
508   /* Now to calculate the associated legendre functions P_lm from the
509      recursion relation from l=0 to Lmax. Conventions of J.D. Jackson,
510      Classical Electrodynamics are used. */
511   Ylm[0] = 1.0;
512   // calculate P_ll and P_l,l-1
513   value_type fac = 1.0;
514   int j          = -1;
515   for (int l = 1; l <= Lmax; l++)
516   {
517     j += 2;
518     fac *= -j * stheta;
519     int ll  = index(l, l);
520     int l1  = index(l, l - 1);
521     int l2  = index(l - 1, l - 1);
522     Ylm[ll] = fac;
523     Ylm[l1] = j * ctheta * Ylm[l2];
524   }
525   // Use recurence to get other plm's //
526   for (int m = 0; m < Lmax - 1; m++)
527   {
528     int j = 2 * m + 1;
529     for (int l = m + 2; l <= Lmax; l++)
530     {
531       j += 2;
532       int lm  = index(l, m);
533       int l1  = index(l - 1, m);
534       int l2  = index(l - 2, m);
535       Ylm[lm] = (ctheta * j * Ylm[l1] - (l + m - 1) * Ylm[l2]) / (l - m);
536     }
537   }
538   // Now to calculate r^l Y_lm. //
539   value_type sphim, cphim, temp;
540   Ylm[0]          = omega; //1.0/sqrt(pi4);
541   value_type rpow = 1.0;
542   for (int l = 1; l <= Lmax; l++)
543   {
544     rpow *= r;
545     //fac = rpow*sqrt(static_cast<T>(2*l+1))*omega;//rpow*sqrt((2*l+1)/pi4);
546     //FactorL[l] = sqrt(2*l+1)/sqrt(4*pi)
547     fac    = rpow * FactorL[l];
548     int l0 = index(l, 0);
549     Ylm[l0] *= fac;
550     cphim = 1.0;
551     sphim = 0.0;
552     for (int m = 1; m <= l; m++)
553     {
554       //fac2 = (l+m)*(l+1-m);
555       //fac = fac/sqrt(fac2);
556       temp   = cphim * cphi - sphim * sphi;
557       sphim  = sphim * cphi + cphim * sphi;
558       cphim  = temp;
559       int lm = index(l, m);
560       //fac2,fac use precalculated FactorLM
561       fac *= FactorLM[lm];
562       temp    = fac * Ylm[lm];
563       Ylm[lm] = temp * cphim;
564       lm      = index(l, -m);
565       Ylm[lm] = temp * sphim;
566     }
567   }
568   // Calculating Gradient now//
569   for (int l = 1; l <= Lmax; l++)
570   {
571     //value_type fac = ((value_type) (2*l+1))/(2*l-1);
572     value_type fac = Factor2L[l];
573     for (int m = -l; m <= l; m++)
574     {
575       int lm = index(l - 1, 0);
576       value_type gx, gy, gz, dpr, dpi, dmr, dmi;
577       int ma        = std::abs(m);
578       value_type cp = sqrt(fac * (l - ma - 1) * (l - ma));
579       value_type cm = sqrt(fac * (l + ma - 1) * (l + ma));
580       value_type c0 = sqrt(fac * (l - ma) * (l + ma));
581       gz            = (l > ma) ? c0 * Ylm[lm + m] : 0.0;
582       if (l > ma + 1)
583       {
584         dpr = cp * Ylm[lm + ma + 1];
585         dpi = cp * Ylm[lm - ma - 1];
586       }
587       else
588       {
589         dpr = 0.0;
590         dpi = 0.0;
591       }
592       if (l > 1)
593       {
594         switch (ma)
595         {
596         case 0:
597           dmr = -cm * Ylm[lm + 1];
598           dmi = cm * Ylm[lm - 1];
599           break;
600         case 1:
601           dmr = cm * Ylm[lm];
602           dmi = 0.0;
603           break;
604         default:
605           dmr = cm * Ylm[lm + ma - 1];
606           dmi = cm * Ylm[lm - ma + 1];
607         }
608       }
609       else
610       {
611         dmr = cm * Ylm[lm];
612         dmi = 0.0;
613         //dmr = (l==1) ? cm*Ylm[lm]:0.0;
614         //dmi = 0.0;
615       }
616       if (m < 0)
617       {
618         gx = 0.5 * (dpi - dmi);
619         gy = -0.5 * (dpr + dmr);
620       }
621       else
622       {
623         gx = 0.5 * (dpr - dmr);
624         gy = 0.5 * (dpi + dmi);
625       }
626       lm = index(l, m);
627       if (ma)
628         gradYlm[lm] = NormFactor[lm] * Point_t(gx, gy, gz);
629       else
630         gradYlm[lm] = Point_t(gx, gy, gz);
631     }
632   }
633   for (int i = 0; i < Ylm.size(); i++)
634     Ylm[i] *= NormFactor[i];
635   //for (int i=0; i<Ylm.size(); i++) gradYlm[i]*= NormFactor[i];
636 }
637 
638 // Not implemented yet, but third derivatives are zero for l<=2
639 // so ok for lmax<=2
640 template<class T, class Point_t, class Tensor_t, class GGG_t>
evaluateThirdDerivOnly(const Point_t & p)641 void SphericalTensor<T, Point_t, Tensor_t, GGG_t>::evaluateThirdDerivOnly(const Point_t& p)
642 {}
643 
644 //These template functions are slower than the recursive one above
645 template<class SCT, unsigned L>
646 struct SCTFunctor
647 {
648   typedef typename SCT::value_type value_type;
649   typedef typename SCT::pos_type pos_type;
applySCTFunctor650   static inline void apply(std::vector<value_type>& Ylm, std::vector<pos_type>& gYlm, const pos_type& p)
651   {
652     SCTFunctor<SCT, L - 1>::apply(Ylm, gYlm, p);
653   }
654 };
655 
656 template<class SCT>
657 struct SCTFunctor<SCT, 1>
658 {
659   typedef typename SCT::value_type value_type;
660   typedef typename SCT::pos_type pos_type;
661   static inline void apply(std::vector<value_type>& Ylm, std::vector<pos_type>& gYlm, const pos_type& p)
662   {
663     const value_type L1 = sqrt(3.0);
664     Ylm[1]              = L1 * p[1];
665     Ylm[2]              = L1 * p[2];
666     Ylm[3]              = L1 * p[0];
667     gYlm[1]             = pos_type(0.0, L1, 0.0);
668     gYlm[2]             = pos_type(0.0, 0.0, L1);
669     gYlm[3]             = pos_type(L1, 0.0, 0.0);
670   }
671 };
672 
673 template<class SCT>
674 struct SCTFunctor<SCT, 2>
675 {
676   typedef typename SCT::value_type value_type;
677   typedef typename SCT::pos_type pos_type;
678   static inline void apply(std::vector<value_type>& Ylm, std::vector<pos_type>& gYlm, const pos_type& p)
679   {
680     SCTFunctor<SCT, 1>::apply(Ylm, gYlm, p);
681     value_type x = p[0], y = p[1], z = p[2];
682     value_type x2 = x * x, y2 = y * y, z2 = z * z;
683     value_type xy = x * y, xz = x * z, yz = y * z;
684     const value_type L0 = sqrt(1.25);
685     const value_type L1 = sqrt(15.0);
686     const value_type L2 = sqrt(3.75);
687     Ylm[4]              = L1 * xy;
688     Ylm[5]              = L1 * yz;
689     Ylm[6]              = L0 * (2.0 * z2 - x2 - y2);
690     Ylm[7]              = L1 * xz;
691     Ylm[8]              = L2 * (x2 - y2);
692     gYlm[4]             = pos_type(L1 * y, L1 * x, 0.0);
693     gYlm[5]             = pos_type(0.0, L1 * z, L1 * y);
694     gYlm[6]             = pos_type(-2.0 * L0 * x, -2.0 * L0 * y, 4.0 * L0 * z);
695     gYlm[7]             = pos_type(L1 * z, 0.0, L1 * x);
696     gYlm[8]             = pos_type(2.0 * L2 * x, -2.0 * L2 * y, 0.0);
697   }
698 };
699 
700 template<class SCT>
701 struct SCTFunctor<SCT, 3>
702 {
703   typedef typename SCT::value_type value_type;
704   typedef typename SCT::pos_type pos_type;
705   static inline void apply(std::vector<value_type>& Ylm, std::vector<pos_type>& gYlm, const pos_type& p)
706   {
707     SCTFunctor<SCT, 2>::apply(Ylm, gYlm, p);
708   }
709 };
710 
711 
712 template<class T, class Point_t, class Tensor_t, class GGG_t>
713 void SphericalTensor<T, Point_t, Tensor_t, GGG_t>::evaluateTest(const Point_t& p)
714 {
715   const value_type pi   = 4.0 * atan(1.0);
716   const value_type norm = 1.0 / sqrt(4.0 * pi);
717   Ylm[0]                = 1.0;
718   gradYlm[0]            = 0.0;
719   switch (Lmax)
720   {
721   case (0):
722     break;
723   case (1):
724     SCTFunctor<This_t, 1>::apply(Ylm, gradYlm, p);
725     break;
726   case (2):
727     SCTFunctor<This_t, 2>::apply(Ylm, gradYlm, p);
728     break;
729     //case(3): SCTFunctor<This_t,3>::apply(Ylm,gradYlm,p); break;
730     //case(4): SCTFunctor<This_t,4>::apply(Ylm,gradYlm,p); break;
731     //case(5): SCTFunctor<This_t,5>::apply(Ylm,gradYlm,p); break;
732   defaults:
733     std::cerr << "Lmax>2 is not valid." << std::endl;
734     break;
735   }
736   for (int i = 0; i < Ylm.size(); i++)
737     Ylm[i] *= norm;
738   for (int i = 0; i < Ylm.size(); i++)
739     gradYlm[i] *= norm;
740 }
741 
742 #endif
743