High-precision calculations for one- and two-valence atomic systems
Sigma2.ipp
1#pragma once
2#include "Angular/include.hpp"
3#include "Coulomb/include.hpp"
4#include "Wavefunction/DiracSpinor.hpp"
5
6namespace MBPT {
7
8//! Direct and exchange parts of the 1-body Sigma (2nd-order correlation)
9//! matrix element, returned separately as {direct, exchange};
10//! <v|Sigma|w> = direct + exchange. qk may be YkTable or QkTable.
11template <class CoulombIntegral> // CoulombIntegral may be YkTable or QkTable
12std::pair<double, double> Sigma_vw_direct_exchange(
13 const DiracSpinor &v, const DiracSpinor &w, const CoulombIntegral &qk,
14 const std::vector<DiracSpinor> &core, const std::vector<DiracSpinor> &excited,
15 int max_l_internal, std::optional<double> ev) {
16
17 static_assert(std::is_same_v<CoulombIntegral, Coulomb::QkTable> ||
18 std::is_same_v<CoulombIntegral, Coulomb::YkTable>);
19
20 // const auto e_v = ev ? *ev : v.en();
21
22 const auto e_v = ev ? *ev : 0.5 * (v.en() + w.en());
23 // Should be e_v, OR e_w, depending on time ordering. We use average to make
24 // diagrams symmetric.
25
26 // Calculates <Fv|Sigma|Fw> from scratch, at Fw energy [full grid + fg+gg]
27 if (v.kappa() != w.kappa())
28 return {0.0, 0.0};
29
30 // Mainly for tests, but can include basis states only up to maximum l
31 if (max_l_internal < 0)
32 max_l_internal = 999;
33
34 double sum_direct = 0.0;
35 double sum_exchange = 0.0;
36#pragma omp parallel for reduction(+ : sum_direct, sum_exchange) collapse(2) \
37 schedule(dynamic)
38 for (auto ia = 0ul; ia < core.size(); ia++) {
39 for (auto in = 0ul; in < excited.size(); in++) {
40
41 const auto &a = core[ia];
42 const auto &n = excited[in];
43
44 if (a.l() > max_l_internal || n.l() > max_l_internal)
45 continue;
46
47 const auto de_an = a.en() - n.en();
48
49 // Diagrams (a) [direct] and (b) [exchange]
50 for (const auto &m : excited) {
51 if (m.l() > max_l_internal)
52 continue;
53
54 const auto inv_de = 1.0 / (e_v + de_an - m.en());
55
56 const auto [k0, k1] = Coulomb::k_minmax_Q(v, a, m, n);
57 for (int k = k0; k <= k1; k += 2) {
58 const auto Qk_vamn = qk.Q(k, v, a, m, n);
59 const auto Qk_wamn = (v == w) ? Qk_vamn : qk.Q(k, w, a, m, n);
60 const auto Pk_wamn = qk.P(k, w, a, m, n);
61 sum_direct += Qk_vamn * Qk_wamn * inv_de / (2.0 * k + 1.0);
62 sum_exchange += Qk_vamn * Pk_wamn * inv_de / (2.0 * k + 1.0);
63 }
64 }
65
66 // Diagrams (c) [direct] and (d) [exchange]
67 for (const auto &b : core) {
68 if (b.l() > max_l_internal)
69 continue;
70
71 const auto inv_de = 1.0 / (e_v - de_an - b.en()); // v? w? ... careful!
72
73 const auto [k0, k1] = Coulomb::k_minmax_Q(v, n, b, a);
74 for (int k = k0; k <= k1; k += 2) {
75 const auto Qk_vnba = qk.Q(k, v, n, b, a);
76 const auto Qk_wnba = (v == w) ? Qk_vnba : qk.Q(k, w, n, b, a);
77 const auto Pk_wnba = qk.P(k, w, n, b, a);
78 sum_direct += Qk_vnba * Qk_wnba * inv_de / (2.0 * k + 1.0);
79 sum_exchange += Qk_vnba * Pk_wnba * inv_de / (2.0 * k + 1.0);
80 }
81 }
82 }
83 }
84
85 const auto factor = 1.0 / v.twojp1();
86 return {sum_direct * factor, sum_exchange * factor};
87}
88
89//==============================================================================
90//! Matrix element of 1-body Sigma (2nd-order correlation) operator;
91//! de_v = <v|Sigma|v>. qk (CoulombIntegral) may be YkTable or QkTable.
92template <class CoulombIntegral> // CoulombIntegral may be YkTable or QkTable
93double Sigma_vw(const DiracSpinor &v, const DiracSpinor &w,
94 const CoulombIntegral &qk, const std::vector<DiracSpinor> &core,
95 const std::vector<DiracSpinor> &excited, int max_l_internal,
96 std::optional<double> ev) {
97 const auto [direct, exchange] =
98 Sigma_vw_direct_exchange(v, w, qk, core, excited, max_l_internal, ev);
99 return direct + exchange;
100}
101
102//==============================================================================
103template <class CoulombIntegral>
104double dSigma_dE_vw(const DiracSpinor &v, const DiracSpinor &w,
105 const CoulombIntegral &qk,
106 const std::vector<DiracSpinor> &core,
107 const std::vector<DiracSpinor> &excited, double ev,
108 int max_l_internal, double delta) {
109 const auto Sp = Sigma_vw(v, w, qk, core, excited, max_l_internal, ev + delta);
110 const auto Sm = Sigma_vw(v, w, qk, core, excited, max_l_internal, ev - delta);
111 return (Sp - Sm) / (2.0 * delta);
112}
113
114} // namespace MBPT
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:55
int twojp1() const
2j+1
Definition DiracSpinor.hpp:123
int kappa() const
Dirac quantum number, kappa.
Definition DiracSpinor.hpp:115
double en() const
Single-particle energy, not including rest energy.
Definition DiracSpinor.hpp:176
std::pair< int, int > k_minmax_Q(const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d)
Returns min and max k (multipolarity) allowed for Q^k_abcd. Parity rule is included,...
Definition CoulombIntegrals.hpp:146
Many-body perturbation theory.
Definition MatrixElements.hpp:12
double dSigma_dE_vw(const DiracSpinor &v, const DiracSpinor &w, const CoulombIntegral &qk, const std::vector< DiracSpinor > &core, const std::vector< DiracSpinor > &excited, double ev, int max_l_internal=99, double delta=0.01)
Energy derivative of the one-body correlation correction.
Definition Sigma2.ipp:104
double Sigma_vw(const DiracSpinor &v, const DiracSpinor &w, const CoulombIntegral &qk, const std::vector< DiracSpinor > &core, const std::vector< DiracSpinor > &excited, int max_l_internal=99, std::optional< double > ev=std::nullopt)
Matrix element of the 1-body Sigma (2nd-order correlation) operator.
Definition Sigma2.ipp:93
std::pair< double, double > Sigma_vw_direct_exchange(const DiracSpinor &v, const DiracSpinor &w, const CoulombIntegral &qk, const std::vector< DiracSpinor > &core, const std::vector< DiracSpinor > &excited, int max_l_internal=99, std::optional< double > ev=std::nullopt)
Direct and exchange parts of Sigma_vw(), returned separately.
Definition Sigma2.ipp:12