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