2#include "CoulombIntegrals.hpp"
3#include "IO/ChronoTimer.hpp"
4#include "IO/FRW_fileReadWrite.hpp"
16 std::cout <<
"Summary: \n";
19 for (
auto &qk : m_data) {
20 std::cout <<
"k=" << k <<
": " << qk.size() <<
" [" << qk.bucket_count()
25 std::cout <<
"Total: " << total <<
" non-zero integrals\n";
31 for (
auto &qk : m_data) {
41 add(k, NormalOrder(a, b, c, d), value);
53 const auto sk = std::size_t(k);
54 if (sk >= m_data.size()) {
55 m_data.resize(sk + 1);
57 m_data.at(sk).insert({index, value});
65 update(k, NormalOrder(a, b, c, d), value);
80 const auto sk = std::size_t(k);
81 if (sk >= m_data.size()) {
82 m_data.resize(sk + 1);
84 m_data.at(sk).insert_or_assign(index, value);
90 const auto sk = std::size_t(k);
91 if (sk >= m_data.size())
93 return m_data.at(sk).find(NormalOrder(a, b, c, d)) != m_data.at(sk).cend();
105 const auto sk = std::size_t(k);
106 if (sk >= m_data.size())
108 return m_data.at(sk).find(index) != m_data.at(sk).cend();
116 const auto sk = std::size_t(k);
117 if (sk >= m_data.size())
120 const auto map_it = m_data.at(sk).find(index);
121 if (map_it == m_data.at(sk).cend())
123 return &(map_it->second);
130 const auto sk = std::size_t(k);
131 if (sk >= m_data.size())
134 const auto map_it = m_data.at(sk).find(NormalOrder(a, b, c, d));
135 if (map_it == m_data.at(sk).cend())
137 return map_it->second;
148 const auto sk = std::size_t(k);
149 if (sk >= m_data.size())
152 const auto map_it = m_data.at(sk).find(index);
153 if (map_it == m_data.at(sk).cend())
155 return map_it->second;
162 const auto tQk = Q(k, a, b, c, d);
174 return tQk / (s * tCkac * tCkbd);
204 const auto [lmin, lmax] =
k_minmax_Q(ka, kb, kd, kc);
205 for (
int l = lmin; l <= lmax; l += 2) {
206 const auto ql = this->Q(l, a, b, d, c);
209 const auto sixj = sj ? sj->
get_2(tja, tjc, 2 * k, tjb, tjd, 2 * l) :
232 const std::vector<double> &fk)
const {
236 if (Coulomb::triangle(a, c, k) == 0 || Coulomb::triangle(k, b, d) == 0)
239 const auto [lmin, lmax] =
k_minmax_Q(a, b, d, c);
240 for (
int l = lmin; l <= lmax; l += 2) {
241 const auto ql = this->Q(l, a, b, d, c);
244 const auto sixj = sj.
get(a, c, k, b, d, l);
247 const auto f_scr_l = (l < (int)fk.size()) ? fk[std::size_t(l)] : 1.0;
249 Pk_abcd += f_scr_l * sixj * ql;
266 return Q(k, a, b, c, d) + P(k, a, b, c, d, sj);
273 int tmb,
int tmc,
int tmd)
const {
275 if (tmc - tma != tmb - tmd)
277 const int twoq = tmc - tma;
282 for (
int k = k0; k <= ki; k += 2) {
283 if (std::abs(twoq) > 2 * k)
299 g += s * tjs1 * tjs2 * Q(k, a, b, c, d);
331 nk4Index out = FormIndex(a, b, c, d);
332 const auto min = std::min({a, b, c, d});
334 const auto tmp = FormIndex(a, d, c, b);
339 const auto tmp = std::min(FormIndex(b, a, d, c), FormIndex(b, c, d, a));
344 const auto tmp = std::min(FormIndex(c, b, a, d), FormIndex(c, d, a, b));
349 const auto tmp = std::min(FormIndex(d, a, b, c), FormIndex(d, c, b, a));
360 const auto tmp1 = FormIndex(a, b, c, d);
361 const auto tmp2 = FormIndex(b, a, d, c);
362 const auto tmp3 = FormIndex(c, d, a, b);
363 const auto tmp4 = FormIndex(d, c, b, a);
364 return std::min({tmp1, tmp2, tmp3, tmp4});
372 const auto tmp1 = FormIndex(a, b, c, d);
373 const auto tmp2 = FormIndex(b, a, d, c);
374 return std::min({tmp1, tmp2});
382 return FormIndex(a, b, c, d);
389 return NormalOrder_impl(a, b, c, d);
403 return FormIndex(a, b, c, d);
431 static_assert(
sizeof(
nkIndex) * 8 == 16);
438std::array<nkIndex, 4>
440 static_assert(
sizeof(
nk4Index) ==
sizeof(std::array<nkIndex, 4>));
441 std::array<nkIndex, 4> set;
442 std::memcpy(&set, &index,
sizeof(
nk4Index));
447 std::swap(set[0], set[3]);
448 std::swap(set[1], set[2]);
456 const YkTable &yk,
int k_cut,
bool print) {
457 static_assert(S == Symmetry::Qk);
458 IO::ChronoTimer t(
"fill");
476 const auto tmp_max_k =
480 (k_cut <= 0) ? tmp_max_k : std::min(tmp_max_k, std::size_t(k_cut));
482 if (m_data.size() < max_k + 1)
483 m_data.resize(max_k + 1);
487 std::vector<std::size_t> count_non_zero_k(max_k + 1);
488#pragma omp parallel for
489 for (
auto k = 0ul; k <= max_k; ++k) {
490 const auto ik =
static_cast<int>(k);
491 for (
const auto &a : basis) {
492 for (
const auto &b : basis) {
493 for (
const auto &c : basis) {
496 for (
const auto &d : basis) {
498 if (NormalOrder(a, b, c, d) == CurrentOrder(a, b, c, d)) {
500 ++count_non_zero_k[k];
509 std::cout <<
"Count non-zero: " << t.lap_reading_str() << std::endl;
513#pragma omp parallel for
514 for (
auto ik = 0ul; ik <= max_k; ++ik) {
515 m_data[ik].reserve(count_non_zero_k[ik]);
518 std::cout <<
"Reserve: " << t.lap_reading_str() << std::endl;
522#pragma omp parallel for
523 for (
auto k = 0ul; k <= max_k; ++k) {
524 for (
const auto &a : basis) {
525 for (
const auto &c : basis) {
528 for (
const auto &b : basis) {
529 for (
const auto &d : basis) {
532 const auto normal_index = NormalOrder(a, b, c, d);
533 if (normal_index == CurrentOrder(a, b, c, d)) {
534 add(
int(k), normal_index, 0.0);
542 std::cout <<
"Fill w/ zeros: " << t.lap_reading_str() << std::endl;
549 std::cout <<
"Fill w/ values: " << std::flush;
551#pragma omp parallel for collapse(2)
552 for (
auto ia = 0ul; ia < basis.size(); ++ia) {
553 for (
auto ib = 0ul; ib < basis.size(); ++ib) {
554 const auto &a = basis[ia];
555 const auto &b = basis[ib];
556 for (
const auto &c : basis) {
557 for (
const auto &d : basis) {
558 const auto normal_index = NormalOrder(a, b, c, d);
559 if (normal_index == CurrentOrder(a, b, c, d)) {
560 const auto [kmin, kmax] =
k_minmax_Q(a, b, c, d);
561 for (
int k = kmin; k <= kmax && k <= int(max_k); k += 2) {
562 double *ptr = get(k, normal_index);
563 assert(ptr !=
nullptr);
567 *ptr = yk.
Q(k, a, b, c, d);
578 std::cout << t.lap_reading_str() << std::endl;
589 int k_cut,
bool print) {
590 static_assert(S == Symmetry::Qk);
591 IO::ChronoTimer t(
"fill");
597 const auto tmp_max_k =
601 (k_cut <= 0) ? tmp_max_k : std::min(tmp_max_k, std::size_t(k_cut));
603 if (m_data.size() < max_k + 1)
604 m_data.resize(max_k + 1);
608 std::vector<std::size_t> count_non_zero_k(max_k + 1);
609#pragma omp parallel for
610 for (
auto k = 0ul; k <= max_k; ++k) {
611 const auto ik =
static_cast<int>(k);
612 for (
const auto &a : basis) {
613 for (
const auto &b : basis) {
614 for (
const auto &c : basis) {
617 for (
const auto &d : basis) {
619 if (NormalOrder(a, b, c, d) == CurrentOrder(a, b, c, d)) {
621 SelectionFunction(ik, a, b, c, d)) {
622 ++count_non_zero_k[k];
631 std::cout <<
"Count non-zero: " << t.lap_reading_str() << std::endl;
635#pragma omp parallel for
636 for (
auto ik = 0ul; ik <= max_k; ++ik) {
637 m_data[ik].reserve(count_non_zero_k[ik]);
640 std::cout <<
"Reserve: " << t.lap_reading_str() << std::endl;
644#pragma omp parallel for
645 for (
auto k = 0ul; k <= max_k; ++k) {
646 for (
const auto &a : basis) {
647 for (
const auto &c : basis) {
650 for (
const auto &b : basis) {
651 for (
const auto &d : basis) {
654 if (!SelectionFunction(
int(k), a, b, c, d))
656 const auto normal_index = NormalOrder(a, b, c, d);
657 if (normal_index == CurrentOrder(a, b, c, d)) {
658 add(
int(k), normal_index, 0.0);
666 std::cout <<
"Fill w/ zeros: " << t.lap_reading_str() << std::endl;
673 std::cout <<
"Fill w/ values: " << std::flush;
675#pragma omp parallel for collapse(2)
676 for (
auto ia = 0ul; ia < basis.size(); ++ia) {
677 for (
auto ib = 0ul; ib < basis.size(); ++ib) {
678 const auto &a = basis[ia];
679 const auto &b = basis[ib];
680 for (
const auto &c : basis) {
681 for (
const auto &d : basis) {
682 const auto normal_index = NormalOrder(a, b, c, d);
683 if (normal_index == CurrentOrder(a, b, c, d)) {
684 const auto [kmin, kmax] =
k_minmax_Q(a, b, c, d);
685 for (
int k = kmin; k <= kmax && k <= int(max_k); k += 2) {
686 if (!SelectionFunction(k, a, b, c, d))
688 double *ptr = get(k, normal_index);
689 assert(ptr !=
nullptr);
693 *ptr = yk.
Q(k, a, b, c, d);
703 std::cout << t.lap_reading_str() << std::endl;
714 IO::ChronoTimer t(
"fill");
736 (k_cut <= 0) ? tmp_max_k : std::min(tmp_max_k, std::size_t(k_cut));
738 if (m_data.size() < max_k + 1)
739 m_data.resize(max_k + 1);
743 std::vector<std::size_t> count_non_zero_k(max_k + 1);
744#pragma omp parallel for
745 for (
auto k = 0ul; k <= max_k; ++k) {
746 const auto ik =
static_cast<int>(k);
747 for (
const auto &a : basis) {
748 for (
const auto &b : basis) {
749 for (
const auto &c : basis) {
750 for (
const auto &d : basis) {
752 if (NormalOrder(a, b, c, d) == CurrentOrder(a, b, c, d)) {
753 if (Fk_SR(ik, a, b, c, d)) {
754 ++count_non_zero_k[k];
763 std::cout <<
"Count non-zero: " << t.lap_reading_str() << std::endl;
767#pragma omp parallel for
768 for (
auto ik = 0ul; ik <= max_k; ++ik) {
769 m_data[ik].reserve(count_non_zero_k[ik]);
772 std::cout <<
"Reserve: " << t.lap_reading_str() << std::endl;
776#pragma omp parallel for
777 for (
auto k = 0ul; k <= max_k; ++k) {
778 for (
const auto &a : basis) {
779 for (
const auto &b : basis) {
780 for (
const auto &c : basis) {
781 for (
const auto &d : basis) {
782 if (Fk_SR(
int(k), a, b, c, d)) {
783 const auto normal_index = NormalOrder(a, b, c, d);
784 if (normal_index == CurrentOrder(a, b, c, d)) {
785 add(
int(k), normal_index, 0.0);
794 std::cout <<
"Fill w/ zeros: " << t.lap_reading_str() << std::endl;
801 std::cout <<
"Fill w/ values: " << std::flush;
804#pragma omp parallel for collapse(2)
805 for (std::size_t ia = 0; ia < basis.size(); ++ia) {
806 for (std::size_t ib = 0; ib < basis.size(); ++ib) {
807 const auto &a = basis[ia];
808 const auto &b = basis[ib];
809 for (
const auto &c : basis) {
810 for (
const auto &d : basis) {
811 const auto normal_index = NormalOrder(a, b, c, d);
812 if (normal_index == CurrentOrder(a, b, c, d)) {
813 for (
int k = 0; k <= int(max_k); ++k) {
814 if (Fk_SR(k, a, b, c, d)) {
815 double *ptr = get(k, normal_index);
816 assert(ptr !=
nullptr);
819 *ptr = Fk(k, a, b, c, d);
830 std::cout << t.lap_reading_str() << std::endl;
839 if (fname ==
"false")
841 std::cout <<
"Writing " << count() <<
" integrals to file: " << fname <<
".."
844 const auto rw = IO::FRW::write;
845 IO::FRW::open_binary(f, fname, rw);
847 auto size = m_data.size();
848 rw_binary(f, rw, size);
849 for (
const auto &Q_k : m_data) {
850 auto size_k = Q_k.size();
851 rw_binary(f, rw, size_k);
852 for (
auto [key, value] : Q_k) {
854 rw_binary(f, rw, key_copy, value);
857 std::cout <<
"\n" << std::flush;
863 if (fname ==
"false")
866 const auto rw = IO::FRW::read;
867 IO::FRW::open_binary(f, fname, rw);
873 rw_binary(f, rw, size);
875 if (m_data.size() == 0)
877 for (std::size_t i = 0; i < m_data.size(); ++i) {
878 auto &Q_k = m_data[i];
879 std::size_t size_k{0};
880 rw_binary(f, rw, size_k);
882 for (std::size_t ik = 0; ik < size_k; ++ik) {
885 rw_binary(f, rw, key, value);
889 std::cout <<
"Read " << count() <<
" integrals 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:229
double * get(int k, nk4Index index)
Directly gets one of the stored elements, given normal-ordered nk4Index.
Definition QkTable.ipp:115
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:586
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:220
bool read(const std::string &fname)
Reads coulomb integrals to disk. Returns false if none read in.
Definition QkTable.ipp:862
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:71
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:97
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:394
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:407
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:416
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:178
std::array< nkIndex, 4 > UnFormIndex(const nk4Index &index) const
Breaks nk4Index back into {ia,ib,ic,id}. Not used.
Definition QkTable.ipp:439
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:455
void write(const std::string &fname) const
Writes coulomb integrals to disk.
Definition QkTable.ipp:838
void summary() const
For testing: prints details of coulomb integrals stored.
Definition QkTable.ipp:15
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:271
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:45
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:141
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:257
std::size_t count() const
Returns number of stored integrals.
Definition QkTable.ipp:29
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:97
int nkindex_to_kappa(int index)
Returns kappa, given nk_index.
Definition Wigner369j.hpp:87
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:146
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:236
constexpr int neg1pow(int a)
Evaluates (-1)^{a} (for integer a)
Definition Wigner369j.hpp:119
double tildeCk_kk(int k, int ka, int kb)
tildeCk_kk = (-1)^{ja+1/2}*Ck_kk
Definition Wigner369j.hpp:302
std::pair< int, int > index_to_nk(int index)
return {n, kappa} given nk_index:
Definition Wigner369j.hpp:78
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:293
constexpr int neg1pow_2(int two_a)
Evaluates (-1)^{two_a/2} (for integer a; two_a is even)
Definition Wigner369j.hpp:121
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:131
constexpr int twoj_k(int ka)
returns 2j given kappa
Definition Wigner369j.hpp:14
Functions (+classes) for computing Coulomb integrals.
Definition Coulomb.hpp:8
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