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