High-precision calculations for one- and two-valence atomic systems
QkTable.hpp
1#pragma once
2#include "Angular/SixJTable.hpp"
3#include "Wavefunction/DiracSpinor.hpp"
4#include "YkTable.hpp"
5#include <array>
6#include <cstdint>
7#include <functional>
8#include <string_view>
9// #define COULOMB_USE_STD_MAP
10#ifdef COULOMB_USE_STD_MAP
11#include <unordered_map>
12#else
13#include "ankerl/unordered_dense.h"
14#endif
15
16namespace Coulomb {
17
18//! Symmetry (state index order) for tables.
19enum class Symmetry { Qk, Wk, Lk, none };
20
21//! Convert Symmetry to string
22inline std::string_view to_string(Symmetry s) {
23 switch (s) {
24 case Symmetry::Qk:
25 return "Qk";
26 case Symmetry::Wk:
27 return "Wk";
28 case Symmetry::Lk:
29 return "Lk";
30 case Symmetry::none:
31 return "none";
32 }
33 return "";
34}
35
36//! Data type used to store integrals
37using Real = double;
38
39//! index type for set of 4 orbitals {nk,nk,nk,nk} -> nk4Index [max n: 256]
40//! @details @warning Requires 0<n<256, see \ref Angular::nk_to_index
41using nk4Index = uint64_t;
42
43//! index type for each {nk} (orbital) [max n: 256]
44//! @details @warning Requires 0<n<256, see \ref Angular::nk_to_index
45using nkIndex = uint16_t;
46
47static_assert(sizeof(nkIndex) == sizeof(DiracSpinor::Index));
48
49//! Hashmap type used to store integrals (one per k).
50//! @details Defaults to ankerl::unordered_dense::map (a fast, densely-stored,
51//! open-addressing hashmap). Define COULOMB_USE_STD_MAP at compile time to fall
52//! back to std::unordered_map. The two share the API used here, so swapping is
53//! transparent to the rest of the code.
54#ifdef COULOMB_USE_STD_MAP
55using QkMap = std::unordered_map<nk4Index, Real>;
56#else
57using QkMap = ankerl::unordered_dense::map<nk4Index, Real>;
58#endif
59
60//! Function type for calculating Coulomb(-like) integrals.
61//! Takes k and 4 DiracSpinors, returns a double
63 std::function<double(int, const DiracSpinor &a, const DiracSpinor &b,
64 const DiracSpinor &c, const DiracSpinor &d)>;
65
66//! Function type for determining Coulomb(-like) integral selection rules.
67//! Takes k and 4 DiracSpinors, returns a true if integral is allowed.
69 std::function<bool(int, const DiracSpinor &a, const DiracSpinor &b,
70 const DiracSpinor &c, const DiracSpinor &d)>;
71
72//==============================================================================
73/*!
74 @brief
75 Base class template to store Coulomb integrals, and similar.
76 3 specific cases (by template instantiation), account for specific symmetrs.
77
78 @details
79 Mostly, a wrapper for std::unordered_map.
80 Stores data in a std::vector of maps, each element (map) for each k
81 (multipolarity).
82 The symmetry is taken into account by defining a "Normal Ordering" of set of
83 orbitals {a,b,c,d}, which is defined as the "smallest" arrangment of equivilant indices. The details depend on the specific symmetry in question:
84
85 ### Qk symmetry:
86
87 - {abcd} = cbad = adcb = cdab = badc = bcda = dabc = dcba.
88 - Normal ordering: "smallest" of above 8 options
89 - [ Also assumes Coulomb selection rules, for @ref fill(), and @ref P(), @ref W() ]
90
91 ### Wk symmetry (same as g):
92
93 - {abcd} = badc = cdab = dcba
94
95 ### Lk symmetry:
96
97 - {abcd} = badc
98
99 ### Nk symmetry:
100
101 - No symmetry assumed; each integral treated as unique.
102
103 @note Hashtable lookup is significant bottleneck. Should look into better hashmaps.
104
105 @warning
106 Requires 0<n<256, see \ref Angular::nk_to_index
107
108 @note The internal summations in @ref P(), @ref P2(), @ref W(), and @ref g()
109 depend on the selection rules, and @p S:
110 for @ref Symmetry::Qk, we assume Coulomb angular selection rules,
111 including parity rule, (via @ref k_minmax_Q);
112 for all other symmetries no explicit selection rule is assumed.
113 The @ref fill() and @ref fill_if() functions also make this assumption;
114 these functions are only available for the @ref Symmetry::Qk symmetry, and
115 in that case, assume the regular Coulomb selection rules.
116*/
117template <Symmetry S>
119
120private:
121 // each vector element corresponds to a 'k'
122 std::vector<QkMap> m_data{};
123
124public:
125 // 'Rule of zero' (except virtual destructor)
126 // virtual ~CoulombTable() = default;
127
128 /*!
129 @brief Fill table with all non-zero Coulomb Q integrals from a YkTable.
130 @details
131 Fills the table with Q^k_abcd for all orbitals in @p basis, exploiting
132 the 8-fold Qk symmetry to avoid redundant calculations. Uses a precomputed
133 @p yk for the radial integrals.
134
135 Filling proceeds in four stages:
136 1. Count non-zero integrals per k (for map reservation)
137 2. Reserve map capacity
138 3. Initialise all entries to zero (parallelised over k)
139 4. Fill values in parallel (safe since no new map insertions occur)
140
141 @param basis Basis set of orbitals.
142 @param yk Precomputed Yk table for radial integrals.
143 @param k_cut Maximum multipolarity k. Set to <=0 for no cut-off.
144 @param print If true, prints timing and summary.
145
146 @note Only valid for QkTable ( @ref Symmetry::Qk ) (enforced via static_assert).
147 @warning Does not update existing entries; only adds new ones.
148 */
149 void fill(const std::vector<DiracSpinor> &basis, const YkTable &yk,
150 int k_cut = -1, bool print = true);
151
152 /*!
153 @brief Fill table with Coulomb Q integrals satisfying a selection rule.
154 @details
155 Same as default @ref fill(basis, yk, k_cut, print), but only computes and stores
156 integrals for which @p SelectionFunction returns true. Useful for
157 restricting to a particular subset of integrals (e.g., CI- or MBPT-relevant).
158
159 Uses the same four-stage filling strategy as fill(basis, yk, ...).
160
161 @param basis Basis set of orbitals.
162 @param yk Precomputed Yk table for radial integrals.
163 @param SelectionFunction Returns true for integrals to include.
164 @param k_cut Maximum multipolarity k. Set to <=0 for no cut-off.
165 @param print If true, prints timing and summary.
166
167 @note Only valid for QkTable ( @ref Symmetry::Qk ) (enforced via static_assert).
168 @warning Does not update existing entries; only adds new ones.
169 */
170 void fill_if(const std::vector<DiracSpinor> &basis, const YkTable &yk,
171 const SelectionRules &SelectionFunction, int k_cut = -1,
172 bool print = true);
173
174 /*!
175 @brief Fill table using a general CoulombFunction and selection rules.
176 @details
177 Fills the table with Fk(k,a,b,c,d) for all orbitals in @p basis,
178 restricted to entries for which @p Fk_SR returns true, accounting for
179 the symmetry of the table. Unlike the YkTable overloads, this version
180 is general-purpose and does not assume Qk angular selection rules.
181
182 Uses the same four-stage filling strategy as fill(basis, yk, ...).
183
184 @param basis Basis set of orbitals.
185 @param Fk Function to compute the integral value.
186 @param Fk_SR Selection rule; only integrals passing this are stored.
187 @param k_cut Maximum multipolarity k. Set to <=0 for no cut-off.
188 @param print If true, prints timing and summary.
189
190 @warning Does not update existing entries; only adds new ones.
191 */
192 void fill(const std::vector<DiracSpinor> &basis, const CoulombFunction &Fk,
193 const SelectionRules &Fk_SR, int k_cut = -1, bool print = true);
194
195 /*!
196 @brief Re-calculate all existing table entries using a CoulombFunction.
197 @details
198 Updates all integrals currently stored in the table by re-evaluating
199 Fk(k,a,b,c,d) using the provided @ref CoulombFunction() for each entry.
200 Unlike @ref fill(), this recalculates all
201 existing entries and does not add new ones. Useful for iterating.
202
203 @warning Note: does not calculate any new integrals;
204 only re-calculates existing ones.
205 Table must have corect size/shape before calling.
206
207 Damping: With \f$\eta = \text{damp}\f$, integrals are updated as:
208 \f[
209 Q^k_{abcd} \to \eta Q^k_{abcd} + (1-\eta) Fk(k,a,b,c,d)
210 \f]
211 where @p Fk is a @ref CoulombFunction. \f$\eta=0\f$ means no damping.
212 Must be between 0 and 1 (0 is allowed, 1 is not).
213
214 @param basis Basis set of orbitals.
215 @param Fk Function to compute the integral value.
216 @param damp Damping factor, \f$\eta \in [0,1)\f$. 0 means no damping.
217 @param print If true, prints a progress bar.
218 @param filter Optional. If set, entries for which filter(a,b,c,d) returns
219 false are left unchanged (skipped before the k-loop, so no
220 lookup/recalculation is done for them). Empty => update all.
221 */
222 void update(const std::vector<DiracSpinor> &basis, const CoulombFunction &Fk,
223 double damp, bool print = true,
224 const std::function<bool(const DiracSpinor &, const DiracSpinor &,
225 const DiracSpinor &,
226 const DiracSpinor &)> &filter = {});
227
228 //! Gives arrow access to all underlying vector<unordered_map> functions
229 auto operator->() { return &m_data; }
230
231 //! For testing: prints details of coulomb integrals stored
232 void summary() const;
233
234 //! Returns number of stored integrals
235 std::size_t count() const;
236
237 //!
238 /*! Maximum k stored in table. If table is empty, returns '-1'
239 @details
240 @note Based on _size_ of map only; doesn't check for values.
241 May sometimes have a map with no non-zero integrals for a given k.
242 Guarenteed to be no integrals with k larger than this;
243 no guarentee that there are non-zero integrals at or below this k.
244 */
245 int max_k() const { return int(m_data.size()) - 1; }
246
247 //! adds a new value. Note: does nothing if {k,a,b,c,d} already exists
248 void add(int k, const DiracSpinor &a, const DiracSpinor &b,
249 const DiracSpinor &c, const DiracSpinor &d, Real value);
250 //! adds a new value. Note: does nothing if {k,a,b,c,d} already exists
251 void add(int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d, Real value);
252 //! adds a new value. Note: does nothing if {k,a,b,c,d} already exists
253 void add(int k, nk4Index index, Real value);
254
255 //! Updates value in table. If not present, adds new value
256 void update(int k, const DiracSpinor &a, const DiracSpinor &b,
257 const DiracSpinor &c, const DiracSpinor &d, Real value);
258 //! Updates value in table. If not present, adds new value
259 void update(int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d, Real value);
260 //! Updates value in table. If not present, adds new value
261 void update(int k, nk4Index index, Real value);
262
263 //! Checks if given {k,a,b,c,d} is in the table
264 bool contains(int k, const DiracSpinor &a, const DiracSpinor &b,
265 const DiracSpinor &c, const DiracSpinor &d) const;
266 //! Checks if given {k,a,b,c,d} is in the table
267 bool contains(int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d) const;
268 //! Checks if given {k,a,b,c,d} is in the table
269 bool contains(int k, nk4Index index) const;
270
271 //! Returns true if table is empty
272 bool emptyQ() const {
273 return (m_data.size() == 0);
274 // nb: possible to be empty if size > 0... (but doesn't matter..)
275 }
276
277 /*! Counts number of non-zero Coulomb integrals, accounting for symmetry
278 @details
279
280 @note Specifically assumes Qk selection rules, and therefore only works for Qk.
281
282 In theory, easily updated for general symmetry/selection rules,
283 but this is usually the bottle-neck!
284 */
285 std::array<std::size_t, 4>
286 count_non_zero_integrals(const std::vector<DiracSpinor> &basis,
287 std::size_t max_k = 99, double eF = 0.0) const;
288
289 //! Retrieve a stored Q. If not present, returns 0. (Returns exactly as
290 //! stored in table.)
291 Real Q(int k, const DiracSpinor &a, const DiracSpinor &b,
292 const DiracSpinor &c, const DiracSpinor &d) const;
293
294 //! Retrieve a stored Q. If not present, returns 0. (Returns exactly as
295 //! stored in table.)
296 Real Q(int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d) const;
297
298 //! Retrieve a stored Q. If not present, returns 0. (Returns exactly as
299 //! stored in table.)
300 Real Q(int k, nk4Index index) const;
301
302 //! Returns 'R', defined via: R := Q / (angular_coef)
303 Real R(int k, const DiracSpinor &a, const DiracSpinor &b,
304 const DiracSpinor &c, const DiracSpinor &d) const;
305
306 //! Returns 'R', defined via: R := Q / (angular_coef)
307 Real R(int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d) const;
308
309 /*!
310 @brief Exchange integral P^k_abcd = (2k+1) sum_l {6j} Q^l_abdc.
311 @details
312 Computes the exchange part of the antisymmetrised W = Q + P integral.
313 Optionally uses a precomputed 6J table for faster evaluation.
314 @note For @ref Symmetry::Qk, l is iterated using Coulomb angular selection
315 rule bounds (k_minmax_Q); for all other symmetries, l runs over
316 [0, max_k()]. See @ref CoulombTable.
317 */
318 Real P(int k, const DiracSpinor &a, const DiracSpinor &b,
319 const DiracSpinor &c, const DiracSpinor &d,
320 const Angular::SixJTable *const sj = nullptr) const;
321
322 /*!
323 @brief Exchange integral P^k_abcd = (2k+1) sum_l {6j} Q^l_abdc.
324 @details
325 As @ref P(int, const DiracSpinor&, ...) but takes @ref nkIndex arguments.
326 @note For @ref Symmetry::Qk, l is iterated using Coulomb angular selection
327 rule bounds (k_minmax_Q); for all other symmetries, l runs over
328 [0, max_k()]. See @ref CoulombTable.
329 */
330 Real P(int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d,
331 const Angular::SixJTable *const sj = nullptr) const;
332
333 /*!
334 @brief Exchange integral P^k_abcd with effective Coulomb screening.
335 @details
336 As @ref P(), but each l term is weighted by a screening factor fk[l].
337 @note For @ref Symmetry::Qk, l is iterated using Coulomb angular selection
338 rule bounds (k_minmax_Q); for all other symmetries, l runs over
339 [0, max_k()]. See @ref CoulombTable.
340 */
341 Real P2(int k, const DiracSpinor &a, const DiracSpinor &b,
342 const DiracSpinor &c, const DiracSpinor &d,
343 const Angular::SixJTable &sj, const std::vector<double> &fk) const;
344
345 /*!
346 @brief Antisymmetrised integral W^k_abcd = Q^k_abcd + P^k_abcd.
347 @details
348 Returns Q + P. Optionally uses a precomputed 6J table.
349 @note l iteration in P() depends on symmetry S; see @ref P() and @ref CoulombTable.
350 */
351 Real W(int k, const DiracSpinor &a, const DiracSpinor &b,
352 const DiracSpinor &c, const DiracSpinor &d,
353 const Angular::SixJTable *const sj = nullptr) const;
354
355 /*!
356 @brief Antisymmetrised integral W^k_abcd = Q^k_abcd + P^k_abcd.
357 @details
358 As @ref W(int, const DiracSpinor&, ...) but takes @ref nkIndex arguments.
359 @note l iteration in P() depends on symmetry S; see @ref P() and @ref CoulombTable.
360 */
361 Real W(int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d,
362 const Angular::SixJTable *const sj = nullptr) const;
363
364 /*!
365 @brief Full matrix element g_abcd with explicit magnetic quantum numbers.
366 @details
367 Sums over k, weighted by 3j symbols, using the stored Q^k values.
368 @note For @ref Symmetry::Qk, k is iterated using Coulomb angular selection
369 rule bounds (step 2); for all other symmetries, k runs over
370 [0, max_k()] with step 1. See @ref CoulombTable.
371 */
372 Real g(const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c,
373 const DiracSpinor &d, int tma, int tmb, int tmc, int tmd) const;
374
375 //! Creates single 'nk4Index' corresponding to 'NormalOrder' symmetry of
376 //! {a,b,c,d}
377 nk4Index NormalOrder(const DiracSpinor &a, const DiracSpinor &b,
378 const DiracSpinor &c, const DiracSpinor &d) const;
379 //! Creates single 'nk4Index' corresponding to 'NormalOrder' symmetry of
380 //! {a,b,c,d}
382
383 //! Checks if set {a,b,c,d} are already in NormalOrder
385 return NormalOrder(a, b, c, d) == CurrentOrder(a, b, c, d);
386 }
387 //! Checks if set {a,b,c,d} are already in NormalOrder
388 bool is_NormalOrdered(const DiracSpinor &a, const DiracSpinor &b,
389 const DiracSpinor &c, const DiracSpinor &d) const {
390 return NormalOrder(a, b, c, d) == CurrentOrder(a, b, c, d);
391 }
392
393 //! Writes coulomb integrals to disk
394 void write(const std::string &fname, bool verbose = true) const;
395 //! Reads coulomb integrals to disk. Returns false if none read in.
396 //! If parse_basis_string is true, decodes every key to determine (and print) the
397 //! basis string [e.g., 30spdf] - adds noticable overhead, purely aesthetic.
398 bool read(const std::string &fname, bool verbose = true,
399 bool parse_basis_string = false);
400
401 //! Directly gets one of the stored elements, given normal-ordered nk4Index
402 double *get(int k, nk4Index index);
403
404 //! Creates single 'nk4Index', WITHOUT accounting for 'NormalOrder'. Can be
405 //! used to check if {a,b,c,d} are already in 'NormalOrder'
406 nk4Index CurrentOrder(const DiracSpinor &a, const DiracSpinor &b,
407 const DiracSpinor &c, const DiracSpinor &d) const;
408
409 //! Creates single 'nk4Index', WITHOUT accounting for 'NormalOrder'. Can be
410 //! used to check if {a,b,c,d} are already in 'NormalOrder'
412
413 //! Converts given set of nkIndex's (in any order) to nk4Index
414 static nk4Index FormIndex(nkIndex a, nkIndex b, nkIndex c, nkIndex d);
415
416 //! Breaks nk4Index back into {ia,ib,ic,id}. Not often used.
417 std::array<nkIndex, 4> UnFormIndex(const nk4Index &index) const;
418
419private:
420 // Returns the "NormalOrder" nk4Index for given set. Implemented by
421 // specific symmetries
422 static inline nk4Index NormalOrder_impl(nkIndex a, nkIndex b, nkIndex c,
423 nkIndex d);
424
425 // True if the table already holds every integral the basis requires, so the
426 // full fill() can skip re-filling. Checks both the per-k count and, if basis
427 // is given, that every orbital is present (via its Q^0_aaaa self-integral).
428 // The coverage check catches a table built for a different basis (e.g. more n
429 // but fewer l) whose per-k count is >= required but is missing whole
430 // angular-momentum channels. For a full fill, coverage implies completeness.
431 // basis must be null for selective fills, which may omit Q^0_aaaa.
432 bool already_filled(const std::vector<std::size_t> &count,
433 const std::vector<DiracSpinor> basis) const {
434
435 for (const auto &a : basis) {
436 if (!contains(0, a, a, a, a))
437 return false;
438 }
439 for (auto k = 0ul; k < count.size(); ++k) {
440 if (count[k] == 0)
441 continue;
442 if (k >= m_data.size() || m_data[k].size() < count[k])
443 return false;
444 }
445 return true;
446 }
447};
448
449/*!
450 @brief Coulomb integral table with 8-fold Qk symmetry.
451 @details
452 Stores Q^k_abcd assuming the symmetry:
453
454 - {abcd} = cbad = adcb = cdab = badc = bcda = dabc = dcba.
455
456 Normal ordering is the lexicographically smallest arrangement among
457 these 8 equivalents.
458
459 @note See @ref CoulombTable note regarding selection rules assumptions
460*/
462
463/*!
464 @brief Coulomb integral table with 4-fold Wk symmetry.
465 @details
466 Stores integrals assuming the symmetry:
467 {abcd} = badc = cdab = dcba.
468*/
470
471/*!
472 @brief Coulomb integral table with 2-fold Lk symmetry.
473 @details
474 Stores integrals assuming the symmetry:
475 {abcd} = badc.
476*/
478
479/*!
480 @brief Coulomb integral table with no assumed symmetry.
481 @details
482 Each {k,a,b,c,d} is treated as a unique entry; no normal ordering is applied.
483*/
485
486//==============================================================================
487//==============================================================================
488//==============================================================================
489
490/*!
491 @brief Estimate memory required for a QkTable for a given basis.
492 @details
493 Given the @p basis_string, counts the number of non-zero Coulomb integrals
494 using Qk symmetry and angular selection rules.
495 Reports the estimated memory usage in GB for three subsets:
496 - All possible Q^k integrals (e.g., structure radiation)
497 - Excited-only integrals (all four orbitals above the Fermi level) [CI]
498 - Core-Excited integrals (1 or 2 orbitals below Fermi level) [MBPT]
499
500 - And @ref CoulombTable::count_non_zero_integrals() to count integrals
501 - Uses @ref estimate_memory_GB() to convert integral counts to memory estimates.
502
503 @note count_non_zero_integrals() takes non-trivial time for large basis
504
505 @param basis_string Basis specification string (e.g., "20spdf").
506 @param core_string Core configuration string (e.g., "[Xe]").
507 Used to determine the Fermi level.
508 Pass an empty string for no core.
509 @param k_cut Maximum multipolarity k to consider.
510*/
511void estimate_memory_usage(const std::string &basis_string,
512 const std::string &core_string, int k_cut = 999);
513
514//! Estimate memory required for a Qk table
515double estimate_memory_GB(std::size_t number_of_integrals,
516 double efficiency = 0.65);
517
518} // namespace Coulomb
519
520#include "QkTable.ipp"
Lookup table for Wigner 6j symbols.
Definition SixJTable.hpp:82
Base class template to store Coulomb integrals, and similar. 3 specific cases (by template instantiat...
Definition QkTable.hpp:118
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 integral P^k_abcd with effective Coulomb screening.
Definition QkTable.ipp:247
double * get(int k, nk4Index index)
Directly gets one of the stored elements, given normal-ordered nk4Index.
Definition QkTable.ipp:123
void fill_if(const std::vector< DiracSpinor > &basis, const YkTable &yk, const SelectionRules &SelectionFunction, int k_cut=-1, bool print=true)
Fill table with Coulomb Q integrals satisfying a selection rule.
Definition QkTable.ipp:699
Real P(int k, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d, const Angular::SixJTable *const sj=nullptr) const
Exchange integral P^k_abcd = (2k+1) sum_l {6j} Q^l_abdc.
Definition QkTable.ipp:238
bool is_NormalOrdered(nkIndex a, nkIndex b, nkIndex c, nkIndex d) const
Checks if set {a,b,c,d} are already in NormalOrder.
Definition QkTable.hpp:384
bool is_NormalOrdered(const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d) const
Checks if set {a,b,c,d} are already in NormalOrder.
Definition QkTable.hpp:388
std::array< std::size_t, 4 > count_non_zero_integrals(const std::vector< DiracSpinor > &basis, std::size_t max_k=99, double eF=0.0) const
Definition QkTable.ipp:490
void update(const std::vector< DiracSpinor > &basis, const CoulombFunction &Fk, double damp, bool print=true, const std::function< bool(const DiracSpinor &, const DiracSpinor &, const DiracSpinor &, const DiracSpinor &)> &filter={})
Re-calculate all existing table entries using a CoulombFunction.
Definition QkTable.ipp:940
void write(const std::string &fname, bool verbose=true) const
Writes coulomb integrals to disk.
Definition QkTable.ipp:1014
auto operator->()
Gives arrow access to all underlying vector<unordered_map> functions.
Definition QkTable.hpp:229
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:105
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:429
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:442
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:451
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:186
std::array< nkIndex, 4 > UnFormIndex(const nk4Index &index) const
Breaks nk4Index back into {ia,ib,ic,id}. Not often used.
Definition QkTable.ipp:474
void fill(const std::vector< DiracSpinor > &basis, const YkTable &yk, int k_cut=-1, bool print=true)
Fill table with all non-zero Coulomb Q integrals from a YkTable.
Definition QkTable.ipp:566
int max_k() const
Definition QkTable.hpp:245
bool read(const std::string &fname, bool verbose=true, bool parse_basis_string=false)
Reads coulomb integrals to disk. Returns false if none read in. If parse_basis_string is true,...
Definition QkTable.ipp:1046
void summary() const
For testing: prints details of coulomb integrals stored.
Definition QkTable.ipp:21
Real g(const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d, int tma, int tmb, int tmc, int tmd) const
Full matrix element g_abcd with explicit magnetic quantum numbers.
Definition QkTable.ipp:299
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:53
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:149
Real W(int k, const DiracSpinor &a, const DiracSpinor &b, const DiracSpinor &c, const DiracSpinor &d, const Angular::SixJTable *const sj=nullptr) const
Antisymmetrised integral W^k_abcd = Q^k_abcd + P^k_abcd.
Definition QkTable.ipp:285
std::size_t count() const
Returns number of stored integrals.
Definition QkTable.ipp:37
bool emptyQ() const
Returns true if table is empty.
Definition QkTable.hpp:272
Calculates + stores Hartree Y functions + Angular (w/ look-up), taking advantage of symmetry.
Definition YkTable.hpp:46
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:55
uint16_t Index
Integer type for the compressed (n,kappa) index.
Definition DiracSpinor.hpp:73
Functions (+classes) for computing Coulomb integrals.
Definition CoulombBreit.cpp:13
void estimate_memory_usage(const std::string &basis_string, const std::string &core_string, int k_cut)
Estimate memory required for a QkTable for a given basis.
Definition QkTable.cpp:8
double Real
Data type used to store integrals.
Definition QkTable.hpp:37
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:70
std::string_view to_string(Symmetry s)
Convert Symmetry to string.
Definition QkTable.hpp:22
uint64_t nk4Index
index type for set of 4 orbitals {nk,nk,nk,nk} -> nk4Index [max n: 256]
Definition QkTable.hpp:41
ankerl::unordered_dense::map< nk4Index, Real > QkMap
Hashmap type used to store integrals (one per k).
Definition QkTable.hpp:57
uint16_t nkIndex
index type for each {nk} (orbital) [max n: 256]
Definition QkTable.hpp:45
double estimate_memory_GB(std::size_t number_of_integrals, double load_factor)
Estimate memory required for a Qk table.
Definition QkTable.cpp:76
Symmetry
Symmetry (state index order) for tables.
Definition QkTable.hpp:19
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:64