DFT-EFE
 
Loading...
Searching...
No Matches
SphericalHarmonicFunctions.h
Go to the documentation of this file.
1#ifndef dftefeSphericalHarmonicFunctions_h
2#define dftefeSphericalHarmonicFunctions_h
3
4#include <vector>
5#include <iostream>
6#include <fstream>
7#include <memory>
8#include <utils/Point.h>
9#include <sstream>
10#include <utils/Spline.h>
12#include <cmath>
13
14namespace dftefe
15{
16 namespace atoms
17 {
19 {
20 public:
21 SphericalHarmonicFunctions(const bool isAssocLegendreSplineEval);
22
24
29
30 //
31 // We use the real form of spherical harmonics without the Condon-Shortley
32 // phase (i.e., the (-1)^m prefactor) (see
33 // https://en.wikipedia.org/wiki/Spherical_harmonics) NOTE: 1) The
34 // wikipedia definition has the Condon-Shortley phase.
35 // 2) The definition of the associated Legendre polynomial (P_lm) in
36 // Boost library also contains a Condon-Shortley phase.
37 // Thus, if you're using Boost library, multiply the P_lm
38 // evaluation with (-1)^m to remove the Condon-Shortley phase.
39 // Most Quantum Chemistry codes (e.g., QChem) do not include the
40 // Condon-Shortley phase. So to make it consistent, it is
41 // prefered to remove the Condon-Shortley phase, if there are any
42 // to begin with.
43 // 3) From C++17 onwards, the <cmath> has the associated Legendre
44 // polynomial (see
45 // https://en.cppreference.com/w/cpp/numeric/special_functions/assoc_legendre)
46 // Thus, if you're using C++17 or beyond, you can use the C++
47 // standard's definition of associated Legendre polynomial
48 // instead of Boost. Note that, the C++ standard does not have
49 // the Condon-Shortley phase while Boost has it. So, we do not
50 // have to do anything special to remove it while using the C++
51 // standard.
52 //
53
54 //
55 // Y_lm(theta, phi) = Clm(l,m) * Dm(m) * P_lm(l,m,cos(theta)) * Qm(m,phi),
56 // where theta = polar angle,
57 // phi = azimuthal angle
58 // P_lm is the associated Legendre polynomial of degree l and order m,
59 // Qm is the real form of exp(i*m*phi),
60 // C_lm is the normalization constant for P_lm,
61 // D_m is the normalization constant for Q_m
62 //
63
64 //
65 // For the definition of the associated Legendre polynomials i.e. P_lm and
66 // their derivatives (as used for evaluating the real form of spherical
67 // harmonics and their derivatives) refer:
68 // @article{bosch2000computation,
69 // title={On the computation of derivatives of Legendre functions},
70 // author={Bosch, W},
71 // journal={Physics and Chemistry of the Earth, Part A: Solid Earth
72 // and Geodesy}, volume={25}, number={9-11}, pages={655--659},
73 // year={2000},
74 // publisher={Elsevier}
75 // }
76 // We use the derivative definitions from the above reference because
77 // finding the derivatives on the pole (i.e., theta = 0) is tricky. This
78 // is because the azimuthal angles (phi) is undefined for a point on the
79 // pole. However, the derivative is still well defined on the pole via the
80 // L'Hospital's rule. However, one can avoid implementing tedious
81 // L'Hospital's rule on pole and use much simpler expressions given in the
82 // above reference.
83 //
84
85 // Scalar single-point evaluation — DFTEFE_HOST_DEVICE_FUNC so that it is
86 // callable from both host and device (analytical path only; spline path
87 // is handled in the batch template specialisations).
89 Plm(const int l, const int m, const double theta) const;
90
92 dPlmDTheta(const int l, const int m, const double theta) const;
93
95 d2PlmDTheta2(const int l, const int m, const double theta) const;
96
97 template <dftefe::utils::MemorySpace memorySpace>
98 void
99 Plm(size_type numPoints,
100 const int l,
101 const int m,
102 const double * theta,
103 double * out,
105
106 template <dftefe::utils::MemorySpace memorySpace>
107 void
109 const int l,
110 const int m,
111 const double * theta,
112 double * out,
114
115 template <dftefe::utils::MemorySpace memorySpace>
116 void
118 const int l,
119 const int m,
120 const double * theta,
121 double * out,
123
127
128 private:
129 std::vector<std::vector<std::shared_ptr<const utils::Spline>>>
132 };
133
134 // Host-only single-point evaluation (utils::Point overload)
135 void
137 double & r,
138 double & theta,
139 double & phi,
140 double polarAngleTolerance);
141
142 // Single-point raw-pointer overload — callable from host and device
144 convertCartesianToSpherical(const double *x,
145 double & r,
146 double & theta,
147 double & phi,
148 double polarAngleTolerance);
149
150 template <dftefe::utils::MemorySpace memorySpace>
151 void
153 size_type numPoints,
154 const double * x,
155 double * r,
156 double * theta,
157 double * phi,
158 double polarAngleTolerance,
160 double
161 Dm(const int m);
162
163 double
164 Clm(const int l, const int m);
165
167 Qm(const int m, const double phi);
168
169 template <dftefe::utils::MemorySpace memorySpace>
170 void
171 Qm(size_type numPoints,
172 const int m,
173 const double * phi,
174 double * out,
176
178 dQmDPhi(const int m, const double phi);
179
180 template <dftefe::utils::MemorySpace memorySpace>
181 void
183 const int m,
184 const double * phi,
185 double * out,
187
188 } // namespace atoms
189} // namespace dftefe
190
191//=============================================================================
192// Inline DFTEFE_HOST_DEVICE_FUNC definitions — embedded here so every
193// translation unit (CPU or GPU) gets its own inline copy.
194//=============================================================================
195
196namespace dftefe
197{
198 namespace atoms
199 {
200 namespace
201 {
202 DFTEFE_HOST_DEVICE double
203 Rlm(const int l, const int m)
204 {
205 if (m == 0)
206 return 1.0;
207 return Rlm(l, m - 1) / ((l - m + 1.0) * (l + m));
208 }
209
211 plm(int l, int absm, double cosTheta)
212 {
213 if (absm > l)
214 return 0.0;
215 double somx2 = sqrt(1.0 - cosTheta * cosTheta);
216 double cxM = 1.0;
217 double fact = 1.0;
218 for (int i = 0; i < absm; i++)
219 {
220 cxM = -cxM * fact * somx2;
221 fact = fact + 2.0;
222 }
223 double cx = cxM;
224 if (absm != l)
225 {
226 double cxMPlus1 = cosTheta * (2 * absm + 1) * cxM;
227 cx = cxMPlus1;
228 double cxPrev = cxMPlus1;
229 double cxPrevPrev = cxM;
230 for (int jj = absm + 2; jj < l + 1; jj++)
231 {
232 cx = ((2 * jj - 1) * cosTheta * cxPrev +
233 (-jj - absm + 1) * cxPrevPrev) /
234 (jj - absm);
235 cxPrevPrev = cxPrev;
236 cxPrev = cx;
237 }
238 }
239 return ((absm % 2 == 0) ? 1.0 : -1.0) * cx;
240 }
241
243 dplmDTheta(int l, int absm, double cosTheta)
244 {
245 if (absm > l)
246 return 0.0;
247 if (l == 0)
248 return 0.0;
249 if (absm == 0)
250 return -1.0 * plm(l, 1, cosTheta);
251 if (absm == l)
252 return (double)l * plm(l, l - 1, cosTheta);
253 double term1 =
254 (double)((l + absm) * (l - absm + 1)) * plm(l, absm - 1, cosTheta);
255 double term2 = plm(l, absm + 1, cosTheta);
256 return 0.5 * (term1 - term2);
257 }
258
260 d2plmDTheta2(int l, int absm, double cosTheta)
261 {
262 if (absm > l)
263 return 0.0;
264 if (l == 0)
265 return 0.0;
266 if (absm == 0)
267 return -1.0 * dplmDTheta(l, 1, cosTheta);
268 if (absm == l)
269 return (double)l * dplmDTheta(l, l - 1, cosTheta);
270 double term1 = (double)((l + absm) * (l - absm + 1)) *
271 dplmDTheta(l, absm - 1, cosTheta);
272 double term2 = dplmDTheta(l, absm + 1, cosTheta);
273 return 0.5 * (term1 - term2);
274 }
275
276 } // anonymous namespace
277
280 double & r,
281 double & theta,
282 double & phi,
283 double polarAngleTolerance)
284 {
285 double px = x[0];
286 double py = x[1];
287 double pz = x[2];
288 r = sqrt(px * px + py * py + pz * pz);
289 if (r == 0.0)
290 {
291 theta = 0.0;
292 phi = 0.0;
293 }
294 else
295 {
296 theta = acos(pz / r);
297 if (fabs(theta - 0.0) >= polarAngleTolerance &&
298 fabs(theta - M_PI) >= polarAngleTolerance)
299 phi = atan2(py, px);
300 else
301 phi = 0.0;
302 }
303 }
304
306 Qm(const int m, const double phi)
307 {
308 double v = 0.0;
309 if (m > 0)
310 v = cos((double)m * phi);
311 else if (m == 0)
312 v = 1.0;
313 else
314 v = sin((double)(-m) * phi);
315 return v;
316 }
317
319 dQmDPhi(const int m, const double phi)
320 {
321 double v;
322 if (m > 0)
323 v = -(double)m * sin((double)m * phi);
324 else if (m == 0)
325 v = 0.0;
326 else
327 v = (double)(-m) * cos((double)(-m) * phi);
328 return v;
329 }
330
333 const int m,
334 const double theta) const
335 {
336 const int absm = (m < 0) ? -m : m;
337 const double factor = (m < 0) ? pow(-1.0, m) * Rlm(l, absm) : 1.0;
338 if (absm > l)
339 return 0.0;
340 return factor * plm(l, absm, cos(theta));
341 }
342
345 const int m,
346 const double theta) const
347 {
348 const int absm = (m < 0) ? -m : m;
349 if (absm > l || l == 0)
350 return 0.0;
351 const double factor = (m < 0) ? pow(-1.0, m) * Rlm(l, absm) : 1.0;
352 return factor * dplmDTheta(l, absm, cos(theta));
353 }
354
357 const int m,
358 const double theta) const
359 {
360 const int absm = (m < 0) ? -m : m;
361 if (absm > l || l == 0)
362 return 0.0;
363 const double factor = (m < 0) ? pow(-1.0, m) * Rlm(l, absm) : 1.0;
364 return factor * d2plmDTheta2(l, absm, cos(theta));
365 }
366
367 } // namespace atoms
368} // namespace dftefe
369
370#endif // SphericalHarmonicFunctions
#define DFTEFE_HOST_DEVICE_FUNC
Definition: DeviceKernelLauncherHelpers.h:306
#define DFTEFE_HOST_DEVICE
Definition: DeviceKernelLauncherHelpers.h:301
Definition: SphericalHarmonicFunctions.h:19
bool d_isAssocLegendreSplineEval
Definition: SphericalHarmonicFunctions.h:131
DFTEFE_HOST_DEVICE_FUNC double d2PlmDTheta2(const int l, const int m, const double theta) const
Definition: SphericalHarmonicFunctions.h:356
std::vector< std::vector< std::shared_ptr< const utils::Spline > > > d_assocLegendreSpline
Definition: SphericalHarmonicFunctions.h:130
void Plm(size_type numPoints, const int l, const int m, const double *theta, double *out, utils::deviceStream_t streamId=utils::defaultStream) const
void d2PlmDTheta2(size_type numPoints, const int l, const int m, const double *theta, double *out, utils::deviceStream_t streamId=utils::defaultStream) const
DFTEFE_HOST_DEVICE_FUNC double Plm(const int l, const int m, const double theta) const
Definition: SphericalHarmonicFunctions.h:332
DFTEFE_HOST_DEVICE_FUNC double dPlmDTheta(const int l, const int m, const double theta) const
Definition: SphericalHarmonicFunctions.h:344
void dPlmDTheta(size_type numPoints, const int l, const int m, const double *theta, double *out, utils::deviceStream_t streamId=utils::defaultStream) const
Definition: PointImpl.h:13
void convertCartesianToSpherical(const utils::Point &x, double &r, double &theta, double &phi, double polarAngleTolerance)
Definition: SphericalHarmonicFunctions.cpp:251
double Dm(const int m)
Definition: SphericalHarmonicFunctions.cpp:413
DFTEFE_HOST_DEVICE_FUNC double dQmDPhi(const int m, const double phi)
Definition: SphericalHarmonicFunctions.h:319
DFTEFE_HOST_DEVICE_FUNC double Qm(const int m, const double phi)
Definition: SphericalHarmonicFunctions.h:306
double Clm(const int l, const int m)
Definition: SphericalHarmonicFunctions.cpp:444
static cudaStream_t defaultStream
Definition: DeviceTypeConfig.cu.h:62
cudaStream_t deviceStream_t
Definition: DeviceTypeConfig.cu.h:27
dealii includes
Definition: AtomFieldDataSpherical.cpp:31
std::uint64_t size_type
Definition: TypeConfig.h:9