SIRIUS 7.5.0
Electronic structure library and applications
hubbard_potential_energy.cpp
1// Copyright (c) 2013-2018 Mathieu Taillefumier, 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 hubbard_potential_energy.hpp
21 *
22 * \brief Generate Hubbard potential correction matrix from the occupancy matrix.
23 */
24
25#include "hubbard.hpp"
26
27namespace sirius {
28
29/* we can use Ref PRB {\bf 102}, 235159 (2020) as reference for the collinear case. */
30
31static void
32generate_potential_collinear_nonlocal(Simulation_context const& ctx__, const int index__,
33 sddk::mdarray<std::complex<double>, 3> const& om__,
34 sddk::mdarray<std::complex<double>, 3>& um__)
35{
36 auto nl = ctx__.cfg().hubbard().nonlocal(index__);
37 um__.zero();
38
39 double v_ij = nl.V() / ha2ev;
40 int il = nl.l()[0];
41 int jl = nl.l()[1];
42 um__.zero();
43 // second term of Eq. 2
44 for (int is = 0; is < ctx__.num_spins(); is++) {
45 for (int m2 = 0; m2 < 2 * jl + 1; m2++) {
46 for (int m1 = 0; m1 < 2 * il + 1; m1++) {
47 um__(m1, m2, is) = -v_ij * om__(m1, m2, is);
48 }
49 }
50 }
51}
52
53static void
54generate_potential_collinear_local(Simulation_context const& ctx__, Atom_type const& atom_type__, const int idx_hub_wf,
55 sddk::mdarray<std::complex<double>, 3> const& om__, sddk::mdarray<std::complex<double>, 3>& um__)
56{
57 /* quick exit */
58 if (!atom_type__.hubbard_correction()) {
59 return;
60 }
61
62 um__.zero();
63
64 /* single orbital implementation */
65 auto& hub_wf = atom_type__.lo_descriptor_hub(idx_hub_wf);
66
67 if (!hub_wf.use_for_calculation()) {
68 return;
69 }
70
71 int const lmax_at = 2 * hub_wf.l() + 1;
72
73 if (ctx__.cfg().hubbard().simplified()) {
74
75 if ((hub_wf.U() != 0.0) || (hub_wf.alpha() != 0.0)) {
76
77 double U_effective = hub_wf.U();
78
79 if (std::abs(hub_wf.J0()) > 1e-8) {
80 U_effective -= hub_wf.J0();
81 }
82
83 for (int is = 0; is < ctx__.num_spins(); is++) {
84
85 // is = 0 up-up
86 // is = 1 down-down
87
88 /* Expression Eq.7 without the P_IJ which is applied when we
89 * actually apply the potential to the wave functions. Also
90 * note the presence of alpha */
91 for (int m1 = 0; m1 < lmax_at; m1++) {
92 um__(m1, m1, is) = hub_wf.alpha() + 0.5 * U_effective;
93
94 for (int m2 = 0; m2 < lmax_at; m2++) {
95 um__(m2, m1, is) -= U_effective * om__(m2, m1, is);
96 }
97 }
98 }
99 }
100
101 if (std::abs(hub_wf.J0()) > 1e-8 || std::abs(hub_wf.beta()) > 1e-8) {
102 for (int is = 0; is < ctx__.num_spins(); is++) {
103
104 // s = 0 -> s_opposite = 1
105 // s = 1 -> s_opposite = 0
106 int const s_opposite = (is + 1) % 2;
107
108 double const sign = (is == 0) - (is == 1);
109
110 for (int m1 = 0; m1 < lmax_at; m1++) {
111
112 um__(m1, m1, is) += sign * hub_wf.beta();
113
114 for (int m2 = 0; m2 < lmax_at; m2++) {
115 um__(m1, m2, is) += hub_wf.J0() * om__(m2, m1, s_opposite);
116 }
117 }
118 }
119 }
120 } else {
121 /* full hubbard correction */
122
123 // total N and M^2 for the double counting problem
124
125 double n_total{0};
126 // n_up and n_down spins
127 double n_updown[2] = {0.0, 0.0};
128
129 for (int s = 0; s < ctx__.num_spins(); s++) {
130 for (int m = 0; m < lmax_at; m++) {
131 n_total += om__(m, m, s).real();
132 }
133
134 for (int m = 0; m < lmax_at; m++) {
135 n_updown[s] += om__(m, m, s).real();
136 }
137 }
138
139 // now hubbard contribution
140
141 for (int is = 0; is < ctx__.num_spins(); is++) {
142 for (int m1 = 0; m1 < lmax_at; m1++) {
143
144 /* dc contribution */
145 um__(m1, m1, is) += hub_wf.J() * n_updown[is] + 0.5 * (hub_wf.U() - hub_wf.J()) - hub_wf.U() * n_total;
146
147 // the u contributions
148 for (int m2 = 0; m2 < lmax_at; m2++) {
149 for (int m3 = 0; m3 < lmax_at; m3++) {
150 for (int m4 = 0; m4 < lmax_at; m4++) {
151 for (int is2 = 0; is2 < ctx__.num_spins(); is2++) {
152 um__(m1, m2, is) += hub_wf.hubbard_matrix(m1, m3, m2, m4) * om__(m3, m4, is2);
153 }
154
155 um__(m1, m2, is) -= hub_wf.hubbard_matrix(m1, m3, m4, m2) * om__(m3, m4, is);
156 }
157 }
158 }
159 }
160 }
161 }
162}
163
164static double
165calculate_energy_collinear_nonlocal(Simulation_context const& ctx__, const int index__,
166 sddk::mdarray<std::complex<double>, 3> const& om__)
167{
168 auto nl = ctx__.cfg().hubbard().nonlocal(index__);
169 double hubbard_energy{0.0};
170 double v_ij_ = nl.V() / ha2ev;
171 int il = nl.l()[0];
172 int jl = nl.l()[1];
173
174 // second term of Eq. 2
175 for (int is = 0; is < ctx__.num_spins(); is++) {
176 for (int m1 = 0; m1 < 2 * jl + 1; m1++) {
177 for (int m2 = 0; m2 < 2 * il + 1; m2++) {
178 hubbard_energy += v_ij_ * std::real(om__(m2, m1, is) * conj(om__(m2, m1, is)));
179 }
180 }
181 }
182
183 // non magnetic case
184 if (ctx__.num_spins() == 1) {
185 hubbard_energy *= 2.0;
186 }
187
188 return -0.5 * hubbard_energy;
189}
190
191static double
192calculate_energy_collinear_local(Simulation_context const& ctx__, Atom_type const& atom_type__, const int idx_hub_wf,
193 sddk::mdarray<std::complex<double>, 3> const& om__)
194{
195 double hubbard_energy{0};
196 double hubbard_energy_u{0};
197 double hubbard_energy_dc_contribution{0};
198
199 /* quick exit */
200 if (!atom_type__.hubbard_correction()) {
201 return 0.0;
202 }
203
204 /* single orbital implementation */
205 auto& hub_wf = atom_type__.lo_descriptor_hub(idx_hub_wf);
206
207 if (!hub_wf.use_for_calculation())
208 return 0.0;
209
210 int const lmax_at = 2 * hub_wf.l() + 1;
211
212 if (ctx__.cfg().hubbard().simplified()) {
213 if ((hub_wf.U() != 0.0) || (hub_wf.alpha() != 0.0)) {
214
215 double U_effective = hub_wf.U();
216
217 if (std::abs(hub_wf.J0()) > 1e-8) {
218 U_effective -= hub_wf.J0();
219 }
220
221 for (int is = 0; is < ctx__.num_spins(); is++) {
222
223 // is = 0 up-up
224 // is = 1 down-down
225
226 for (int m1 = 0; m1 < lmax_at; m1++) {
227 hubbard_energy += (hub_wf.alpha() + 0.5 * U_effective) * om__(m1, m1, is).real();
228
229 for (int m2 = 0; m2 < lmax_at; m2++) {
230 hubbard_energy -= 0.5 * U_effective * (om__(m1, m2, is) * om__(m2, m1, is)).real();
231 }
232 }
233 }
234 }
235 if (std::abs(hub_wf.J0()) > 1e-8 || std::abs(hub_wf.beta()) > 1e-8) {
236 for (int is = 0; is < ctx__.num_spins(); is++) {
237 // s = 0 -> s_opposite = 1
238 // s= 1 -> s_opposite = 0
239 const int s_opposite = (is + 1) % 2;
240
241 const double sign = (is == 0) - (is == 1);
242
243 for (int m1 = 0; m1 < lmax_at; m1++) {
244
245 hubbard_energy += sign * hub_wf.beta() * om__(m1, m1, is).real();
246
247 for (int m2 = 0; m2 < lmax_at; m2++) {
248 hubbard_energy += 0.5 * hub_wf.J0() * (om__(m2, m1, is) * om__(m1, m2, s_opposite)).real();
249 }
250 }
251 }
252 }
253
254 if (ctx__.num_spins() == 1) {
255 hubbard_energy *= 2.0;
256 }
257
258 } else {
259 /* full hubbard correction */
260
261 // total N and M^2 for the double counting problem
262
263 double n_total{0};
264 // n_up and n_down spins
265 double n_updown[2] = {0.0, 0.0};
266
267 for (int s = 0; s < ctx__.num_spins(); s++) {
268 for (int m = 0; m < lmax_at; m++) {
269 n_total += om__(m, m, s).real();
270 }
271
272 for (int m = 0; m < lmax_at; m++) {
273 n_updown[s] += om__(m, m, s).real();
274 }
275 }
276 double magnetization{0};
277
278 if (ctx__.num_mag_dims() == 0) {
279 n_total *= 2.0; /* factor two here because the occupations are <= 1 */
280 } else {
281 for (int m = 0; m < lmax_at; m++) {
282 magnetization += (om__(m, m, 0) - om__(m, m, 1)).real();
283 }
284 magnetization *= magnetization;
285 }
286
287 hubbard_energy_dc_contribution +=
288 0.5 * (hub_wf.U() * n_total * (n_total - 1.0) - hub_wf.J() * n_total * (0.5 * n_total - 1.0) -
289 hub_wf.J() * magnetization * 0.5);
290
291 /* now hubbard contribution */
292
293 for (int is = 0; is < ctx__.num_spins(); is++) {
294 for (int m1 = 0; m1 < lmax_at; m1++) {
295
296 /* the u contributions */
297 for (int m2 = 0; m2 < lmax_at; m2++) {
298 for (int m3 = 0; m3 < lmax_at; m3++) {
299 for (int m4 = 0; m4 < lmax_at; m4++) {
300
301 hubbard_energy_u +=
302 0.5 * ((hub_wf.hubbard_matrix(m1, m2, m3, m4) - hub_wf.hubbard_matrix(m1, m2, m4, m3)) *
303 om__(m1, m3, is) * om__(m2, m4, is) +
304 hub_wf.hubbard_matrix(m1, m2, m3, m4) * om__(m1, m3, is) *
305 om__(m2, m4, (ctx__.num_mag_dims() == 1) ? ((is + 1) % 2) : (0)))
306 .real();
307 }
308 }
309 }
310 }
311 }
312
313 if (ctx__.num_mag_dims() == 0) {
314 hubbard_energy_u *= 2.0;
315 }
316 hubbard_energy = hubbard_energy_u - hubbard_energy_dc_contribution;
317 }
318
319 //// TODO: move the printout to proper place
320 // if ((ctx.verbosity() >= 1) && (ctx.comm().rank() == 0)) {
321 // std::printf("hub Energy (total) %.5lf (dc) %.5lf\n", hubbard_energy, hubbard_energy_dc_contribution);
322 //}
323 return hubbard_energy;
324}
325
326static void
327generate_potential_non_collinear_local(Simulation_context const& ctx__, Atom_type const& atom_type__,
328 const int idx_hub_wf, sddk::mdarray<std::complex<double>, 3> const& om__,
329 sddk::mdarray<std::complex<double>, 3>& um__)
330{
331 /* quick exit */
332 if (!atom_type__.hubbard_correction()) {
333 return;
334 }
335
336 um__.zero();
337
338 /* single orbital implementation */
339 auto& hub_wf = atom_type__.lo_descriptor_hub(idx_hub_wf);
340
341 if (!hub_wf.use_for_calculation())
342 return;
343
344 int const lmax_at = 2 * hub_wf.l() + 1;
345
346 // compute the charge and magnetization of the hubbard bands for
347 // calculation of the double counting term in the hubbard correction
348
349 std::complex<double> n_total{0};
350 for (int m = 0; m < lmax_at; m++) {
351 n_total += om__(m, m, 0) + om__(m, m, 1);
352 }
353
354 for (int is = 0; is < 4; is++) {
355
356 // diagonal elements of n^{\sigma\sigma'}
357
358 int is1 = -1;
359
360 switch (is) {
361 case 2:
362 is1 = 3;
363 break;
364 case 3:
365 is1 = 2;
366 break;
367 default:
368 is1 = is;
369 break;
370 }
371
372 // same thing for the hubbard potential
373 if (is1 == is) {
374 // non spin flip
375 for (int m1 = 0; m1 < lmax_at; ++m1) {
376 for (int m2 = 0; m2 < lmax_at; ++m2) {
377 for (int m3 = 0; m3 < lmax_at; ++m3) {
378 for (int m4 = 0; m4 < lmax_at; ++m4) {
379 um__(m1, m2, is) +=
380 hub_wf.hubbard_matrix(m1, m3, m2, m4) * (om__(m3, m4, 0) + om__(m3, m4, 1));
381 }
382 }
383 }
384 }
385 }
386
387 // double counting contribution
388
389 std::complex<double> n_aux{0};
390 for (int m1 = 0; m1 < lmax_at; m1++) {
391 n_aux += om__(m1, m1, is1);
392 }
393
394 for (int m1 = 0; m1 < lmax_at; m1++) {
395
396 // hubbard potential : dc contribution
397
398 um__(m1, m1, is) += hub_wf.J() * n_aux;
399
400 if (is1 == is) {
401 um__(m1, m1, is) += 0.5 * (hub_wf.U() - hub_wf.J()) - hub_wf.U() * n_total;
402 }
403
404 // spin flip contributions
405 for (int m2 = 0; m2 < lmax_at; m2++) {
406 for (int m3 = 0; m3 < lmax_at; m3++) {
407 for (int m4 = 0; m4 < lmax_at; m4++) {
408 um__(m1, m2, is) -= hub_wf.hubbard_matrix(m1, m3, m4, m2) * om__(m3, m4, is1);
409 }
410 }
411 }
412 }
413 }
414}
415
416static double
417calculate_energy_non_collinear_local(Simulation_context const& ctx__, Atom_type const& atom_type__,
418 const int idx_hub_wf, sddk::mdarray<std::complex<double>, 3> const& om__)
419{
420 /* quick exit */
421 if (!atom_type__.hubbard_correction()) {
422 return 0.0;
423 }
424
425 /* single orbital implementation */
426 auto& hub_wf = atom_type__.lo_descriptor_hub(idx_hub_wf);
427
428 if (!hub_wf.use_for_calculation())
429 return 0.0;
430
431 int const lmax_at = 2 * hub_wf.l() + 1;
432
433 double hubbard_energy_dc_contribution{0};
434 double hubbard_energy_noflip{0};
435 double hubbard_energy_flip{0};
436 double hubbard_energy{0};
437
438 // compute the charge and magnetization of the hubbard bands for
439 // calculation of the double counting term in the hubbard correction
440
441 double n_total{0};
442 double mx{0};
443 double my{0};
444 double mz{0};
445
446 for (int m = 0; m < lmax_at; m++) {
447 n_total += (om__(m, m, 0) + om__(m, m, 1)).real();
448 mz += (om__(m, m, 0) - om__(m, m, 1)).real();
449 mx += (om__(m, m, 2) + om__(m, m, 3)).real();
450 my += (om__(m, m, 2) - om__(m, m, 3)).imag();
451 }
452
453 double magnetization = mz * mz + mx * mx + my * my;
454
455 hubbard_energy_dc_contribution +=
456 0.5 * (hub_wf.U() * n_total * (n_total - 1.0) - hub_wf.J() * n_total * (0.5 * n_total - 1.0) -
457 0.5 * hub_wf.J() * magnetization);
458
459 for (int is = 0; is < 4; is++) {
460
461 // diagonal elements of n^{\sigma\sigma'}
462
463 int is1 = -1;
464
465 switch (is) {
466 case 2:
467 is1 = 3;
468 break;
469 case 3:
470 is1 = 2;
471 break;
472 default:
473 is1 = is;
474 break;
475 }
476
477 if (is1 == is) {
478 // non spin flip contributions for the hubbard energy
479 for (int m1 = 0; m1 < lmax_at; ++m1) {
480 for (int m2 = 0; m2 < lmax_at; ++m2) {
481 for (int m3 = 0; m3 < lmax_at; ++m3) {
482 for (int m4 = 0; m4 < lmax_at; ++m4) {
483 // 2 - is - 1 = 0 if is = 1
484 // = 1 if is = 0
485
486 hubbard_energy_noflip +=
487 0.5 *
488 ((hub_wf.hubbard_matrix(m1, m2, m3, m4) - hub_wf.hubbard_matrix(m1, m2, m4, m3)) *
489 om__(m1, m3, is) * om__(m2, m4, is) +
490 hub_wf.hubbard_matrix(m1, m2, m3, m4) * om__(m1, m3, is) * om__(m2, m4, (is + 1) % 2))
491 .real();
492 }
493 }
494 }
495 }
496 } else {
497 // spin flip contributions
498 for (int m1 = 0; m1 < lmax_at; ++m1) {
499 for (int m2 = 0; m2 < lmax_at; ++m2) {
500 for (int m3 = 0; m3 < lmax_at; ++m3) {
501 for (int m4 = 0; m4 < lmax_at; ++m4) {
502 hubbard_energy_flip -=
503 0.5 *
504 (hub_wf.hubbard_matrix(m1, m2, m4, m3) * om__(m1, m3, is) * om__(m2, m4, is1)).real();
505 }
506 }
507 }
508 }
509 }
510 }
511
512 hubbard_energy = hubbard_energy_noflip + hubbard_energy_flip - hubbard_energy_dc_contribution;
513
514 // if ((ctx_.verbosity() >= 1) && (ctx_.comm().rank() == 0)) {
515 // std::printf("\n hub Energy (total) %.5lf (no-flip) %.5lf (flip) %.5lf (dc) %.5lf\n",
516 // hubbard_energy, hubbard_energy_noflip, hubbard_energy_flip, hubbard_energy_dc_contribution);
517 //}
518
519 return hubbard_energy;
520}
521
522void
523generate_potential(Hubbard_matrix const& om__, Hubbard_matrix& um__)
524{
525 auto& ctx = om__.ctx();
526
527 for (int at_lvl = 0; at_lvl < static_cast<int>(om__.local().size()); at_lvl++) {
528 const int ia = om__.atomic_orbitals(at_lvl).first;
529 auto& atype = ctx.unit_cell().atom(ia).type();
530 int lo_ind = om__.atomic_orbitals(at_lvl).second;
531
532 if (ctx.num_mag_dims() != 3) {
533 ::sirius::generate_potential_collinear_local(ctx, atype, lo_ind, om__.local(at_lvl), um__.local(at_lvl));
534 } else {
535 ::sirius::generate_potential_non_collinear_local(ctx, atype, lo_ind, om__.local(at_lvl),
536 um__.local(at_lvl));
537 }
538 }
539
540 for (int i = 0; i < static_cast<int>(ctx.cfg().hubbard().nonlocal().size()); i++) {
541
542 if (ctx.num_mag_dims() != 3) {
543 ::sirius::generate_potential_collinear_nonlocal(ctx, i, om__.nonlocal(i), um__.nonlocal(i));
544 } // else {
545 // ::sirius::generate_potential_non_collinear_nonlocal(ctx, nl, om__.nonlocal(i), um__.nonlocal(i));
546 //}
547 }
548}
549
550double
551energy(Hubbard_matrix const& om__)
552{
553 double energy{0};
554
555 auto& ctx = om__.ctx();
556
557 for (int at_lvl = 0; at_lvl < static_cast<int>(om__.local().size()); at_lvl++) {
558 const int ia = om__.atomic_orbitals(at_lvl).first;
559 auto& atype = ctx.unit_cell().atom(ia).type();
560 int lo_ind = om__.atomic_orbitals(at_lvl).second;
561 if (ctx.num_mag_dims() != 3) {
562 energy += ::sirius::calculate_energy_collinear_local(ctx, atype, lo_ind, om__.local(at_lvl));
563 } else {
564 energy += ::sirius::calculate_energy_non_collinear_local(ctx, atype, lo_ind, om__.local(at_lvl));
565 }
566 }
567
568 for (int i = 0; i < static_cast<int>(ctx.cfg().hubbard().nonlocal().size()); i++) {
569 if (ctx.num_mag_dims() != 3) {
570 energy += ::sirius::calculate_energy_collinear_nonlocal(ctx, i, om__.nonlocal(i));
571 } // else {
572 // energy += ::sirius::calculate_energy_noncollinear_nonlocal(ctx, nl, om__.nonlocal(i));
573 //}
574 }
575 return energy;
576}
577
578// this function is used when we want to calculate the kinetic energy. The
579// kinetic energy is calculated from the self consistent hamiltonian not from
580// the direct calculation of the gradient of the wave functions.
581//
582// E_kin = \sum_{i,k} \epsilon_ik n_{ik} - <\psi_{ik} | V | \psi_{ik}>
583//
584//
585// where V is the potential (that include all contributions). V = V0 + VHub and
586//
587// <\psi_{ik} | V_Hub | \psi_{ik}> = \sum_{m_1,m_2} U^I (\delta_m1m2 - n^I_{m_1, m_2}) conj(n^I_{m1, m2}) - V^{ij} |
588// n^{ij}_{m1,m2}| ^ 2
589//
590// it is a real number
591
592double
593one_electron_energy_hubbard(Hubbard_matrix const& om__, Hubbard_matrix const& pm__)
594{
595 auto& ctx = om__.ctx();
596 if (ctx.hubbard_correction()) {
597 std::complex<double> tmp{0.0, 0.0};
598 for (int at_lvl = 0; at_lvl < static_cast<int>(om__.local().size()); at_lvl++) {
599 const int ia = om__.atomic_orbitals(at_lvl).first;
600 int lo_ind = om__.atomic_orbitals(at_lvl).second;
601 auto& atype = ctx.unit_cell().atom(ia).type();
602 auto& hub_wf = atype.lo_descriptor_hub(lo_ind);
603
604 if (hub_wf.use_for_calculation()) {
605 auto src1 = om__.local(at_lvl).at(sddk::memory_t::host);
606 auto src2 = pm__.local(at_lvl).at(sddk::memory_t::host);
607
608 for (int i = 0; i < static_cast<int>(om__.local(at_lvl).size()); i++) {
609 tmp += src1[i] * std::conj(src2[i]);
610 }
611 }
612 }
613
614 for (int i = 0; i < static_cast<int>(ctx.cfg().hubbard().nonlocal().size()); i++) {
615 auto nl = ctx.cfg().hubbard().nonlocal(i);
616 int il = nl.l()[0];
617 int jl = nl.l()[1];
618
619 const auto& n1 = om__.nonlocal(i);
620 const auto& n2 = pm__.nonlocal(i);
621
622 for (int is = 0; is < ctx.num_spins(); is++) {
623 for (int m2 = 0; m2 < 2 * jl + 1; m2++) {
624 for (int m1 = 0; m1 < 2 * il + 1; m1++) {
625 tmp += std::conj(n2(m1, m2, is)) * n1(m1, m2, is);
626 }
627 }
628 }
629 }
630
631 if (ctx.num_spins() == 1) {
632 tmp *= 2.0;
633 }
634 return std::real(tmp);
635 }
636 return 0.0;
637}
638
639} // namespace sirius
Contains declaration and partial implementation of sirius::Hubbard class.
Namespace of the SIRIUS library.
Definition: sirius.f90:5
const double ha2ev
Hartree in electron-volt units.
Definition: constants.hpp:54
int sign(T val)
Sign of the variable.
Definition: math_tools.hpp:52
auto conj(double x__)
Return complex conjugate of a number. For a real value this is the number itself.
Definition: math_tools.hpp:165