SIRIUS 7.5.0
Electronic structure library and applications
matching_coefficients.hpp
Go to the documentation of this file.
1// Copyright (c) 2013-2015 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/** \file matching_coefficients.hpp
21 *
22 * \brief Contains definition and partial implementation of sirius::Matching_coefficients class.
23 */
24
25#ifndef __MATCHING_COEFFICIENTS_HPP__
26#define __MATCHING_COEFFICIENTS_HPP__
27
28#include <gsl/gsl_sf_bessel.h>
30#include "core/fft/gvec.hpp"
31
32namespace sirius {
33
34/** The following matching conditions must be fulfilled:
35 * \f[
36 * \frac{\partial^j}{\partial r^j} \sum_{L \nu} A_{L \nu}^{\bf k}({\bf G})u_{\ell \nu}(r)
37 * Y_{L}(\hat {\bf r}) \bigg|_{R^{MT}} = \frac{\partial^j}{\partial r^j} \frac{4 \pi}{\sqrt \Omega}
38 * e^{i{\bf (G+k)\tau}} \sum_{L}i^{\ell} j_{\ell}(|{\bf G+k}|r) Y_{L}^{*}(\widehat {\bf G+k}) Y_{L}(\hat {\bf r})
39 * \bigg|_{R^{MT}}
40 * \f]
41 * where \f$ L = \{ \ell, m \} \f$. Dropping sum over L we arrive to the following system of linear equations:
42 * \f[ \sum_{\nu} \frac{\partial^j u_{\ell \nu}(r)}{\partial r^j} \bigg|_{R^{MT}} A_{L \nu}^{\bf k}({\bf G})
43 * = \frac{4 \pi}{\sqrt \Omega} e^{i{\bf (G+k)\tau}} i^{\ell} \frac{\partial^j j_{\ell}(|{\bf G+k}|r)}{\partial r^j}
44 * \bigg|_{R^{MT}} Y_{L}^{*}(\widehat {\bf G+k})
45 * \f]
46 * The matching coefficients are then equal to:
47 * \f[
48 * A_{L \nu}^{\bf k}({\bf G}) = \sum_{j} \bigg[ \frac{\partial^j u_{\ell \nu}(r)}{\partial r^j} \bigg|_{R^{MT}}
49 * \bigg]_{\nu j}^{-1} \frac{\partial^j j_{\ell}(|{\bf G+k}|r)}{\partial r^j} \bigg|_{R^{MT}}
50 * \frac{4 \pi}{\sqrt \Omega} i^{\ell} e^{i{\bf (G+k)\tau}} Y_{L}^{*}(\widehat {\bf G+k})
51 * \f]
52 */
53class Matching_coefficients // TODO: compute on GPU
54{
55 private:
56 /// Description of the unit cell.
58
59 /// Description of the G+k vectors.
61
62 std::vector<double> gkvec_len_;
63
64 /// Spherical harmonics Ylm(theta, phi) of the G+k vectors.
66
67 /// Precomputed values for the linear equations for matching coefficients.
69
70 /// Generate matching coefficients for a specific \f$ \ell \f$ and order.
71 /** \param [in] ngk Number of G+k vectors.
72 * \param [in] phase_factors Phase factors of G+k vectors.
73 * \param [in] iat Index of atom type.
74 * \param [in] l Orbital quantum nuber.
75 * \param [in] lm Composite l,m index.
76 * \param [in] nu Order of radial function \f$ u_{\ell \nu}(r) \f$ for which coefficients are generated.
77 * \param [in] A Inverse matrix of radial derivatives.
78 * \param [out] alm Pointer to alm coefficients.
79 */
80 template <int N, bool conjugate, typename T, typename = std::enable_if_t<!std::is_scalar<T>::value>>
81 inline void generate(int ngk, std::vector<std::complex<double>> const& phase_factors__, int iat, int l, int lm,
82 int nu, r3::matrix<double> const& A, T* alm) const
83 {
84 std::complex<double> zt;
85
86 for (int igk = 0; igk < ngk; igk++) {
87 switch (N) {
88 case 1: {
89 zt = alm_b_(0, igk, l, iat) * A(0, 0);
90 break;
91 }
92 case 2: {
93 zt = alm_b_(0, igk, l, iat) * A(nu, 0) + alm_b_(1, igk, l, iat) * A(nu, 1);
94 break;
95 }
96 case 3: {
97 zt = alm_b_(0, igk, l, iat) * A(nu, 0) + alm_b_(1, igk, l, iat) * A(nu, 1) +
98 alm_b_(2, igk, l, iat) * A(nu, 2);
99 break;
100 }
101 }
102 if (conjugate) {
103 alm[igk] = std::conj(phase_factors__[igk] * zt) * gkvec_ylm_(igk, lm);
104 } else {
105 alm[igk] = phase_factors__[igk] * zt * std::conj(gkvec_ylm_(igk, lm));
106 }
107 }
108 }
109
110 public:
111 /// Constructor
112 Matching_coefficients(Unit_cell const& unit_cell__, fft::Gvec const& gkvec__)
113 : unit_cell_(unit_cell__)
114 , gkvec_(gkvec__)
115 {
116 int lmax_apw = unit_cell__.lmax_apw();
117 int lmmax_apw = sf::lmmax(lmax_apw);
118
120 gkvec_len_.resize(gkvec_.count());
121
122 /* get length and Ylm harmonics of G+k vectors */
123 #pragma omp parallel
124 {
125 std::vector<std::complex<double>> ylm(lmmax_apw);
126
127 #pragma omp for
128 for (int i = 0; i < gkvec_.count(); i++) {
129 auto gkvec_cart = gkvec_.gkvec_cart<index_domain_t::local>(i);
130 /* get r, theta, phi */
131 auto vs = r3::spherical_coordinates(gkvec_cart);
132 gkvec_len_[i] = vs[0];
133
134 /* get spherical harmonics */
135 sf::spherical_harmonics(lmax_apw, vs[1], vs[2], &ylm[0]);
136
137 for (int lm = 0; lm < lmmax_apw; lm++) {
138 gkvec_ylm_(i, lm) = ylm[lm];
139 }
140 }
141 }
142
144 alm_b_.zero();
145
146 #pragma omp parallel
147 {
148 /* value and first two derivatives of spherical Bessel functions */
149 sddk::mdarray<double, 2> sbessel_mt(lmax_apw + 2, 3);
150
151 #pragma omp for
152 for (int igk = 0; igk < gkvec_.count(); igk++) {
153 for (int iat = 0; iat < unit_cell_.num_atom_types(); iat++) {
154 double R = unit_cell_.atom_type(iat).mt_radius();
155
156 double RGk = R * gkvec_len_[igk];
157
158 /* compute values and first and second derivatives of the spherical Bessel functions
159 at the MT boundary */
160 gsl_sf_bessel_jl_array(lmax_apw + 1, RGk, &sbessel_mt(0, 0));
161
162 /* Bessel function derivative: f_{{n}}^{{\prime}}(z)=-f_{{n+1}}(z)+(n/z)f_{{n}}(z)
163 *
164 * In[]:= FullSimplify[D[SphericalBesselJ[n,a*x],{x,1}]]
165 * Out[]= (n SphericalBesselJ[n,a x])/x-a SphericalBesselJ[1+n,a x]
166 *
167 * In[]:= FullSimplify[D[SphericalBesselJ[n,a*x],{x,2}]]
168 * Out[]= (((-1+n) n-a^2 x^2) SphericalBesselJ[n,a x]+2 a x SphericalBesselJ[1+n,a x])/x^2
169 */
170 for (int l = 0; l <= lmax_apw; l++) { // TODO: move to sbessel class
171 sbessel_mt(l, 1) = -sbessel_mt(l + 1, 0) * gkvec_len_[igk] + (l / R) * sbessel_mt(l, 0);
172 sbessel_mt(l, 2) = 2 * gkvec_len_[igk] * sbessel_mt(l + 1, 0) / R +
173 ((l - 1) * l - std::pow(RGk, 2)) * sbessel_mt(l, 0) / std::pow(R, 2);
174 }
175
176 for (int l = 0; l <= lmax_apw; l++) {
177 std::complex<double> z = std::pow(std::complex<double>(0, 1), l);
178 double f = fourpi / std::sqrt(unit_cell_.omega());
179 alm_b_(0, igk, l, iat) = z * f * sbessel_mt(l, 0);
180 alm_b_(1, igk, l, iat) = z * f * sbessel_mt(l, 1);
181 alm_b_(2, igk, l, iat) = z * f * sbessel_mt(l, 2);
182 }
183 }
184 }
185 }
186 }
187
188 /// Generate plane-wave matching coefficients for the radial solutions of a given atom.
189 /** \param [in] atom Atom, for which matching coefficients are generated.
190 \param [out] alm Array of matching coefficients with dimension indices \f$ ({\bf G+k}, \xi) \f$.
191 */
192 template <bool conjugate, typename T, typename = std::enable_if_t<!std::is_scalar<T>::value>>
193 void generate(Atom const& atom__, sddk::mdarray<T, 2>& alm__) const
194 {
195 auto& type = atom__.type();
196
197 RTE_ASSERT(type.max_aw_order() <= 3);
198
199 int iat = type.id();
200
201 std::vector<std::complex<double>> phase_factors(gkvec_.count());
202 for (int i = 0; i < gkvec_.count(); i++) {
203 double phase = twopi * dot(gkvec_.template gkvec<index_domain_t::local>(i), atom__.position());
204 phase_factors[i] = std::exp(std::complex<double>(0, phase));
205 }
206
207 const double eps{0.1};
208 std::vector<r3::matrix<double>> Al(atom__.type().lmax_apw() + 1);
209 /* create inverse matrix of radial derivatives for all values of \ell */
210 for (int l = 0; l <= atom__.type().lmax_apw(); l++) {
211 /* order of augmentation for a given orbital quantum number */
212 int num_aw = static_cast<int>(type.aw_descriptor(l).size());
214 /* create matrix of radial derivatives */
215 for (int order = 0; order < num_aw; order++) {
216 for (int dm = 0; dm < num_aw; dm++) {
217 A(dm, order) = atom__.symmetry_class().aw_surface_deriv(l, order, dm);
218 }
219 }
220
221 /* invert matrix of radial derivatives */
222 switch (num_aw) {
223 case 1: {
224 if (unit_cell_.parameters().cfg().control().verification() >= 1) {
225 if (std::abs(A(0, 0)) < eps * (1.0 / std::sqrt(unit_cell_.omega()))) {
226 std::stringstream s;
227 s << "Ill defined plane wave matching problem for atom type " << iat << ", l = " << l
228 << std::endl
229 << " radial function value at the MT boundary : " << A(0, 0);
230 RTE_WARNING(s.str());
231 }
232 }
233
234 A(0, 0) = 1.0 / A(0, 0);
235 break;
236 }
237 case 2: {
238 double det = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
239
240 if (unit_cell_.parameters().cfg().control().verification() >= 1) {
241 if (std::abs(det) < eps * (1.0 / std::sqrt(unit_cell_.omega()))) {
242 std::stringstream s;
243 s << "Ill defined plane wave matching problem for atom type " << iat << ", l = " << l
244 << std::endl
245 << " radial function value at the MT boundary : " << A(0, 0);
246 RTE_WARNING(s.str());
247 }
248 }
249
250 std::swap(A(0, 0), A(1, 1));
251 A(0, 0) /= det;
252 A(1, 1) /= det;
253 A(0, 1) = -A(0, 1) / det;
254 A(1, 0) = -A(1, 0) / det;
255 break;
256 }
257 case 3: {
258 A = inverse(A);
259 break;
260 }
261 }
262 Al[l] = A;
263 }
264
265 for (int xi = 0; xi < type.mt_aw_basis_size(); xi++) {
266 int l = type.indexb(xi).am.l();
267 int lm = type.indexb(xi).lm;
268 int nu = type.indexb(xi).order;
269
270 /* order of augmentation for a given orbital quantum number */
271 int num_aw = static_cast<int>(type.aw_descriptor(l).size());
272
273 switch (num_aw) {
274 /* APW */
275 case 1: {
276 generate<1, conjugate>(gkvec_.count(), phase_factors, iat, l, lm, nu, Al[l], &alm__(0, xi));
277 break;
278 }
279 /* LAPW */
280 case 2: {
281 generate<2, conjugate>(gkvec_.count(), phase_factors, iat, l, lm, nu, Al[l], &alm__(0, xi));
282 break;
283 }
284 /* Super LAPW */
285 case 3: {
286 generate<3, conjugate>(gkvec_.count(), phase_factors, iat, l, lm, nu, Al[l], &alm__(0, xi));
287 break;
288 }
289 default: {
290 RTE_THROW("wrong order of augmented wave");
291 }
292 }
293 }
294 }
295
296 auto const& gkvec() const
297 {
298 return gkvec_;
299 }
300};
301
302} // namespace sirius
303
304#endif // __MATCHING_COEFFICIENTS_HPP__
double aw_surface_deriv(int l__, int order__, int dm__) const
Get m-th order radial derivative of AW functions at the MT surface.
double mt_radius() const
Return muffin-tin radius.
Definition: atom_type.hpp:712
Data and methods specific to the actual atom in the unit cell.
Definition: atom.hpp:37
Atom_symmetry_class & symmetry_class()
Return reference to corresponding atom symmetry class.
Definition: atom.hpp:324
Atom_type const & type() const
Return const reference to corresponding atom type object.
Definition: atom.hpp:318
r3::vector< double > const & position() const
Return atom position in fractional coordinates.
Definition: atom.hpp:342
Unit_cell const & unit_cell_
Description of the unit cell.
sddk::mdarray< std::complex< double >, 4 > alm_b_
Precomputed values for the linear equations for matching coefficients.
void generate(Atom const &atom__, sddk::mdarray< T, 2 > &alm__) const
Generate plane-wave matching coefficients for the radial solutions of a given atom.
sddk::mdarray< std::complex< double >, 2 > gkvec_ylm_
Spherical harmonics Ylm(theta, phi) of the G+k vectors.
fft::Gvec const & gkvec_
Description of the G+k vectors.
Matching_coefficients(Unit_cell const &unit_cell__, fft::Gvec const &gkvec__)
Constructor.
void generate(int ngk, std::vector< std::complex< double > > const &phase_factors__, int iat, int l, int lm, int nu, r3::matrix< double > const &A, T *alm) const
Generate matching coefficients for a specific and order.
Representation of a unit cell.
Definition: unit_cell.hpp:43
double omega() const
Unit cell volume.
Definition: unit_cell.hpp:275
int num_atom_types() const
Number of atom types.
Definition: unit_cell.hpp:281
Atom_type & atom_type(int id__)
Return atom type instance by id.
Definition: unit_cell.hpp:288
A set of G-vectors for FFTs and G+k basis functions.
Definition: gvec.hpp:130
int count() const
Number of G-vectors for a fine-grained distribution for the current MPI rank.
Definition: gvec.hpp:506
r3::vector< double > gkvec_cart(int ig__) const
Return G+k vector in fractional coordinates.
Definition: gvec.hpp:592
Multidimensional array with the column-major (Fortran) order.
Definition: memory.hpp:660
void zero(memory_t mem__, size_t idx0__, size_t n__)
Zero n elements starting from idx0.
Definition: memory.hpp:1316
Declaration and implementation of Gvec class.
auto inverse(matrix< int > src)
Return inverse of the integer matrix.
Definition: r3.hpp:475
auto spherical_coordinates(vector< double > vc)
Transform Cartesian coordinates [x,y,z] to spherical coordinates [r,theta,phi].
Definition: r3.hpp:590
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
void spherical_harmonics(int lmax, double theta, double phi, std::complex< double > *ylm)
Optimized implementation of complex spherical harmonics.
Definition: specfunc.hpp:298
Namespace of the SIRIUS library.
Definition: sirius.f90:5
const double twopi
Definition: constants.hpp:45
const double fourpi
Definition: constants.hpp:48
auto conj(double x__)
Return complex conjugate of a number. For a real value this is the number itself.
Definition: math_tools.hpp:165
Contains definition and partial implementation of sirius::Unit_cell class.