SIRIUS 7.5.0
Electronic structure library and applications
radial_integrals.cpp
Go to the documentation of this file.
1// Copyright (c) 2013-2019 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 radial_integrals.cpp
21 *
22 * \brief Implementation of various radial integrals.
23 */
24
25#include "radial_integrals.hpp"
26
27namespace sirius {
28
29template <bool jl_deriv>
30void Radial_integrals_atomic_wf<jl_deriv>::generate(std::function<Spline<double> const&(int, int)> fl__)
31{
32 PROFILE("sirius::Radial_integrals|atomic_wfs");
33
34 /* spherical Bessel functions jl(qx) */
36
37 for (int iat = 0; iat < unit_cell_.num_atom_types(); iat++) {
38
39 auto& atom_type = unit_cell_.atom_type(iat);
40
41 int nwf = indexr_(iat).size();
42 if (!nwf) {
43 continue;
44 }
45
46 /* create jl(qx) */
47 #pragma omp parallel for
48 for (int iq = 0; iq < nq(); iq++) {
49 jl(iq) = sf::Spherical_Bessel_functions(indexr_(iat).lmax(), atom_type.radial_grid(), grid_q_[iq]);
50 }
51
52 /* loop over all pseudo wave-functions */
53 for (int i = 0; i < nwf; i++) {
54 values_(i, iat) = Spline<double>(grid_q_);
55
56 int l = indexr_(iat).am(rf_index(i)).l();
57 auto& rwf = fl__(iat, i);
58
59 #pragma omp parallel for
60 for (int iq = 0; iq < nq(); iq++) {
61 if (jl_deriv) {
62 auto s = jl(iq).deriv_q(l);
63 values_(i, iat)(iq) = sirius::inner(s, rwf, 1);
64 } else {
65 values_(i, iat)(iq) = sirius::inner(jl(iq)[l], rwf, 1);
66 }
67 }
68
69 values_(i, iat).interpolate();
70 }
71 }
72}
73
74template<bool jl_deriv>
76{
77 PROFILE("sirius::Radial_integrals|aug");
78
79 /* interpolate <j_{l_n}(q*x) | Q_{xi,xi'}^{l}(x) > with splines */
80 for (int iat = 0; iat < unit_cell_.num_atom_types(); iat++) {
81 auto& atom_type = unit_cell_.atom_type(iat);
82
83 if (!atom_type.augment()) {
84 continue;
85 }
86
87 /* number of radial beta-functions */
88 int nbrf = atom_type.mt_radial_basis_size();
89 /* maximum l of beta-projectors */
90 int lmax_beta = atom_type.indexr().lmax();
91
92 for (int l = 0; l <= 2 * lmax_beta; l++) {
93 for (int idx = 0; idx < nbrf * (nbrf + 1) / 2; idx++) {
94 values_(idx, l, iat) = Spline<double>(grid_q_);
95 }
96 }
97
98 #pragma omp parallel for
99 for (auto it : spl_q_) {
100 int iq = it.i;
101
102 sf::Spherical_Bessel_functions jl(2 * lmax_beta, atom_type.radial_grid(), grid_q_[iq]);
103
104 for (int l3 = 0; l3 <= 2 * lmax_beta; l3++) {
105 for (int idxrf2 = 0; idxrf2 < nbrf; idxrf2++) {
106 int l2 = atom_type.indexr(idxrf2).am.l();
107 for (int idxrf1 = 0; idxrf1 <= idxrf2; idxrf1++) {
108 int l1 = atom_type.indexr(idxrf1).am.l();
109
110 int idx = idxrf2 * (idxrf2 + 1) / 2 + idxrf1;
111
112 if (l3 >= std::abs(l1 - l2) && l3 <= (l1 + l2) && (l1 + l2 + l3) % 2 == 0) {
113 if (jl_deriv) {
114 auto s = jl.deriv_q(l3);
115 values_(idx, l3, iat)(iq) =
116 sirius::inner(s, atom_type.q_radial_function(idxrf1, idxrf2, l3), 0);
117 } else {
118 values_(idx, l3, iat)(iq) =
119 sirius::inner(jl[l3], atom_type.q_radial_function(idxrf1, idxrf2, l3), 0);
120 }
121 }
122 }
123 }
124 }
125 }
126 for (int l = 0; l <= 2 * lmax_beta; l++) {
127 for (int idx = 0; idx < nbrf * (nbrf + 1) / 2; idx++) {
128 unit_cell_.comm().allgather(&values_(idx, l, iat)(0), spl_q_.local_size(), spl_q_.global_offset());
129 }
130 }
131
132 #pragma omp parallel for
133 for (int l = 0; l <= 2 * lmax_beta; l++) {
134 for (int idx = 0; idx < nbrf * (nbrf + 1) / 2; idx++) {
135 values_(idx, l, iat).interpolate();
136 }
137 }
138 }
139}
140
141void Radial_integrals_rho_pseudo::generate()
142{
143 PROFILE("sirius::Radial_integrals|rho_pseudo");
144
145 for (int iat = 0; iat < unit_cell_.num_atom_types(); iat++) {
146 auto& atom_type = unit_cell_.atom_type(iat);
147
148 if (atom_type.ps_total_charge_density().empty()) {
149 continue;
150 }
151
152 values_(iat) = Spline<double>(grid_q_);
153
154 Spline<double> rho(atom_type.radial_grid(), atom_type.ps_total_charge_density());
155
156 #pragma omp parallel for
157 for (auto it : spl_q_) {
158 int iq = it.i;
159 sf::Spherical_Bessel_functions jl(0, atom_type.radial_grid(), grid_q_[iq]);
160
161 values_(iat)(iq) = sirius::inner(jl[0], rho, 0, atom_type.num_mt_points()) / fourpi;
162 }
163 unit_cell_.comm().allgather(&values_(iat)(0), spl_q_.local_size(), spl_q_.global_offset());
164 values_(iat).interpolate();
165 }
166}
167
168template<bool jl_deriv>
169void Radial_integrals_rho_core_pseudo<jl_deriv>::generate()
170{
171 PROFILE("sirius::Radial_integrals|rho_core_pseudo");
172
173 for (int iat = 0; iat < unit_cell_.num_atom_types(); iat++) {
174 auto& atom_type = unit_cell_.atom_type(iat);
175
176 if (atom_type.ps_core_charge_density().empty()) {
177 continue;
178 }
179
180 values_(iat) = Spline<double>(grid_q_);
181
182 Spline<double> ps_core(atom_type.radial_grid(), atom_type.ps_core_charge_density());
183
184 #pragma omp parallel for
185 for (auto it : spl_q_) {
186 int iq = it.i;
187 sf::Spherical_Bessel_functions jl(0, atom_type.radial_grid(), grid_q_[iq]);
188
189 if (jl_deriv) {
190 auto s = jl.deriv_q(0);
191 values_(iat)(iq) = sirius::inner(s, ps_core, 2, atom_type.num_mt_points());
192 } else {
193 values_(iat)(iq) = sirius::inner(jl[0], ps_core, 2, atom_type.num_mt_points());
194 }
195 }
196 unit_cell_.comm().allgather(&values_(iat)(0), spl_q_.local_size(), spl_q_.global_offset());
197 values_(iat).interpolate();
198 }
199}
200
201template<bool jl_deriv>
203{
204 PROFILE("sirius::Radial_integrals|beta");
205
206 for (int iat = 0; iat < unit_cell_.num_atom_types(); iat++) {
207 auto& atom_type = unit_cell_.atom_type(iat);
208 int nrb = atom_type.num_beta_radial_functions();
209
210 if (!nrb) {
211 continue;
212 }
213
214 for (int idxrf = 0; idxrf < nrb; idxrf++) {
215 values_(idxrf, iat) = Spline<double>(grid_q_);
216 }
217
218 #pragma omp parallel for
219 for (auto it : spl_q_) {
220 int iq = it.i;
221 sf::Spherical_Bessel_functions jl(unit_cell_.lmax(), atom_type.radial_grid(), grid_q_[iq]);
222 for (int idxrf = 0; idxrf < nrb; idxrf++) {
223 int l = atom_type.indexr(idxrf).am.l();
224 /* compute \int j_l(q * r) beta_l(r) r^2 dr or \int d (j_l(q*r) / dq) beta_l(r) r^2 */
225 /* remember that beta(r) are defined as miltiplied by r */
226 if (jl_deriv) {
227 auto s = jl.deriv_q(l);
228 values_(idxrf, iat)(iq) = sirius::inner(s, atom_type.beta_radial_function(rf_index(idxrf)).second, 1);
229 } else {
230 values_(idxrf, iat)(iq) = sirius::inner(jl[l], atom_type.beta_radial_function(rf_index(idxrf)).second, 1);
231 }
232 }
233 }
234
235 for (int idxrf = 0; idxrf < nrb; idxrf++) {
236 unit_cell_.comm().allgather(&values_(idxrf, iat)(0), spl_q_.local_size(), spl_q_.global_offset());
237 values_(idxrf, iat).interpolate();
238 }
239 }
240}
241
242template <bool jl_deriv>
244{
245 PROFILE("sirius::Radial_integrals|vloc");
246
247 for (int iat = 0; iat < unit_cell_.num_atom_types(); iat++) {
248 auto& atom_type = unit_cell_.atom_type(iat);
249
250 if (atom_type.local_potential().empty()) {
251 continue;
252 }
253
254 values_(iat) = Spline<double>(grid_q_);
255
256 auto& vloc = atom_type.local_potential();
257
258 int np = atom_type.num_mt_points();
259 // if (std::abs(vloc.back() * atom_type.radial_grid().last() + atom_type.zn()) > 1e-10) {
260 // std::stringstream s;
261 // s << "Wrong asymptotics of local potential for atom type " << iat << std::endl
262 // << "hack with 10 a.u. cutoff is activated";
263 // WARNING(s);
264 /* This is a hack implemented in QE. For many pseudopotentials the tail doesn't decay as -z/r
265 * but rather diverges. Instead of issuing an error, the code trunkates the integraition at ~10 a.u. */
266 if (true) {
267 int np1 = atom_type.radial_grid().index_of(10);
268 if (np1 != -1) {
269 np = np1;
270 }
271 }
272
273 auto rg = atom_type.radial_grid().segment(np);
274
275 #pragma omp parallel for
276 for (auto it : spl_q_) {
277 int iq = it.i;
278 Spline<double> s(rg);
279 double g = grid_q_[iq];
280
281 if (jl_deriv) { /* integral with derivative of j0(q*r) over q */
282 for (int ir = 0; ir < rg.num_points(); ir++) {
283 double x = rg[ir];
284 s(ir) = (x * vloc[ir] + atom_type.zn() * std::erf(x)) * (std::sin(g * x) - g * x * std::cos(g * x));
285 }
286 } else { /* integral with j0(q*r) */
287 if (iq == 0) { /* q=0 case */
288 for (int ir = 0; ir < rg.num_points(); ir++) {
289 double x = rg[ir];
290
291 s(ir) = (x * vloc[ir] + atom_type.zn()) * x;
292 }
293 } else {
294 for (int ir = 0; ir < rg.num_points(); ir++) {
295 double x = rg[ir];
296
297 s(ir) = (x * vloc[ir] + atom_type.zn() * std::erf(x)) * std::sin(g * x);
298 }
299 }
300 }
301 values_(iat)(iq) = s.interpolate().integrate(0);
302 }
303 unit_cell_.comm().allgather(&values_(iat)(0), spl_q_.local_size(), spl_q_.global_offset());
304 values_(iat).interpolate();
305 }
306}
307
308void Radial_integrals_rho_free_atom::generate()
309{
310 PROFILE("sirius::Radial_integrals|rho_free_atom");
311
312 for (int iat = 0; iat < unit_cell_.num_atom_types(); iat++) {
313 auto& atom_type = unit_cell_.atom_type(iat);
314 values_(iat) = Spline<double>(grid_q_);
315
316 #pragma omp parallel for
317 for (int iq = 0; iq < grid_q_.num_points(); iq++) {
318 double g = grid_q_[iq];
319 Spline<double> s(unit_cell_.atom_type(iat).free_atom_radial_grid());
320 if (iq == 0) {
321 for (int ir = 0; ir < s.num_points(); ir++) {
322 s(ir) = atom_type.free_atom_density(ir);
323 }
324 values_(iat)(iq) = s.interpolate().integrate(2);
325 } else {
326 for (int ir = 0; ir < s.num_points(); ir++) {
327 s(ir) = atom_type.free_atom_density(ir) * std::sin(g * atom_type.free_atom_radial_grid(ir));
328 }
329 values_(iat)(iq) = s.interpolate().integrate(1);
330 }
331 }
332 values_(iat).interpolate();
333 }
334}
335
336template class Radial_integrals_atomic_wf<true>;
337template class Radial_integrals_atomic_wf<false>;
338
339template class Radial_integrals_aug<true>;
340template class Radial_integrals_aug<false>;
341
342template class Radial_integrals_rho_core_pseudo<true>;
343template class Radial_integrals_rho_core_pseudo<false>;
344
345template class Radial_integrals_beta<true>;
346template class Radial_integrals_beta<false>;
347
348template class Radial_integrals_vloc<true>;
349template class Radial_integrals_vloc<false>;
350
351
352} // namespace sirius
int num_points() const
Number of grid points.
void generate(std::function< Spline< double > const &(int, int)> fl__)
Generate radial integrals.
Radial integrals of the augmentation operator.
sddk::mdarray< Spline< double >, N > values_
Array with integrals.
Unit_cell const & unit_cell_
Unit cell.
splindex_block spl_q_
Split index of q-points.
Radial_grid< double > grid_q_
Linear grid of q-points on which the interpolation of radial integrals is done.
void generate()
Generate radial integrals on the q-grid.
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
Multidimensional array with the column-major (Fortran) order.
Definition: memory.hpp:660
Spherical Bessel functions up to lmax.
Definition: sbessel.hpp:38
Spline< double > deriv_q(int l__)
Derivative of Bessel function with respect to q.
Definition: sbessel.cpp:107
value_type local_size(block_id block_id__) const
Return local size of the split index for a given block.
Definition: splindex.hpp:302
int lmax(int lmmax__)
Get maximum orbital quantum number by the maximum lm index.
Definition: specfunc.hpp:56
Namespace of the SIRIUS library.
Definition: sirius.f90:5
strong_type< int, struct __rf_index_tag > rf_index
Radial function index.
const double fourpi
Definition: constants.hpp:48
Representation of various radial integrals.