SIRIUS 7.5.0
Electronic structure library and applications
sht.cpp
1// Copyright (c) 2013-2017 Anton Kozhevnikov, Thomas Schulthess
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without modification, are permitted provided that
5// the following conditions are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
8// following disclaimer.
9// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
10// and the following disclaimer in the documentation and/or other materials provided with the distribution.
11//
12// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
13// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
14// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
15// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
16// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
17// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
18// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
20#include "core/sht/sht.hpp"
21#include "core/math_tools.hpp"
22
23namespace sirius {
24
25namespace sht { // TODO: move most of this to special functions
26
27sddk::mdarray<double, 2>
28wigner_d_matrix(int l, double beta)
29{
30 sddk::mdarray<double, 2> d_mtrx(2 * l + 1, 2 * l + 1);
31
32 long double cos_b2 = std::cos((long double)beta / 2.0L);
33 long double sin_b2 = std::sin((long double)beta / 2.0L);
34
35 for (int m1 = -l; m1 <= l; m1++) {
36 for (int m2 = -l; m2 <= l; m2++) {
37 long double d = 0;
38 for (int j = 0; j <= std::min(l + m1, l - m2); j++) {
39 if ((l - m2 - j) >= 0 && (l + m1 - j) >= 0 && (j + m2 - m1) >= 0) {
40 long double g = (std::sqrt(factorial<long double>(l + m1)) / factorial<long double>(l - m2 - j)) *
41 (std::sqrt(factorial<long double>(l - m1)) / factorial<long double>(l + m1 - j)) *
42 (std::sqrt(factorial<long double>(l - m2)) / factorial<long double>(j + m2 - m1)) *
43 (std::sqrt(factorial<long double>(l + m2)) / factorial<long double>(j));
44 d += g * std::pow(-1, j) * std::pow(cos_b2, 2 * l + m1 - m2 - 2 * j) * std::pow(sin_b2, 2 * j + m2 - m1);
45 }
46 }
47 d_mtrx(m1 + l, m2 + l) = (double)d;
48 }
49 }
50
51 return d_mtrx;
52}
53
54template <>
55sddk::mdarray<std::complex<double>, 2>
56rotation_matrix_l<std::complex<double>>(int l, r3::vector<double> euler_angles, int proper_rotation)
57{
58 sddk::mdarray<std::complex<double>, 2> rot_mtrx(2 * l + 1, 2 * l + 1);
59
60 auto d_mtrx = wigner_d_matrix(l, euler_angles[1]);
61
62 for (int m1 = -l; m1 <= l; m1++) {
63 for (int m2 = -l; m2 <= l; m2++) {
64 rot_mtrx(m1 + l, m2 + l) = std::exp(std::complex<double>(0, -euler_angles[0] * m1 - euler_angles[2] * m2)) *
65 d_mtrx(m1 + l, m2 + l) * std::pow(proper_rotation, l);
66 }
67 }
68
69 return rot_mtrx;
70}
71
72template <>
73sddk::mdarray<double, 2>
74rotation_matrix_l<double>(int l, r3::vector<double> euler_angles, int proper_rotation)
75{
76 auto rot_mtrx_ylm = rotation_matrix_l<std::complex<double>>(l, euler_angles, proper_rotation);
77
78 sddk::mdarray<double, 2> rot_mtrx(2 * l + 1, 2 * l + 1);
79 rot_mtrx.zero();
80
81 for (int m1 = -l; m1 <= l; m1++) {
82 auto i13 = (m1 == 0) ? std::vector<int>({0}) : std::vector<int>({-m1, m1});
83
84 for (int m2 = -l; m2 <= l; m2++) {
85 auto i24 = (m2 == 0) ? std::vector<int>({0}) : std::vector<int>({-m2, m2});
86
87 for (int m3 : i13) {
88 for (int m4 : i24) {
89 rot_mtrx(m1 + l, m2 + l) += std::real(SHT::rlm_dot_ylm(l, m1, m3) *
90 rot_mtrx_ylm(m3 + l, m4 + l) *
91 SHT::ylm_dot_rlm(l, m4, m2));
92 }
93 }
94 }
95 }
96 return rot_mtrx;
97}
98
99
100// TODO: this is used in rotatin rlm spherical functions, but this is wrong.
101// the rotation must happen inside l-shells
102template <typename T>
103void
104rotation_matrix(int lmax, r3::vector<double> euler_angles, int proper_rotation,
105 sddk::mdarray<T, 2>& rotm)
106{
107 rotm.zero();
108
109 for (int l = 0; l <= lmax; l++) {
110 auto rl = rotation_matrix_l<T>(l, euler_angles, proper_rotation);
111 for (int m = 0; m < 2 * l + 1; m++) {
112 for (int mp = 0; mp < 2 * l + 1; mp++) {
113 rotm(l * l + m, l * l + mp) = rl(m, mp);
114 }
115 }
116 }
117}
118
119template
120void
121rotation_matrix<double>(int lmax, r3::vector<double> euler_angles, int proper_rotation,
122 sddk::mdarray<double, 2>& rotm);
123
124template
125void
126rotation_matrix<std::complex<double>>(int lmax, r3::vector<double> euler_angles, int proper_rotation,
127 sddk::mdarray<std::complex<double>, 2>& rotm);
128
129template <typename T>
130std::vector<sddk::mdarray<T, 2>>
131rotation_matrix(int lmax, r3::vector<double> euler_angles, int proper_rotation)
132{
133 std::vector<sddk::mdarray<T, 2>> result(lmax + 1);
134
135 for (int l = 0; l <= lmax; l++) {
136 result[l] = rotation_matrix_l<T>(l, euler_angles, proper_rotation);
137 }
138 return result;
139}
140
141template
142std::vector<sddk::mdarray<double, 2>>
143rotation_matrix<double>(int lmax, r3::vector<double> euler_angles, int proper_rotation);
144
145template
146std::vector<sddk::mdarray<std::complex<double>, 2>>
147rotation_matrix<std::complex<double>>(int lmax, r3::vector<double> euler_angles, int proper_rotation);
148
149double
150ClebschGordan(const int l, const double j, const double mj, const int spin)
151{
152 // l : orbital angular momentum
153 // m: projection of the total angular momentum $m \pm /frac12$
154 // spin: Component of the spinor, 0 up, 1 down
155
156 double CG = 0.0; // Clebsch Gordan coeeficient cf PRB 71, 115106 page 3 first column
157
158 if ((spin != 0) && (spin != 1)) {
159 RTE_THROW("Error : unknown spin direction");
160 }
161
162 const double denom = std::sqrt(1.0 / (2.0 * l + 1.0));
163
164 if (std::abs(j - l - 0.5) < 1e-8) { // check for j = l + 1/2
165 int m = static_cast<int>(mj - 0.5); // if mj is integer (2 * m), then int m = (mj-1) >> 1;
166 if (spin == 0) {
167 CG = std::sqrt(l + m + 1.0);
168 }
169 if (spin == 1) {
170 CG = std::sqrt((l - m));
171 }
172 } else {
173 if (std::abs(j - l + 0.5) < 1e-8) { // check for j = l - 1/2
174 int m = static_cast<int>(mj + 0.5);
175 if (m < (1 - l)) {
176 CG = 0.0;
177 } else {
178 if (spin == 0) {
179 CG = std::sqrt(l - m + 1);
180 }
181 if (spin == 1) {
182 CG = -std::sqrt(l + m);
183 }
184 }
185 } else {
186 std::stringstream s;
187 s << "Clebsch-Gordan coefficients do not exist for this combination of j=" << j << " and l=" << l;
188 RTE_THROW(s);
189 }
190 }
191 return (CG * denom);
192}
193
194
195// this function computes the U^sigma_{ljm mj} coefficient that
196// rotates the complex spherical harmonics to the real one for the
197// spin orbit case
198
199// mj is normally half integer from -j to j but to avoid computation
200// error it is considered as integer so mj = 2 mj
201
202std::complex<double>
203calculate_U_sigma_m(const int l, const double j, const int mj, const int mp, const int sigma)
204{
205 if ((sigma != 0) && (sigma != 1)) {
206 RTE_THROW("SphericalIndex function : unknown spin direction");
207 }
208
209 if (std::abs(j - l - 0.5) < 1e-8) {
210 // j = l + 1/2
211 // m = mj - 1/2
212
213 int m1 = (mj - 1) >> 1;
214 if (sigma == 0) { // up spin
215 if (m1 < -l) { // convention U^s_{mj,m'} = 0
216 return 0.0;
217 } else {// U^s_{mj,mp} =
218 return SHT::rlm_dot_ylm(l, m1, mp);
219 }
220 } else { // down spin
221 if ((m1 + 1) > l) {
222 return 0.0;
223 } else {
224 return SHT::rlm_dot_ylm(l, m1 + 1, mp);
225 }
226 }
227 } else {
228 if (std::abs(j - l + 0.5) < 1e-8) {
229 int m1 = (mj + 1) >> 1;
230 if (sigma == 0) {
231 return SHT::rlm_dot_ylm(l, m1 - 1, mp);
232 } else {
233 return SHT::rlm_dot_ylm(l, m1, mp);
234 }
235 } else {
236 RTE_THROW("Spherical Index function : l and j are not compatible");
237 }
238 }
239 return 0; // make compiler happy
240}
241
242} // namespace sht
243
244template<>
245void SHT::backward_transform<double>(int ld, double const *flm, int nr, int lmmax, double *ftp) const
246{
247 assert(lmmax <= lmmax_);
248 assert(ld >= lmmax);
249 la::wrap(la::lib_t::blas).gemm('T', 'N', num_points_, nr, lmmax, &la::constant<double>::one(),
250 &rlm_backward_(0, 0), lmmax_, flm, ld, &la::constant<double>::zero(), ftp, num_points_);
251}
252
253template<>
254void SHT::backward_transform<std::complex<double>>(int ld, std::complex<double> const *flm, int nr, int lmmax,
255 std::complex<double> *ftp) const
256{
257 assert(lmmax <= lmmax_);
258 assert(ld >= lmmax);
259 la::wrap(la::lib_t::blas).gemm('T', 'N', num_points_, nr, lmmax,
260 &la::constant<std::complex<double>>::one(), &ylm_backward_(0, 0), lmmax_, flm, ld,
261 &la::constant<std::complex<double>>::zero(), ftp, num_points_);
262}
263
264template<>
265void SHT::forward_transform<double>(double const *ftp, int nr, int lmmax, int ld, double *flm) const
266{
267 assert(lmmax <= lmmax_);
268 assert(ld >= lmmax);
269 la::wrap(la::lib_t::blas).gemm('T', 'N', lmmax, nr, num_points_, &la::constant<double>::one(),
270 &rlm_forward_(0, 0), num_points_, ftp, num_points_, &la::constant<double>::zero(), flm, ld);
271}
272
273template<>
274void SHT::forward_transform<std::complex<double>>(std::complex<double> const *ftp, int nr, int lmmax, int ld,
275 std::complex<double> *flm) const
276{
277 assert(lmmax <= lmmax_);
278 assert(ld >= lmmax);
279 la::wrap(la::lib_t::blas).gemm('T', 'N', lmmax, nr, num_points_, &la::constant<std::complex<double>>::one(),
280 &ylm_forward_(0, 0), num_points_, ftp, num_points_, &la::constant<std::complex<double>>::zero(), flm, ld);
281}
282
283void SHT::check() const
284{
285 double dr = 0;
286 double dy = 0;
287
288 for (int lm = 0; lm < lmmax_; lm++) {
289 for (int lm1 = 0; lm1 < lmmax_; lm1++) {
290 double t = 0;
291 std::complex<double> zt(0, 0);
292 for (int itp = 0; itp < num_points_; itp++) {
293 zt += ylm_forward_(itp, lm) * ylm_backward_(lm1, itp);
294 t += rlm_forward_(itp, lm) * rlm_backward_(lm1, itp);
295 }
296
297 if (lm == lm1) {
298 zt -= 1.0;
299 t -= 1.0;
300 }
301 dr += std::abs(t);
302 dy += std::abs(zt);
303 }
304 }
305 dr = dr / lmmax_ / lmmax_;
306 dy = dy / lmmax_ / lmmax_;
307
308 if (dr > 1e-15 || dy > 1e-15) {
309 std::stringstream s;
310 s << "spherical mesh error is too big" << std::endl
311 << " real spherical integration error " << dr << std::endl
312 << " complex spherical integration error " << dy;
313 RTE_WARNING(s.str())
314 }
315
316 std::vector<double> flm(lmmax_);
317 std::vector<double> ftp(num_points_);
318 for (int lm = 0; lm < lmmax_; lm++) {
319 std::memset(&flm[0], 0, lmmax_ * sizeof(double));
320 flm[lm] = 1.0;
321 backward_transform(lmmax_, &flm[0], 1, lmmax_, &ftp[0]);
322 forward_transform(&ftp[0], 1, lmmax_, lmmax_, &flm[0]);
323 flm[lm] -= 1.0;
324
325 double t = 0.0;
326 for (int lm1 = 0; lm1 < lmmax_; lm1++) {
327 t += std::abs(flm[lm1]);
328 }
329
330 t /= lmmax_;
331
332 if (t > 1e-15) {
333 std::stringstream s;
334 s << "test of backward / forward real SHT failed" << std::endl
335 << " total error " << t;
336 RTE_WARNING(s.str());
337 }
338 }
339}
340
341}
void check() const
Check the transformations.
Definition: sht.cpp:283
sddk::mdarray< std::complex< double >, 2 > ylm_forward_
Forward transformation from spherical coordinates to Ylm.
Definition: sht.hpp:108
int lmmax_
Maximum number of components.
Definition: sht.hpp:90
void backward_transform(int ld, T const *flm, int nr, int lmmax, T *ftp) const
Perform a backward transformation from spherical harmonics to spherical coordinates.
sddk::mdarray< double, 2 > rlm_forward_
Forward transformation from spherical coordinates to Rlm.
Definition: sht.hpp:114
void forward_transform(T const *ftp, int nr, int lmmax, int ld, T *flm) const
Perform a forward transformation from spherical coordinates to spherical harmonics.
sddk::mdarray< std::complex< double >, 2 > ylm_backward_
Backward transformation from Ylm to spherical coordinates.
Definition: sht.hpp:105
int num_points_
Number of real-space points on the sphere.
Definition: sht.hpp:93
sddk::mdarray< double, 2 > rlm_backward_
Backward transformation from Rlm to spherical coordinates.
Definition: sht.hpp:111
static std::complex< double > ylm_dot_rlm(int l, int m1, int m2)
Compute element of the transformation matrix from complex to real spherical harmonics.
Definition: sht.hpp:363
Math helper functions.
void zero(T *ptr__, size_t n__)
Zero the device memory.
Definition: acc.hpp:397
int lmax(int lmmax__)
Get maximum orbital quantum number by the maximum lm index.
Definition: specfunc.hpp:56
int lmmax(int lmax)
Maximum number of combinations for a given .
Definition: specfunc.hpp:44
int lm(int l, int m)
Get composite lm index by angular index l and azimuthal index m.
Definition: specfunc.hpp:50
Namespace of the SIRIUS library.
Definition: sirius.f90:5
auto euler_angles(r3::matrix< double > const &rot__, double tolerance__)
Compute Euler angles corresponding to the proper rotation matrix.
Definition: rotation.hpp:196
Contains declaration and particular implementation of sirius::SHT class.