ampsci
High-precision calculations for one- and two-valence atomic systems
Ladder.ipp
1#pragma once
2#include "Angular/include.hpp"
3#include "Coulomb/include.hpp"
4#include "Wavefunction/DiracSpinor.hpp"
5#include <type_traits>
6// namespace MBPT
7
8//==============================================================================
9// Calculate energy shift (either ladder, or sigma2)
10// nb: set lk to qk to det de(2)
11template <typename Qintegrals, typename QorLintegrals>
12double de_valence(const DiracSpinor &v, const Qintegrals &qk,
13 const QorLintegrals &lk, const std::vector<DiracSpinor> &core,
14 const std::vector<DiracSpinor> &excited,
15 const std::vector<double> &v_fk,
16 const std::vector<double> &v_eta) {
17
18 // XXX Fix this!
19 // static_assert(std::is_same_v<Qintegrals, Coulomb::YkTable> ||
20 // std::is_base_of_v<Coulomb::CoulombTable, Qintegrals>,
21 // "Qintegrals must be YkTable or CoulombTable
22 // (QkTable/LkTable)");
23 // static_assert(
24 // std::is_same_v<QorLintegrals, Coulomb::YkTable> ||
25 // std::is_base_of_v<Coulomb::CoulombTable, QorLintegrals>,
26 // "QorLintegrals must be YkTable or CoulombTable (QkTable/LkTable)");
27
28 const bool LisQ = [&]() {
29 if constexpr (std::is_same_v<Qintegrals, QorLintegrals>)
30 return &qk == &lk;
31 else
32 return false;
33 }();
34
35 // screening factors:
36 auto Fk = [&v_fk](int k) {
37 return k < (int)v_fk.size() ? v_fk[std::size_t(k)] : 1.0;
38 };
39 // hole-particle
40 auto Eta = [&v_eta](int k) {
41 return k < (int)v_eta.size() ? v_eta[std::size_t(k)] : 1.0;
42 };
43
44 auto de_v = 0.0;
45
46 // nb: always put 'v' in correct* position. This ensures that actual state v
47 // is used when evaluating Q/W integrals [as opposed to the excited basis
48 // version of v]. Note: only matters when using YkTable for integrals
49 // *correct == 1st or 3rd in Q, 1st or 4th in P
50
51#pragma omp parallel for reduction(+ : de_v)
52 for (auto in = 0ul; in < excited.size(); ++in) {
53 const auto &n = excited[in];
54 for (const auto &a : core) {
55
56 // Diagrams (a) + (b)
57 for (const auto &m : excited) {
58 const auto inv_de = 1.0 / (v.en() + a.en() - m.en() - n.en());
59 const auto [k0, kI] = Coulomb::k_minmax_Q(v, a, m, n);
60 for (int k = k0; k <= kI; k += 2) {
61
62 const auto fk = Fk(k);
63 const auto etak = Eta(k);
64
65 const auto Q_kvamn = qk.Q(k, v, a, m, n);
66 const auto L_kvamn = LisQ ? fk * Q_kvamn : lk.Q(k, m, n, v, a);
67 const auto P_kvamn = lk.P(k, n, m, a, v); // \propto Q_mnva
68
69 // diagram (a). xxx include fk here? I though no...
70 de_v += /*fk **/ etak * Q_kvamn * L_kvamn * inv_de / (2 * k + 1);
71 // diagram (b) [exchange]
72 de_v += fk * Q_kvamn * P_kvamn * inv_de / (2 * k + 1);
73 } // k
74 } // m
75
76 // Diagrams (c) + (d)
77 for (const auto &b : core) {
78 const auto inv_de = 1.0 / (v.en() + n.en() - a.en() - b.en());
79 const auto [k0, kI] = Coulomb::k_minmax_Q(v, n, a, b);
80 for (int k = k0; k <= kI; k += 2) {
81
82 const auto fk = Fk(k);
83 const auto etak = Eta(k);
84
85 const auto Q_kvnab = qk.Q(k, v, n, a, b);
86 const auto L_kvnab = LisQ ? fk * Q_kvnab : lk.Q(k, v, n, a, b);
87 const auto P_kvnab = lk.P(k, v, n, a, b);
88
89 // diagram (c). xxx include fk here? I though no...
90 de_v += /*fk **/ etak * Q_kvnab * L_kvnab * inv_de / (2 * k + 1);
91 // diagram (d) [exchange]
92 de_v += fk * Q_kvnab * P_kvnab * inv_de / (2 * k + 1);
93 }
94 }
95 }
96 }
97
98 return de_v / v.twojp1();
99}
100
101//------------------------------------------------------------------------------
102// Calculate energy shift (either ladder, or sigma2) for CORE
103// lk may be regular Coulomb integrals [in which case this returns MBPT(2)
104// correction], or Ladder diagrams [in which case this returns the ladder
105// diagram correction]
106template <typename Qintegrals, typename QorLintegrals>
107double de_core(const Qintegrals &qk, const QorLintegrals &lk,
108 const std::vector<DiracSpinor> &core,
109 const std::vector<DiracSpinor> &excited) {
110
111 // XXX Fix this
112 // static_assert(std::is_same_v<Qintegrals, Coulomb::YkTable> ||
113 // std::is_base_of_v<Coulomb::CoulombTable, QorLintegrals>,
114 // "Qintegrals must be YkTable or CoulombTable
115 // (QkTable/LkTable)");
116
117 const bool LisQ = [&]() {
118 if constexpr (std::is_same_v<Qintegrals, QorLintegrals>)
119 return &qk == &lk;
120 else
121 return false;
122 }();
123
124 auto de_c = 0.0;
125#pragma omp parallel for reduction(+ : de_c)
126 for (auto in = 0ul; in < excited.size(); ++in) {
127 const auto &n = excited[in];
128 for (const auto &m : excited) {
129 for (const auto &a : core) {
130 for (const auto &b : core) {
131 const auto inv_de = 1.0 / (a.en() + b.en() - m.en() - n.en());
132 const auto [k0, kI] = Coulomb::k_minmax_Q(a, b, m, n);
133 for (int k = k0; k <= kI; k += 2) {
134 const auto Q_kabmn = qk.Q(k, a, b, m, n);
135 const auto L_kmnab = LisQ ? Q_kabmn : lk.Q(k, m, n, a, b);
136 const auto P_kmnab = lk.P(k, m, n, a, b);
137 de_c += 0.5 * Q_kabmn * (L_kmnab + P_kmnab) * inv_de / (2 * k + 1);
138 }
139 }
140 }
141 }
142 }
143 return de_c;
144}
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:42
int twojp1() const
2j+1
Definition DiracSpinor.hpp:95
double en() const
Single-particle energy, not including rest energy.
Definition DiracSpinor.hpp:122
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.cpp:496
double de_core(const Qintegrals &qk, const QorLintegrals &lk, const std::vector< DiracSpinor > &core, const std::vector< DiracSpinor > &excited)
Second-order (or ladder) correction to the core energy.
double de_valence(const DiracSpinor &v, const Qintegrals &qk, const QorLintegrals &lk, const std::vector< DiracSpinor > &core, const std::vector< DiracSpinor > &excited, const std::vector< double > &fk={}, const std::vector< double > &etak={})
Second-order (or ladder) correction to the valence energy.