SIRIUS 7.5.0
Electronic structure library and applications
paw_potential.cpp
1// Copyright (c) 2013-2018 Anton Kozhevnikov, Ilia Sivkov, 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 paw_potential.hpp
21 *
22 * \brief Generate PAW potential.
23 */
24
25#include "potential.hpp"
27
28namespace sirius {
29
30void Potential::init_PAW()
31{
33 return;
34 }
35
36 bool const is_global{true};
37 paw_potential_ = std::make_unique<PAW_field4D<double>>("PAW potential", unit_cell_, is_global);
38
39 paw_ae_exc_ = std::make_unique<Spheric_function_set<double, paw_atom_index_t>>("paw_ae_exc_", unit_cell_,
40 unit_cell_.paw_atoms(), [this](int ia){return lmax_t(2 * this->unit_cell_.atom(ia).type().indexr().lmax());});
41
42 paw_ps_exc_ = std::make_unique<Spheric_function_set<double, paw_atom_index_t>>("paw_ps_exc_", unit_cell_,
43 unit_cell_.paw_atoms(), [this](int ia){return lmax_t(2 * this->unit_cell_.atom(ia).type().indexr().lmax());});
44
45 /* initialize dij matrix */
47 for (int i = 0; i < unit_cell_.num_paw_atoms(); i++) {
48 int ia = unit_cell_.paw_atom_index(paw_atom_index_t::global(i));
49 paw_dij_[i] = sddk::mdarray<double, 3>(unit_cell_.atom(ia).mt_basis_size(), unit_cell_.atom(ia).mt_basis_size(),
50 ctx_.num_mag_dims() + 1);
51 }
52}
53
54void Potential::generate_PAW_effective_potential(Density const& density)
55{
56 PROFILE("sirius::Potential::generate_PAW_effective_potential");
57
59 return;
60 }
61
62 paw_potential_->zero();
63
65
66 /* calculate xc and hartree for atoms */
67 for (auto it : unit_cell_.spl_num_paw_atoms()) {
68 auto ia = unit_cell_.paw_atom_index(it.i);
69 paw_hartree_total_energy_ += calc_PAW_local_potential(ia, density.paw_ae_density(ia),
70 density.paw_ps_density(ia));
71 }
73
74 paw_potential_->sync();
75 std::vector<Spheric_function_set<double, paw_atom_index_t>*> ae_comp;
76 std::vector<Spheric_function_set<double, paw_atom_index_t>*> ps_comp;
77 for (int j = 0; j < ctx_.num_mag_dims() + 1; j++) {
78 ae_comp.push_back(&paw_potential_->ae_component(j));
79 ps_comp.push_back(&paw_potential_->ps_component(j));
80 }
81 sirius::symmetrize_mt_function(unit_cell_.symmetry(), unit_cell_.comm(), ctx_.num_mag_dims(), ae_comp);
82 sirius::symmetrize_mt_function(unit_cell_.symmetry(), unit_cell_.comm(), ctx_.num_mag_dims(), ps_comp);
83
84 /* symmetrize ae- component of Exc */
86 ae_comp.clear();
87 ae_comp.push_back(paw_ae_exc_.get());
88 sirius::symmetrize_mt_function(unit_cell_.symmetry(), unit_cell_.comm(), 0, ae_comp);
89
90 /* symmetrize ps- component of Exc */
92 ps_comp.clear();
93 ps_comp.push_back(paw_ps_exc_.get());
94 sirius::symmetrize_mt_function(unit_cell_.symmetry(), unit_cell_.comm(), 0, ps_comp);
95
96 /* calculate PAW Dij matrix */
97 #pragma omp parallel for
98 for (auto it : unit_cell_.spl_num_paw_atoms()) {
99 auto ia = unit_cell_.paw_atom_index(it.i);
100 calc_PAW_local_Dij(ia, paw_dij_[it.i]);
101 }
102 for (int i = 0; i < unit_cell_.num_paw_atoms(); i++) {
103 auto location = unit_cell_.spl_num_paw_atoms().location(typename paw_atom_index_t::global(i));
104 comm_.bcast(paw_dij_[i].at(sddk::memory_t::host), paw_dij_[i].size(), location.ib);
105 }
106
107 /* add paw Dij to uspp Dij */
108 #pragma omp parallel for
109 for (int i = 0; i < unit_cell_.num_paw_atoms(); i++) {
110 auto ia = unit_cell_.paw_atom_index(typename paw_atom_index_t::global(i));
111 auto& atom = unit_cell_.atom(ia);
112
113 for (int imagn = 0; imagn < ctx_.num_mag_dims() + 1; imagn++) {
114 for (int ib2 = 0; ib2 < atom.mt_basis_size(); ib2++) {
115 for (int ib1 = 0; ib1 < atom.mt_basis_size(); ib1++) {
116 atom.d_mtrx(ib1, ib2, imagn) += paw_dij_[i](ib1, ib2, imagn);
117 }
118 }
119 }
120 }
121}
122
123double xc_mt_paw(std::vector<XC_functional> const& xc_func__, int lmax__, int num_mag_dims__, SHT const& sht__,
124 Radial_grid<double> const& rgrid__, std::vector<Flm const*> rho__, std::vector<double> const& rho_core__,
125 std::vector<Flm>& vxc__, Flm& exclm__)
126{
127 int lmmax = sf::lmmax(lmax__);
128
129 /* new array to store core and valence densities */
130 Flm rho0(lmmax, rgrid__);
131
132 if (rho0.size(0) != rho__[0]->size(0)) {
133 std::stringstream s;
134 s << "Sizes of rho0 and rho do not match" << std::endl
135 << " rho0.size(0) : " << rho0.size(0) << std::endl
136 << " rho__[0]->size(0) : " << rho__[0]->size(0) << std::endl
137 << " lmax : " << lmax__;
138 RTE_THROW(s);
139 }
140
141 rho0.zero();
142 rho0 += (*rho__[0]);
143
144 double invY00 = 1.0 / y00;
145
146 /* add core density */
147 for (int ir = 0; ir < rgrid__.num_points(); ir++) {
148 rho0(0, ir) += invY00 * rho_core__[ir];
149 }
150
151 std::vector<Flm const*> rho;
152 rho.push_back(&rho0);
153 for (int j = 0; j < num_mag_dims__; j++) {
154 rho.push_back(rho__[j + 1]);
155 }
156
157 std::vector<Flm*> vxc;
158 for (int j = 0; j < num_mag_dims__ + 1; j++) {
159 vxc.push_back(&vxc__[j]);
160 }
161
162 sirius::xc_mt(rgrid__, sht__, xc_func__, num_mag_dims__, rho, vxc, &exclm__);
163 return inner(exclm__, rho0);
164}
165
166double Potential::calc_PAW_hartree_potential(Atom& atom__, Flm const& rho__, Flm& v_tot__)
167{
168 auto lmmax = rho__.angular_domain_size();
169
170 auto& grid = rho__.radial_grid();
171
172 /* array passed to poisson solver */
173 Flm v_ha(lmmax, grid);
174 v_ha.zero();
175
176 poisson_vmt<true>(atom__, rho__, v_ha);
177
178 /* add hartree contribution */
179 v_tot__ += v_ha;
180
181 return 0.5 * inner(rho__, v_ha);
182}
183
184double
185Potential::calc_PAW_local_potential(typename atom_index_t::global ia__, std::vector<Flm const*> ae_density__,
186 std::vector<Flm const*> ps_density__)
187{
188 auto& atom = unit_cell_.atom(ia__);
189
190 /* calculation of XC potential */
191 auto& ps_core = atom.type().ps_core_charge_density();
192 auto& ae_core = atom.type().paw_ae_core_charge_density();
193
194 auto& rgrid = atom.type().radial_grid();
195 int l_max = 2 * atom.type().indexr().lmax();
196
197 std::vector<Flm> vxc;
198 for (int j = 0; j < ctx_.num_mag_dims() + 1; j++) {
199 vxc.emplace_back(sf::lmmax(l_max), rgrid);
200 }
201
202 sirius::xc_mt_paw(xc_func_, l_max, ctx_.num_mag_dims(), *sht_, rgrid, ae_density__, ae_core, vxc, (*paw_ae_exc_)[ia__]);
203 for (int i = 0; i < ctx_.num_mag_dims() + 1; i++) {
204 paw_potential_->ae_component(i)[ia__] += vxc[i];
205 }
206
207 sirius::xc_mt_paw(xc_func_, l_max, ctx_.num_mag_dims(), *sht_, rgrid, ps_density__, ps_core, vxc, (*paw_ps_exc_)[ia__]);
208 for (int i = 0; i < ctx_.num_mag_dims() + 1; i++) {
209 paw_potential_->ps_component(i)[ia__] += vxc[i];
210 }
211
212 auto eha = calc_PAW_hartree_potential(atom, *ae_density__[0], paw_potential_->ae_component(0)[ia__]) -
213 calc_PAW_hartree_potential(atom, *ps_density__[0], paw_potential_->ps_component(0)[ia__]);
214
215 return eha;
216}
217
218void Potential::calc_PAW_local_Dij(typename atom_index_t::global ia__, sddk::mdarray<double, 3>& paw_dij__)
219{
220 paw_dij__.zero();
221
222 auto& atom = unit_cell_.atom(ia__);
223
224 auto& atom_type = atom.type();
225
226 auto& paw_ae_wfs = atom_type.ae_paw_wfs_array();
227 auto& paw_ps_wfs = atom_type.ps_paw_wfs_array();
228
229 /* get lm size for density */
230 int lmax = atom_type.indexr().lmax();
231 int lmmax = sf::lmmax(2 * lmax);
232
233 auto l_by_lm = sf::l_by_lm(2 * lmax);
234
236
237 auto nbrf = atom_type.num_beta_radial_functions();
238
239 /* store integrals here */
240 sddk::mdarray<double, 3> integrals(lmmax, nbrf * (nbrf + 1) / 2, ctx_.num_mag_dims() + 1);
241
242 auto& rgrid = atom_type.radial_grid();
243
244 for (int imagn = 0; imagn < ctx_.num_mag_dims() + 1; imagn++) {
245 auto& ae_atom_pot = paw_potential_->ae_component(imagn)[ia__];
246 auto& ps_atom_pot = paw_potential_->ps_component(imagn)[ia__];
247
248 for (int irb2 = 0; irb2 < nbrf; irb2++) {
249 for (int irb1 = 0; irb1 <= irb2; irb1++) {
250
251 /* create array for integration */
252 std::vector<double> intdata(rgrid.num_points());
253
254 // TODO: precompute radial integrals of paw_ae_wfs and paw_ps_wfs pair products
255 for (int lm3 = 0; lm3 < lmmax; lm3++) {
256 /* fill array */
257 for (int irad = 0; irad < rgrid.num_points(); irad++) {
258 double ae_part = paw_ae_wfs(irad, irb1) * paw_ae_wfs(irad, irb2);
259 double ps_part = paw_ps_wfs(irad, irb1) * paw_ps_wfs(irad, irb2) +
260 atom_type.q_radial_function(irb1, irb2, l_by_lm[lm3])(irad);
261
262 intdata[irad] = ae_atom_pot(lm3, irad) * ae_part - ps_atom_pot(lm3, irad) * ps_part;
263 }
264
265 /* integrate */
266 integrals(lm3, packed_index(irb1, irb2), imagn) = Spline<double>(rgrid, intdata).integrate(0);
267 }
268 }
269 }
270 }
271
272 /* calculate Dij */
273 for (int ib2 = 0; ib2 < atom_type.mt_basis_size(); ib2++) {
274 for (int ib1 = 0; ib1 <= ib2; ib1++) {
275
276 /* get lm quantum numbers (lm index) of the basis functions */
277 int lm1 = atom_type.indexb(ib1).lm;
278 int lm2 = atom_type.indexb(ib2).lm;
279
280 /* get radial basis functions indices */
281 int irb1 = atom_type.indexb(ib1).idxrf;
282 int irb2 = atom_type.indexb(ib2).idxrf;
283
284 /* common index */
285 int iqij = packed_index(irb1, irb2);
286
287 /* get num of non-zero GC */
288 int num_non_zero_gk = GC.num_gaunt(lm1, lm2);
289
290 for (int imagn = 0; imagn < ctx_.num_mag_dims() + 1; imagn++) {
291 /* add nonzero coefficients */
292 for (int inz = 0; inz < num_non_zero_gk; inz++) {
293 auto& lm3coef = GC.gaunt(lm1, lm2, inz);
294
295 /* add to atom Dij an integral of dij array */
296 paw_dij__(ib1, ib2, imagn) += lm3coef.coef * integrals(lm3coef.lm3, iqij, imagn);
297 }
298
299 if (ib1 != ib2) {
300 paw_dij__(ib2, ib1, imagn) = paw_dij__(ib1, ib2, imagn);
301 }
302 }
303 }
304 }
305}
306
307double
308Potential::calc_PAW_one_elec_energy(Atom const& atom__, sddk::mdarray<double, 2> const& density_matrix__,
309 sddk::mdarray<double, 3> const& paw_dij__) const
310{
311 double energy{0.0};
312
313 for (int ib2 = 0; ib2 < atom__.mt_basis_size(); ib2++) {
314 for (int ib1 = 0; ib1 < atom__.mt_basis_size(); ib1++) {
315 for (int imagn = 0; imagn < ctx_.num_mag_dims() + 1; imagn++) {
316 energy += density_matrix__(packed_index(ib1, ib2), imagn) * paw_dij__(ib1, ib2, imagn);
317 }
318 }
319 }
320 return energy;
321}
322
323} // namespace sirius
Atom_type const & type() const
Return const reference to corresponding atom type object.
Definition: atom.hpp:318
Compact storage of non-zero Gaunt coefficients .
Definition: gaunt.hpp:58
mpi::Communicator const & comm_
Communicator of the simulation.
Definition: potential.hpp:52
double paw_hartree_total_energy_
Hartree contribution to total energy from PAW atoms.
Definition: potential.hpp:115
double calc_PAW_local_potential(typename atom_index_t::global ia__, std::vector< Flm const * > ae_density, std::vector< Flm const * > ps_density)
Calculate PAW potential for a given atom.
std::unique_ptr< Spheric_function_set< double, paw_atom_index_t > > paw_ae_exc_
Exchange-correlation energy density of PAW atoms all-electron density.
Definition: potential.hpp:124
std::unique_ptr< PAW_field4D< double > > paw_potential_
All-electron and pseudopotential parts of PAW potential.
Definition: potential.hpp:118
std::unique_ptr< Spheric_function_set< double, paw_atom_index_t > > paw_ps_exc_
Exchange-correlation energy density of PAW atoms pseudodensity.
Definition: potential.hpp:121
Unit_cell & unit_cell_
Alias to unit cell.
Definition: potential.hpp:49
std::vector< sddk::mdarray< double, 3 > > paw_dij_
Contribution to D-operator matrix from the PAW atoms.
Definition: potential.hpp:127
static double gaunt_rrr(int l1, int l2, int l3, int m1, int m2, int m3)
Gaunt coefficent of three real spherical harmonics.
Definition: sht.hpp:423
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
int num_paw_atoms() const
Return number of atoms with PAW pseudopotential.
Definition: unit_cell.hpp:161
auto const & spl_num_paw_atoms() const
Get split index of PAW atoms.
Definition: unit_cell.hpp:172
auto paw_atom_index(paw_atom_index_t::global ipaw__) const
Return global index of atom by the index in the list of PAW atoms.
Definition: unit_cell.hpp:183
void bcast(T *buffer__, int count__, int root__) const
Perform buffer broadcast.
void allreduce(T *buffer__, int count__) const
Perform the in-place (the output buffer is used as the input buffer) all-to-all reduction.
void zero(memory_t mem__, size_t idx0__, size_t n__)
Zero n elements starting from idx0.
Definition: memory.hpp:1316
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
std::vector< int > l_by_lm(int lmax__)
Get array of orbital quantum numbers for each lm component.
Definition: specfunc.hpp:69
std::enable_if_t< std::is_same< T, real_type< F > >::value, void > inner(::spla::Context &spla_ctx__, sddk::memory_t mem__, spin_range spins__, W const &wf_i__, band_range br_i__, Wave_functions< T > const &wf_j__, band_range br_j__, la::dmatrix< F > &result__, int irow0__, int jcol0__)
Compute inner product between the two sets of wave-functions.
Namespace of the SIRIUS library.
Definition: sirius.f90:5
const double y00
First spherical harmonic .
Definition: constants.hpp:51
int packed_index(int i__, int j__)
Pack two indices into one for symmetric matrices.
Contains declaration and partial implementation of sirius::Potential class.
Symmetrize muffin-tin spheric functions.