2#include "CoulombIntegrals.hpp"
3#include "IO/ChronoTimer.hpp"
4#include "IO/FRW_fileReadWrite.hpp"
5#include "Physics/AtomData.hpp"
17 std::cout <<
"Summary: \n";
20 for (
auto &qk : m_data) {
21 std::cout <<
"k=" << k <<
": " << qk.size() <<
" [" << qk.bucket_count()
26 std::cout <<
"Total: " << total <<
" non-zero integrals\n";
32 for (
auto &qk : m_data) {
42 add(k, NormalOrder(a, b, c, d), value);
54 const auto sk = std::size_t(k);
55 if (sk >= m_data.size()) {
56 m_data.resize(sk + 1);
58 m_data.at(sk).insert({index, value});
66 update(k, NormalOrder(a, b, c, d), value);
81 const auto sk = std::size_t(k);
82 if (sk >= m_data.size()) {
83 m_data.resize(sk + 1);
85 m_data.at(sk).insert_or_assign(index, value);
91 const auto sk = std::size_t(k);
92 if (sk >= m_data.size())
94 return m_data.at(sk).find(NormalOrder(a, b, c, d)) != m_data.at(sk).cend();
106 const auto sk = std::size_t(k);
107 if (sk >= m_data.size())
109 return m_data.at(sk).find(index) != m_data.at(sk).cend();
117 const auto sk = std::size_t(k);
118 if (sk >= m_data.size())
121 const auto map_it = m_data.at(sk).find(index);
122 if (map_it == m_data.at(sk).cend())
124 return &(map_it->second);
131 const auto sk = std::size_t(k);
132 if (sk >= m_data.size())
135 const auto map_it = m_data.at(sk).find(NormalOrder(a, b, c, d));
136 if (map_it == m_data.at(sk).cend())
138 return map_it->second;
149 const auto sk = std::size_t(k);
150 if (sk >= m_data.size())
153 const auto map_it = m_data.at(sk).find(index);
154 if (map_it == m_data.at(sk).cend())
156 return map_it->second;
163 const auto tQk = Q(k, a, b, c, d);
175 return tQk / (s * tCkac * tCkbd);
205 const auto [lmin, lmax] =
k_minmax_Q(ka, kb, kd, kc);
206 for (
int l = lmin; l <= lmax; l += 2) {
207 const auto ql = this->Q(l, a, b, d, c);
210 const auto sixj = sj ? sj->
get_2(tja, tjc, 2 * k, tjb, tjd, 2 * l) :
233 const std::vector<double> &fk)
const {
237 if (Coulomb::triangle(a, c, k) == 0 || Coulomb::triangle(k, b, d) == 0)
240 const auto [lmin, lmax] =
k_minmax_Q(a, b, d, c);
241 for (
int l = lmin; l <= lmax; l += 2) {
242 const auto ql = this->Q(l, a, b, d, c);
245 const auto sixj = sj.
get(a, c, k, b, d, l);
248 const auto f_scr_l = (l < (int)fk.size()) ? fk[std::size_t(l)] : 1.0;
250 Pk_abcd += f_scr_l * sixj * ql;
267 return Q(k, a, b, c, d) + P(k, a, b, c, d, sj);
274 int tmb,
int tmc,
int tmd)
const {
276 if (tmc - tma != tmb - tmd)
278 const int twoq = tmc - tma;
283 for (
int k = k0; k <= ki; k += 2) {
284 if (std::abs(twoq) > 2 * k)
300 g += s * tjs1 * tjs2 * Q(k, a, b, c, d);
332 nk4Index out = FormIndex(a, b, c, d);
333 const auto min = std::min({a, b, c, d});
335 const auto tmp = FormIndex(a, d, c, b);
340 const auto tmp = std::min(FormIndex(b, a, d, c), FormIndex(b, c, d, a));
345 const auto tmp = std::min(FormIndex(c, b, a, d), FormIndex(c, d, a, b));
350 const auto tmp = std::min(FormIndex(d, a, b, c), FormIndex(d, c, b, a));
361 const auto tmp1 = FormIndex(a, b, c, d);
362 const auto tmp2 = FormIndex(b, a, d, c);
363 const auto tmp3 = FormIndex(c, d, a, b);
364 const auto tmp4 = FormIndex(d, c, b, a);
365 return std::min({tmp1, tmp2, tmp3, tmp4});
373 const auto tmp1 = FormIndex(a, b, c, d);
374 const auto tmp2 = FormIndex(b, a, d, c);
375 return std::min({tmp1, tmp2});
383 return FormIndex(a, b, c, d);
390 return NormalOrder_impl(a, b, c, d);
404 return FormIndex(a, b, c, d);
432 static_assert(
sizeof(
nkIndex) * 8 == 16);
439std::array<nkIndex, 4>
441 static_assert(
sizeof(
nk4Index) ==
sizeof(std::array<nkIndex, 4>));
442 std::array<nkIndex, 4> set;
443 std::memcpy(&set, &index,
sizeof(
nk4Index));
448 std::swap(set[0], set[3]);
449 std::swap(set[1], set[2]);
457 const YkTable &yk,
int k_cut,
bool print) {
458 static_assert(S == Symmetry::Qk);
459 IO::ChronoTimer t(
"fill");
477 const auto tmp_max_k =
481 (k_cut <= 0) ? tmp_max_k : std::min(tmp_max_k, std::size_t(k_cut));
483 if (m_data.size() < max_k + 1)
484 m_data.resize(max_k + 1);
488 std::vector<std::size_t> count_non_zero_k(max_k + 1);
489#pragma omp parallel for
490 for (
auto k = 0ul; k <= max_k; ++k) {
491 const auto ik =
static_cast<int>(k);
492 for (
const auto &a : basis) {
493 for (
const auto &b : basis) {
494 for (
const auto &c : basis) {
497 for (
const auto &d : basis) {
499 if (NormalOrder(a, b, c, d) == CurrentOrder(a, b, c, d)) {
501 ++count_non_zero_k[k];
510 std::cout <<
"Count non-zero: " << t.lap_reading_str() << std::endl;
514#pragma omp parallel for
515 for (
auto ik = 0ul; ik <= max_k; ++ik) {
516 m_data[ik].reserve(count_non_zero_k[ik]);
519 std::cout <<
"Reserve: " << t.lap_reading_str() << std::endl;
523#pragma omp parallel for
524 for (
auto k = 0ul; k <= max_k; ++k) {
525 for (
const auto &a : basis) {
526 for (
const auto &c : basis) {
529 for (
const auto &b : basis) {
530 for (
const auto &d : basis) {
533 const auto normal_index = NormalOrder(a, b, c, d);
534 if (normal_index == CurrentOrder(a, b, c, d)) {
535 add(
int(k), normal_index, 0.0);
543 std::cout <<
"Fill w/ zeros: " << t.lap_reading_str() << std::endl;
550 std::cout <<
"Fill w/ values: " << std::flush;
552#pragma omp parallel for collapse(2)
553 for (
auto ia = 0ul; ia < basis.size(); ++ia) {
554 for (
auto ib = 0ul; ib < basis.size(); ++ib) {
555 const auto &a = basis[ia];
556 const auto &b = basis[ib];
557 for (
const auto &c : basis) {
558 for (
const auto &d : basis) {
559 const auto normal_index = NormalOrder(a, b, c, d);
560 if (normal_index == CurrentOrder(a, b, c, d)) {
561 const auto [kmin, kmax] =
k_minmax_Q(a, b, c, d);
562 for (
int k = kmin; k <= kmax && k <= int(max_k); k += 2) {
563 double *ptr = get(k, normal_index);
564 assert(ptr !=
nullptr);
568 *ptr = yk.
Q(k, a, b, c, d);
579 std::cout << t.lap_reading_str() << std::endl;
590 int k_cut,
bool print) {
591 static_assert(S == Symmetry::Qk);
592 IO::ChronoTimer t(
"fill");
598 const auto tmp_max_k =
602 (k_cut <= 0) ? tmp_max_k : std::min(tmp_max_k, std::size_t(k_cut));
604 if (m_data.size() < max_k + 1)
605 m_data.resize(max_k + 1);
609 std::vector<std::size_t> count_non_zero_k(max_k + 1);
610#pragma omp parallel for
611 for (
auto k = 0ul; k <= max_k; ++k) {
612 const auto ik =
static_cast<int>(k);
613 for (
const auto &a : basis) {
614 for (
const auto &b : basis) {
615 for (
const auto &c : basis) {
618 for (
const auto &d : basis) {
620 if (NormalOrder(a, b, c, d) == CurrentOrder(a, b, c, d)) {
622 SelectionFunction(ik, a, b, c, d)) {
623 ++count_non_zero_k[k];
632 std::cout <<
"Count non-zero: " << t.lap_reading_str() << std::endl;
636#pragma omp parallel for
637 for (
auto ik = 0ul; ik <= max_k; ++ik) {
638 m_data[ik].reserve(count_non_zero_k[ik]);
641 std::cout <<
"Reserve: " << t.lap_reading_str() << std::endl;
645#pragma omp parallel for
646 for (
auto k = 0ul; k <= max_k; ++k) {
647 for (
const auto &a : basis) {
648 for (
const auto &c : basis) {
651 for (
const auto &b : basis) {
652 for (
const auto &d : basis) {
655 if (!SelectionFunction(
int(k), a, b, c, d))
657 const auto normal_index = NormalOrder(a, b, c, d);
658 if (normal_index == CurrentOrder(a, b, c, d)) {
659 add(
int(k), normal_index, 0.0);
667 std::cout <<
"Fill w/ zeros: " << t.lap_reading_str() << std::endl;
674 std::cout <<
"Fill w/ values: " << std::flush;
676#pragma omp parallel for collapse(2)
677 for (
auto ia = 0ul; ia < basis.size(); ++ia) {
678 for (
auto ib = 0ul; ib < basis.size(); ++ib) {
679 const auto &a = basis[ia];
680 const auto &b = basis[ib];
681 for (
const auto &c : basis) {
682 for (
const auto &d : basis) {
683 const auto normal_index = NormalOrder(a, b, c, d);
684 if (normal_index == CurrentOrder(a, b, c, d)) {
685 const auto [kmin, kmax] =
k_minmax_Q(a, b, c, d);
686 for (
int k = kmin; k <= kmax && k <= int(max_k); k += 2) {
687 if (!SelectionFunction(k, a, b, c, d))
689 double *ptr = get(k, normal_index);
690 assert(ptr !=
nullptr);
694 *ptr = yk.
Q(k, a, b, c, d);
704 std::cout << t.lap_reading_str() << std::endl;
715 IO::ChronoTimer t(
"fill");
737 (k_cut <= 0) ? tmp_max_k : std::min(tmp_max_k, std::size_t(k_cut));
739 if (m_data.size() < max_k + 1)
740 m_data.resize(max_k + 1);
744 std::vector<std::size_t> count_non_zero_k(max_k + 1);
745#pragma omp parallel for
746 for (
auto k = 0ul; k <= max_k; ++k) {
747 const auto ik =
static_cast<int>(k);
748 for (
const auto &a : basis) {
749 for (
const auto &b : basis) {
750 for (
const auto &c : basis) {
751 for (
const auto &d : basis) {
753 if (NormalOrder(a, b, c, d) == CurrentOrder(a, b, c, d)) {
754 if (Fk_SR(ik, a, b, c, d)) {
755 ++count_non_zero_k[k];
764 std::cout <<
"Count non-zero: " << t.lap_reading_str() << std::endl;
768#pragma omp parallel for
769 for (
auto ik = 0ul; ik <= max_k; ++ik) {
770 m_data[ik].reserve(count_non_zero_k[ik]);
773 std::cout <<
"Reserve: " << t.lap_reading_str() << std::endl;
777#pragma omp parallel for
778 for (
auto k = 0ul; k <= max_k; ++k) {
779 for (
const auto &a : basis) {
780 for (
const auto &b : basis) {
781 for (
const auto &c : basis) {
782 for (
const auto &d : basis) {
783 if (Fk_SR(
int(k), a, b, c, d)) {
784 const auto normal_index = NormalOrder(a, b, c, d);
785 if (normal_index == CurrentOrder(a, b, c, d)) {
786 add(
int(k), normal_index, 0.0);
795 std::cout <<
"Fill w/ zeros: " << t.lap_reading_str() << std::endl;
802 std::cout <<
"Fill w/ values: " << std::flush;
805#pragma omp parallel for collapse(2)
806 for (std::size_t ia = 0; ia < basis.size(); ++ia) {
807 for (std::size_t ib = 0; ib < basis.size(); ++ib) {
808 const auto &a = basis[ia];
809 const auto &b = basis[ib];
810 for (
const auto &c : basis) {
811 for (
const auto &d : basis) {
812 const auto normal_index = NormalOrder(a, b, c, d);
813 if (normal_index == CurrentOrder(a, b, c, d)) {
814 for (
int k = 0; k <= int(max_k); ++k) {
815 if (Fk_SR(k, a, b, c, d)) {
816 double *ptr = get(k, normal_index);
817 assert(ptr !=
nullptr);
820 *ptr = Fk(k, a, b, c, d);
831 std::cout << t.lap_reading_str() << std::endl;
840 if (fname ==
"false")
842 std::cout <<
"Writing " << count() <<
" integrals to file: " << fname <<
".."
845 const auto rw = IO::FRW::write;
846 IO::FRW::open_binary(f, fname, rw);
848 auto size = m_data.size();
849 rw_binary(f, rw, size);
850 for (
const auto &Q_k : m_data) {
851 auto size_k = Q_k.size();
852 rw_binary(f, rw, size_k);
853 for (
auto [key, value] : Q_k) {
855 rw_binary(f, rw, key_copy, value);
858 std::cout <<
"\n" << std::flush;
864 IO::ChronoTimer t(
"read");
865 if (fname ==
"false")
868 std::cout <<
"Reading: " << fname <<
"\n" << std::flush;
870 const auto rw = IO::FRW::read;
871 IO::FRW::open_binary(f, fname, rw);
875 std::vector<int> max_n_l;
876 constexpr bool count_orbs =
true;
882 rw_binary(f, rw, size);
884 if (m_data.size() == 0)
886 for (std::size_t i = 0; i < m_data.size(); ++i) {
887 auto &Q_k = m_data[i];
888 std::size_t size_k{0};
889 rw_binary(f, rw, size_k);
891 for (std::size_t ik = 0; ik < size_k; ++ik) {
894 rw_binary(f, rw, key, value);
898 if constexpr (count_orbs) {
899 const auto indexes = UnFormIndex(key);
900 for (
const auto index : indexes) {
901 const auto [n, ki] = Angular::nkindex_to_n_kindex(
int(index));
903 if (max_n_l.size() < l + 1)
904 max_n_l.resize(l + 1);
905 if (n > max_n_l.at(l))
915 std::string basis_str;
916 for (
const auto &n : max_n_l) {
918 basis_str += std::to_string(n);
923 if (!basis_str.empty())
924 basis_str =
"[" + basis_str +
"] ";
926 std::cout <<
"Read " << count() <<
" integrals " << basis_str
927 <<
"from file: " << fname <<
"\n"
Lookup table for Wigner 6J symbols.
Definition SixJTable.hpp:24
double get_2(int a, int b, int c, int d, int e, int f) const
Return 6j symbol {a/2,b/2,c/2,d/2,e/2,f/2}. Note: takes in 2*j/2*k as int.
Definition SixJTable.hpp:53
double get(const A &a, const B &b, const C &c, const D &d, const E &e, const F &f) const
Return "magic" 6j. Pass in either integer (for k), or DiracSpinor, F. e.g.: (F,F,k,...
Definition SixJTable.hpp:65
Base (pure virtual) class to store Coulomb integrals, and similar. 3 derived classes,...
Definition QkTable.hpp:57
Real P2(int k, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d, const Angular::SixJTable &sj, const std::vector< double > &fk) const
'Exchange-only', defined via W = Q + P. Optionally, takes pointer to 6J table (faster eval of 6J symb...
Definition QkTable.ipp:230
double * get(int k, nk4Index index)
Directly gets one of the stored elements, given normal-ordered nk4Index.
Definition QkTable.ipp:116
void fill_if(const std::vector< DiracSpinor > &basis, const YkTable &yk, const SelectionRules &SelectionFunction, int k_cut=-1, bool print=true)
Takes a constructed YkTable, and fills Coulomb table with all possible non-zero Qk Coulomb integrals ...
Definition QkTable.ipp:587
Real P(int k, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d, const Angular::SixJTable *const sj=nullptr) const
'Exchange-only', defined via W = Q + P. Optionally, takes pointer to 6J table (faster eval of 6J symb...
Definition QkTable.ipp:221
bool read(const std::string &fname)
Reads coulomb integrals to disk. Returns false if none read in.
Definition QkTable.ipp:863
void update(int k, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d, Real value)
Updates value in table. If not present, adds new value.
Definition QkTable.ipp:72
bool contains(int k, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d) const
Checks if given {k,a,b,c,d} is in the table.
Definition QkTable.ipp:98
nk4Index NormalOrder(const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d) const
Creates single 'nk4Index' corresponding to 'NormalOrder' symmetry of {a,b,c,d}.
Definition QkTable.ipp:395
nk4Index CurrentOrder(const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d) const
Creates single 'nk4Index', WITHOUT accounting for 'NormalOrder'. Can be used to check if {a,...
Definition QkTable.ipp:408
static nk4Index FormIndex(nkIndex a, nkIndex b, nkIndex c, nkIndex d)
Converts given set of nkIndex's (in any order) to nk4Index.
Definition QkTable.ipp:417
Real R(int k, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d) const
Returns 'R', defined via: R := Q / (angular_coef)
Definition QkTable.ipp:179
std::array< nkIndex, 4 > UnFormIndex(const nk4Index &index) const
Breaks nk4Index back into {ia,ib,ic,id}. Not used.
Definition QkTable.ipp:440
void fill(const std::vector< DiracSpinor > &basis, const YkTable &yk, int k_cut=-1, bool print=true)
Takes a constructed YkTable, and fills Coulomb table with all possible non-zero Qk Coulomb integrals,...
Definition QkTable.ipp:456
void write(const std::string &fname) const
Writes coulomb integrals to disk.
Definition QkTable.ipp:839
void summary() const
For testing: prints details of coulomb integrals stored.
Definition QkTable.ipp:16
Real g(const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d, int tma, int tmb, int tmc, int tmd) const
Returns 'g_abcd'.
Definition QkTable.ipp:272
void add(int k, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d, Real value)
adds a new value. Note: does nothing if {k,a,b,c,d} already exists
Definition QkTable.ipp:46
Real Q(int k, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d) const
Retrieve a stored Q. If not present, returns 0. (Returns exactly as stored in table....
Definition QkTable.ipp:142
Real W(int k, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d, const Angular::SixJTable *const sj=nullptr) const
W^k_abcd = Q^k_abcd + \sum_l [k] 6j * Q^l_abdc. Optionally, takes pointer to 6J table (faster eval of...
Definition QkTable.ipp:258
std::size_t count() const
Returns number of stored integrals.
Definition QkTable.ipp:30
Calculates + stores Hartree Y functions + Angular (w/ look-up), taking advantage of symmetry.
Definition YkTable.hpp:26
double Q(const int k, const DiracSpinor &Fa, const DiracSpinor &Fb, const DiracSpinor &Fc, const DiracSpinor &Fd) const
Calculates Qk using the existing yk integrals. Note: Yk and Ck tables must include all required value...
Definition YkTable.cpp:127
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:41
static int max_tj(const std::vector< DiracSpinor > &orbs)
Returns maximum (2j) found in {orbs}.
Definition DiracSpinor.cpp:266
Index nk_index() const
(n,kappa) index (see AtomData)
Definition DiracSpinor.hpp:100
int nkindex_to_kappa(int index)
Returns kappa, given nk_index.
Definition Wigner369j.hpp:126
double threej_2(int two_j1, int two_j2, int two_j3, int two_m1, int two_m2, int two_m3)
Calculates wigner 3j symbol. Takes in 2*j (or 2*l) - intput is integer.
Definition Wigner369j.hpp:185
double sixj_2(int two_j1, int two_j2, int two_j3, int two_j4, int two_j5, int two_j6)
6j symbol {j1 j2 j3 \ j4 j5 j6} - [takes 2*j as int]
Definition Wigner369j.hpp:275
constexpr int lFromIndex(int i)
returns l, given kappa_index
Definition Wigner369j.hpp:90
constexpr int neg1pow(int a)
Evaluates (-1)^{a} (for integer a)
Definition Wigner369j.hpp:158
double tildeCk_kk(int k, int ka, int kb)
tildeCk_kk = (-1)^{ja+1/2}*Ck_kk
Definition Wigner369j.hpp:341
std::pair< int, int > index_to_nk(int index)
return {n, kappa} given nk_index:
Definition Wigner369j.hpp:109
bool Ck_kk_SR(int k, int ka, int kb)
Ck selection rule only. Returns false if C^k=0, true if non-zero.
Definition Wigner369j.hpp:332
constexpr int neg1pow_2(int two_a)
Evaluates (-1)^{two_a/2} (for integer a; two_a is even)
Definition Wigner369j.hpp:160
constexpr int triangle(int j1, int j2, int J)
Returns 1 if triangle rule is satisfied. nb: works with j OR twoj!
Definition Wigner369j.hpp:170
constexpr int twoj_k(int ka)
returns 2j given kappa
Definition Wigner369j.hpp:45
std::string l_symbol(int l)
l (int) to symbol (e.g., 0->'s', 1->'p')
Definition AtomData.cpp:110
Functions (+classes) for computing Coulomb integrals.
Definition CoulombIntegrals.cpp:13
double Real
Data type used to store integrals.
Definition QkTable.hpp:15
std::function< bool(int, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d)> SelectionRules
Function type for determining Coulomb(-like) integral selection rules. Takes k and 4 DiracSpinors,...
Definition QkTable.hpp:33
uint64_t nk4Index
index type for set of 4 orbitals {nk,nk,nk,nk} -> nk4Index
Definition QkTable.hpp:17
double Pk_abcd(const int k, const DiracSpinor &Fa, const DiracSpinor &Fb, const DiracSpinor &Fc, const DiracSpinor &Fd)
Exchange only version of W (W-Q): W = Q + P [see Qk above].
Definition CoulombIntegrals.cpp:428
uint16_t nkIndex
index type for each {nk} (orbital)
Definition QkTable.hpp:19
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:556
std::function< double(int, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d)> CoulombFunction
Function type for calculating Coulomb(-like) integrals. Takes k and 4 DiracSpinors,...
Definition QkTable.hpp:27
void add(std::vector< T > *first, const std::vector< T > &second, const Args &...rest)
Adds any number of vectors, in place (modifies first vector). Must be of same type....
Definition Vector.hpp:106