SIRIUS 7.5.0
Electronic structure library and applications
xc_mt.cpp
Go to the documentation of this file.
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/** \file xc_mt.cpp
21 *
22 * \brief Generate XC potential in the muffin-tins.
23 */
24
25#include <vector>
26
27#include "potential.hpp"
28#include "core/typedefs.hpp"
29#include "core/omp.hpp"
30#include "core/profiler.hpp"
31#include "xc_functional.hpp"
32
33namespace sirius {
34
35void xc_mt_nonmagnetic(Radial_grid<double> const& rgrid__, SHT const& sht__, std::vector<XC_functional> const& xc_func__,
36 Flm const& rho_lm__, Ftp& rho_tp__, Flm& vxc_lm__, Flm& exc_lm__)
37{
38 bool is_gga{false};
39 for (auto& ixc : xc_func__) {
40 if (ixc.is_gga() || ixc.is_vdw()) {
41 is_gga = true;
42 }
43 }
44
45 Ftp exc_tp(sht__.num_points(), rgrid__);
46 Ftp vxc_tp(sht__.num_points(), rgrid__);
47
48 RTE_ASSERT(rho_tp__.size() == vxc_tp.size());
49 RTE_ASSERT(rho_tp__.size() == exc_tp.size());
50
51 Ftp grad_rho_grad_rho_tp;
52 Ftp vsigma_tp;
53 Ftp lapl_rho_tp;
54 Spheric_vector_function<function_domain_t::spatial, double> grad_rho_tp;
55
56 /* use Laplacian (true) or divergence of gradient (false) */
57 bool use_lapl{false};
58
59 if (is_gga) {
60 /* compute gradient in Rlm spherical harmonics */
61 auto grad_rho_lm = gradient(rho_lm__);
62 grad_rho_tp = Spheric_vector_function<function_domain_t::spatial, double>(sht__.num_points(), rgrid__);
63 /* backward transform gradient from Rlm to (theta, phi) */
64 for (int x = 0; x < 3; x++) {
65 transform(sht__, grad_rho_lm[x], grad_rho_tp[x]);
66 }
67 /* compute density gradient product */
68 grad_rho_grad_rho_tp = grad_rho_tp * grad_rho_tp;
69 RTE_ASSERT(rho_tp__.size() == grad_rho_grad_rho_tp.size());
70
71 vsigma_tp = Ftp(sht__.num_points(), rgrid__);
72 RTE_ASSERT(rho_tp__.size() == vsigma_tp.size());
73 if (use_lapl) {
74 /* backward transform Laplacian from Rlm to (theta, phi) */
75 lapl_rho_tp = transform(sht__, laplacian(rho_lm__));
76 RTE_ASSERT(lapl_rho_tp.size() == rho_tp__.size());
77 }
78 }
79
80 for (auto& ixc: xc_func__) {
81 /* if this is an LDA functional */
82 if (ixc.is_lda()) {
83 ixc.get_lda(sht__.num_points() * rgrid__.num_points(), rho_tp__.at(sddk::memory_t::host),
84 vxc_tp.at(sddk::memory_t::host), exc_tp.at(sddk::memory_t::host));
85 }
86 /* if this is a GGA functional */
87 if (ixc.is_gga()) {
88
89 /* compute vrho and vsigma */
90 ixc.get_gga(sht__.num_points() * rgrid__.num_points(), rho_tp__.at(sddk::memory_t::host),
91 grad_rho_grad_rho_tp.at(sddk::memory_t::host), vxc_tp.at(sddk::memory_t::host), vsigma_tp.at(sddk::memory_t::host),
92 exc_tp.at(sddk::memory_t::host));
93
94 if (use_lapl) {
95 vxc_tp -= 2.0 * vsigma_tp * lapl_rho_tp;
96
97 /* compute gradient of vsgima in spherical harmonics */
98 auto grad_vsigma_lm = gradient(transform(sht__, vsigma_tp));
99
100 /* backward transform gradient from Rlm to (theta, phi) */
101 Spheric_vector_function<function_domain_t::spatial, double> grad_vsigma_tp(sht__.num_points(), rgrid__);
102 for (int x = 0; x < 3; x++) {
103 transform(sht__, grad_vsigma_lm[x], grad_vsigma_tp[x]);
104 }
105
106 /* compute scalar product of two gradients */
107 auto grad_vsigma_grad_rho_tp = grad_vsigma_tp * grad_rho_tp;
108 /* add remaining term to Vxc */
109 vxc_tp -= 2.0 * grad_vsigma_grad_rho_tp;
110 } else {
111 Spheric_vector_function<function_domain_t::spectral, double> vsigma_grad_rho_lm(sht__.lmmax(), rgrid__);
112 for (int x: {0, 1, 2}) {
113 auto vsigma_grad_rho_tp = vsigma_tp * grad_rho_tp[x];
114 transform(sht__, vsigma_grad_rho_tp, vsigma_grad_rho_lm[x]);
115 }
116 auto div_vsigma_grad_rho_tp = transform(sht__, divergence(vsigma_grad_rho_lm));
117 /* add remaining term to Vxc */
118 vxc_tp -= 2.0 * div_vsigma_grad_rho_tp;
119 }
120 }
121 exc_lm__ += transform(sht__, exc_tp);
122 vxc_lm__ += transform(sht__, vxc_tp);
123 } //ixc
124}
125
126void xc_mt_magnetic(Radial_grid<double> const& rgrid__, SHT const& sht__, int num_mag_dims__,
127 std::vector<XC_functional> const& xc_func__, std::vector<Ftp> const& rho_tp__,
128 std::vector<Flm*> vxc__, Flm& exc__)
129{
130 bool is_gga{false};
131 for (auto& ixc : xc_func__) {
132 if (ixc.is_gga() || ixc.is_vdw()) {
133 is_gga = true;
134 }
135 }
136
137 Ftp exc_tp(sht__.num_points(), rgrid__);
138 Ftp vxc_tp(sht__.num_points(), rgrid__);
139
140 /* convert to rho_up, rho_dn */
141 Ftp rho_dn_tp(sht__.num_points(), rgrid__);
142 Ftp rho_up_tp(sht__.num_points(), rgrid__);
143 /* loop over radial grid points */
144 for (int ir = 0; ir < rgrid__.num_points(); ir++) {
145 /* loop over points on the sphere */
146 for (int itp = 0; itp < sht__.num_points(); itp++) {
147 r3::vector<double> m;
148 for (int j = 0; j < num_mag_dims__; j++) {
149 m[j] = rho_tp__[1 + j](itp, ir);
150 }
151 auto rud = get_rho_up_dn(num_mag_dims__, rho_tp__[0](itp, ir), m);
152
153 /* compute "up" and "dn" components */
154 rho_up_tp(itp, ir) = rud.first;
155 rho_dn_tp(itp, ir) = rud.second;
156 }
157 }
158 /* transform from (theta, phi) to Rlm */
159 auto rho_up_lm = transform(sht__, rho_up_tp);
160 auto rho_dn_lm = transform(sht__, rho_dn_tp);
161
162 std::vector<Ftp> bxc_tp(num_mag_dims__);
163
164 Ftp vxc_up_tp(sht__.num_points(), rgrid__);
165 Ftp vxc_dn_tp(sht__.num_points(), rgrid__);
166 for (int j = 0; j < num_mag_dims__; j++) {
167 bxc_tp[j] = Ftp(sht__.num_points(), rgrid__);
168 }
169
170 Ftp grad_rho_up_grad_rho_up_tp;
171 Ftp grad_rho_up_grad_rho_dn_tp;
172 Ftp grad_rho_dn_grad_rho_dn_tp;
173 Ftp vsigma_uu_tp;
174 Ftp vsigma_ud_tp;
175 Ftp vsigma_dd_tp;
176 Spheric_vector_function<function_domain_t::spatial, double> grad_rho_up_tp;
177 Spheric_vector_function<function_domain_t::spatial, double> grad_rho_dn_tp;
178 Spheric_vector_function<function_domain_t::spatial, double> grad_rho_up_vsigma_tp;
179 Spheric_vector_function<function_domain_t::spatial, double> grad_rho_dn_vsigma_tp;
180
181 if (is_gga) {
182 /* compute gradient in Rlm spherical harmonics */
183 auto grad_rho_up_lm = gradient(rho_up_lm);
184 auto grad_rho_dn_lm = gradient(rho_dn_lm);
185 grad_rho_up_tp = Spheric_vector_function<function_domain_t::spatial, double>(sht__.num_points(), rgrid__);
186 grad_rho_dn_tp = Spheric_vector_function<function_domain_t::spatial, double>(sht__.num_points(), rgrid__);
187 grad_rho_up_vsigma_tp = Spheric_vector_function<function_domain_t::spatial, double>(sht__.num_points(), rgrid__);
188 grad_rho_dn_vsigma_tp = Spheric_vector_function<function_domain_t::spatial, double>(sht__.num_points(), rgrid__);
189
190 /* backward transform gradient from Rlm to (theta, phi) */
191 for (int x = 0; x < 3; x++) {
192 transform(sht__, grad_rho_up_lm[x], grad_rho_up_tp[x]);
193 transform(sht__, grad_rho_dn_lm[x], grad_rho_dn_tp[x]);
194 }
195 /* compute density gradient products */
196 grad_rho_up_grad_rho_up_tp = grad_rho_up_tp * grad_rho_up_tp;
197 grad_rho_up_grad_rho_dn_tp = grad_rho_up_tp * grad_rho_dn_tp;
198 grad_rho_dn_grad_rho_dn_tp = grad_rho_dn_tp * grad_rho_dn_tp;
199
200 vsigma_uu_tp = Ftp(sht__.num_points(), rgrid__);
201 vsigma_ud_tp = Ftp(sht__.num_points(), rgrid__);
202 vsigma_dd_tp = Ftp(sht__.num_points(), rgrid__);
203 }
204
205 for (auto& ixc: xc_func__) {
206 if (ixc.is_lda()) {
207 ixc.get_lda(sht__.num_points() * rgrid__.num_points(), rho_up_tp.at(sddk::memory_t::host),
208 rho_dn_tp.at(sddk::memory_t::host), vxc_up_tp.at(sddk::memory_t::host), vxc_dn_tp.at(sddk::memory_t::host),
209 exc_tp.at(sddk::memory_t::host));
210 }
211 if (ixc.is_gga()) {
212 /* get the vrho and vsigma */
213 ixc.get_gga(sht__.num_points() * rgrid__.num_points(), rho_up_tp.at(sddk::memory_t::host),
214 rho_dn_tp.at(sddk::memory_t::host), grad_rho_up_grad_rho_up_tp.at(sddk::memory_t::host),
215 grad_rho_up_grad_rho_dn_tp.at(sddk::memory_t::host), grad_rho_dn_grad_rho_dn_tp.at(sddk::memory_t::host),
216 vxc_up_tp.at(sddk::memory_t::host), vxc_dn_tp.at(sddk::memory_t::host), vsigma_uu_tp.at(sddk::memory_t::host),
217 vsigma_ud_tp.at(sddk::memory_t::host), vsigma_dd_tp.at(sddk::memory_t::host), exc_tp.at(sddk::memory_t::host));
218
219 /* directly add to Vxc available contributions */
220 for (int x: {0, 1, 2}) {
221 grad_rho_up_vsigma_tp[x] = (2.0 * vsigma_uu_tp * grad_rho_up_tp[x] + vsigma_ud_tp * grad_rho_dn_tp[x]);
222 grad_rho_dn_vsigma_tp[x] = (2.0 * vsigma_dd_tp * grad_rho_dn_tp[x] + vsigma_ud_tp * grad_rho_up_tp[x]);
223 }
224
225 Spheric_vector_function<function_domain_t::spectral, double> grad_rho_up_vsigma_lm(sht__.lmmax(), rgrid__);
226 Spheric_vector_function<function_domain_t::spectral, double> grad_rho_dn_vsigma_lm(sht__.lmmax(), rgrid__);
227
228 for (int x: {0, 1, 2}) {
229 grad_rho_up_vsigma_lm[x] = transform(sht__, grad_rho_up_vsigma_tp[x]);
230 grad_rho_dn_vsigma_lm[x] = transform(sht__, grad_rho_dn_vsigma_tp[x]);
231 }
232
233 auto div_grad_rho_up_vsigma_lm = divergence(grad_rho_up_vsigma_lm);
234 auto div_grad_rho_dn_vsigma_lm = divergence(grad_rho_dn_vsigma_lm);
235
236 /* add remaining terms to Vxc */
237 vxc_up_tp -= transform(sht__, div_grad_rho_up_vsigma_lm) ;
238 vxc_dn_tp -= transform(sht__, div_grad_rho_dn_vsigma_lm);
239 }
240 /* generate magnetic field and effective potential inside MT sphere */
241 for (int ir = 0; ir < rgrid__.num_points(); ir++) {
242 for (int itp = 0; itp < sht__.num_points(); itp++) {
243 /* Vxc = 0.5 * (V_up + V_dn) */
244 vxc_tp(itp, ir) = 0.5 * (vxc_up_tp(itp, ir) + vxc_dn_tp(itp, ir));
245 /* Bxc = 0.5 * (V_up - V_dn) */
246 double bxc = 0.5 * (vxc_up_tp(itp, ir) - vxc_dn_tp(itp, ir));
247 /* get the sign between mag and B */
248 auto s = sign((rho_up_tp(itp, ir) - rho_dn_tp(itp, ir)) * bxc);
249
250 r3::vector<double> m;
251 for (int j = 0; j < num_mag_dims__; j++) {
252 m[j] = rho_tp__[1 + j](itp, ir);
253 }
254 auto m_len = m.length();
255 if (m_len > 1e-8) {
256 for (int j = 0; j < num_mag_dims__; j++) {
257 bxc_tp[j](itp, ir) = std::abs(bxc) * s * m[j] / m_len;
258 }
259 } else {
260 for (int j = 0; j < num_mag_dims__; j++) {
261 bxc_tp[j](itp, ir) = 0.0;
262 }
263 }
264 }
265 }
266 /* convert magnetic field back to Rlm */
267 for (int j = 0; j < num_mag_dims__; j++) {
268 *vxc__[j + 1] += transform(sht__, bxc_tp[j]);
269 }
270 /* forward transform from (theta, phi) to Rlm */
271 *vxc__[0] += transform(sht__, vxc_tp);
272 exc__ += transform(sht__, exc_tp);
273 } // ixc
274}
275
276double xc_mt(Radial_grid<double> const& rgrid__, SHT const& sht__, std::vector<XC_functional> const& xc_func__,
277 int num_mag_dims__, std::vector<Flm const*> rho__, std::vector<Flm*> vxc__, Flm* exc__)
278{
279 /* zero the fields */
280 exc__->zero();
281 for (int j = 0; j < num_mag_dims__ + 1; j++) {
282 vxc__[j]->zero();
283 }
284
285 std::vector<Ftp> rho_tp(num_mag_dims__ + 1);
286 for (int j = 0; j < num_mag_dims__ + 1; j++) {
287 /* convert density and magnetization to theta, phi */
288 rho_tp[j] = transform(sht__, *rho__[j]);
289 }
290
291 /* check if density has negative values */
292 double rhomin{0};
293 for (int ir = 0; ir < rgrid__.num_points(); ir++) {
294 for (int itp = 0; itp < sht__.num_points(); itp++) {
295 rhomin = std::min(rhomin, rho_tp[0](itp, ir));
296 /* fix negative density */
297 if (rho_tp[0](itp, ir) < 0.0) {
298 for (int j = 0; j < num_mag_dims__ + 1; j++) {
299 rho_tp[j](itp, ir) = 0.0;
300 }
301 }
302 }
303 }
304
305 if (num_mag_dims__ == 0) {
306 xc_mt_nonmagnetic(rgrid__, sht__, xc_func__, *rho__[0], rho_tp[0], *vxc__[0], *exc__);
307 } else {
308 xc_mt_magnetic(rgrid__, sht__, num_mag_dims__, xc_func__, rho_tp, vxc__, *exc__);
309 }
310
311 return rhomin;
312}
313
314void Potential::xc_mt(Density const& density__)
315{
316 PROFILE("sirius::Potential::xc_mt");
317
318 #pragma omp parallel for
319 for (auto it : unit_cell_.spl_num_atoms()) {
320 auto& rgrid = unit_cell_.atom(it.i).radial_grid();
321 std::vector<Flm const*> rho(ctx_.num_mag_dims() + 1);
322 std::vector<Flm*> vxc(ctx_.num_mag_dims() + 1);
323 rho[0] = &density__.rho().mt()[it.i];
324 vxc[0] = &xc_potential_->mt()[it.i];
325 for (int j = 0; j < ctx_.num_mag_dims(); j++) {
326 rho[j + 1] = &density__.mag(j).mt()[it.i];
327 vxc[j + 1] = &effective_magnetic_field(j).mt()[it.i];
328 }
329
330 auto rhomin = sirius::xc_mt(rgrid, *sht_, xc_func_, ctx_.num_mag_dims(), rho, vxc, &xc_energy_density_->mt()[it.i]);
331 if (rhomin < 0.0) {
332 std::stringstream s;
333 s << "[xc_mt] negative charge density " << rhomin << " for atom " << it.i << std::endl
334 << " current Rlm expansion of the charge density may be not sufficient, try to increase lmax" << std::endl
335 << " sht.lmax : " << sht_->lmax() << std::endl
336 << " sht.num_points : " << sht_->num_points();
337 RTE_WARNING(s);
338 }
339
340 /* z, x, y order */
341 std::array<int, 3> comp_map = {2, 0, 1};
342 /* add auxiliary magnetic field antiparallel to starting magnetization */
343 for (int j = 0; j < ctx_.num_mag_dims(); j++) {
344 for (int ir = 0; ir < rgrid.num_points(); ir++) {
345 effective_magnetic_field(j).mt()[it.i](0, ir) -=
346 aux_bf_(j, it.i) * ctx_.unit_cell().atom(it.i).vector_field()[comp_map[j]];
347 }
348 }
349 } // ialoc
350}
351
352} // namespace sirius
Generate charge density and magnetization from occupied spinor wave-functions.
Definition: density.hpp:214
auto const & rho() const
Return const reference to charge density (scalar functions).
Definition: density.hpp:416
std::unique_ptr< Periodic_function< double > > xc_energy_density_
XC energy per unit particle.
Definition: potential.hpp:61
void xc_mt(Density const &density__)
Generate XC potential in the muffin-tins.
Definition: xc_mt.cpp:314
std::unique_ptr< Periodic_function< double > > xc_potential_
XC potential.
Definition: potential.hpp:58
Unit_cell & unit_cell_
Alias to unit cell.
Definition: potential.hpp:49
int num_mag_dims() const
Number of dimensions in the magnetization vector.
Atom const & atom(int id__) const
Return const atom instance by id.
Definition: unit_cell.hpp:344
std::enable_if_t< std::is_same< T, real_type< F > >::value, void > transform(::spla::Context &spla_ctx__, sddk::memory_t mem__, la::dmatrix< F > const &M__, int irow0__, int jcol0__, real_type< F > alpha__, Wave_functions< T > const &wf_in__, spin_index s_in__, band_range br_in__, real_type< F > beta__, Wave_functions< T > &wf_out__, spin_index s_out__, band_range br_out__)
Apply linear transformation to the wave-functions.
Namespace of the SIRIUS library.
Definition: sirius.f90:5
auto get_rho_up_dn(int num_mag_dims__, double rho__, r3::vector< double > mag__)
Use Kuebler's trick to get rho_up and rho_dn from density and magnetisation.
Definition: density.hpp:80
Smooth_periodic_vector_function< T > gradient(Smooth_periodic_function< T > &f__)
Gradient of the function in the plane-wave domain.
Smooth_periodic_function< T > laplacian(Smooth_periodic_function< T > &f__)
Laplacian of the function in the plane-wave domain.
Smooth_periodic_function< T > divergence(Smooth_periodic_vector_function< T > &g__)
Divergence of the vecor function.
int sign(T val)
Sign of the variable.
Definition: math_tools.hpp:52
Add or substitute OMP functions.
Contains declaration and partial implementation of sirius::Potential class.
A time-based profiler.
Contains typedefs, enums and simple descriptors.
Contains implementation of sirius::XC_functional class.