ampsci
High-precision calculations for one- and two-valence atomic systems
Coulomb::CoulombTable< S >

ok

template<Symmetry S>
class Coulomb::CoulombTable< S >

Base class template to store Coulomb integrals, and similar. 3 specific cases (by template instantiation), account for specific symmetrs.

Mostly, a wrapper for std::unordered_map. Stores data in a std::vector of maps, each element (map) for each k (multipolarity). The symmetry is taken into account by defining a "Normal Ordering" of set of orbitals {a,b,c,d}, which is defined as the "smallest" arrangment of equivilant indices. The details depend on the specific symmetry in question:

Qk symmetry:

  • {abcd} = cbad = adcb = cdab = badc = bcda = dabc = dcba.
  • Normal ordering: "smallest" of above 8 options
  • [ Also assumes Coulomb selection rules, for fill(), and P(), W() ]

Wk symmetry (same as g):

  • {abcd} = badc = cdab = dcba

Lk symmetry:

  • {abcd} = badc

Nk symmetry:

  • No symmetry assumed; each integral treated as unique.
Note
Hashtable lookup is significant bottleneck. Should look into better hashmaps.
Warning
Requires 0<n<256, see Angular::nk_to_index
Note
The internal summations in P(), P2(), W(), and g() depend on the selection rules, and S: for Symmetry::Qk, we assume Coulomb angular selection rules, including parity rule, (via k_minmax_Q); for all other symmetries no explicit selection rule is assumed. The fill() and fill_if() functions also make this assumption; these functions are only available for the Symmetry::Qk symmetry, and in that case, assume the regular Coulomb selection rules.

#include <QkTable.hpp>

Public Member Functions

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.
 
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.
 
void fill (const std::vector< DiracSpinor > &basis, const CoulombFunction &Fk, const SelectionRules &Fk_SR, int k_cut=-1, bool print=true)
 Fill table using a general CoulombFunction and selection rules.
 
void update (const std::vector< DiracSpinor > &basis, const CoulombFunction &Fk, double damp, bool print=true)
 Re-calculate all existing table entries using a CoulombFunction.
 
auto operator-> ()
 Gives arrow access to all underlying vector<unordered_map> functions.
 
void summary () const
 For testing: prints details of coulomb integrals stored.
 
std::size_t count () const
 Returns number of stored integrals.
 
int max_k () const
 
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
 
void add (int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d, Real value)
 adds a new value. Note: does nothing if {k,a,b,c,d} already exists
 
void add (int k, nk4Index index, Real value)
 adds a new value. Note: does nothing if {k,a,b,c,d} already exists
 
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.
 
void update (int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d, Real value)
 Updates value in table. If not present, adds new value.
 
void update (int k, nk4Index index, Real value)
 Updates value in table. If not present, adds new value.
 
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.
 
bool contains (int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d) const
 Checks if given {k,a,b,c,d} is in the table.
 
bool contains (int k, nk4Index index) const
 Checks if given {k,a,b,c,d} is in the table.
 
bool emptyQ () const
 Returns true if table is empty.
 
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
 
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.)
 
Real Q (int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d) const
 Retrieve a stored Q. If not present, returns 0. (Returns exactly as stored in table.)
 
Real Q (int k, nk4Index index) const
 Retrieve a stored Q. If not present, returns 0. (Returns exactly as stored in table.)
 
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)
 
Real R (int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d) const
 Returns 'R', defined via: R := Q / (angular_coef)
 
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.
 
Real P (int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d, const Angular::SixJTable *const sj=nullptr) const
 Exchange integral P^k_abcd = (2k+1) sum_l {6j} Q^l_abdc.
 
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.
 
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.
 
Real W (int k, nkIndex a, nkIndex b, nkIndex c, nkIndex d, const Angular::SixJTable *const sj=nullptr) const
 Antisymmetrised integral W^k_abcd = Q^k_abcd + P^k_abcd.
 
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.
 
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}.
 
nk4Index NormalOrder (nkIndex a, nkIndex b, nkIndex c, nkIndex d) const
 Creates single 'nk4Index' corresponding to 'NormalOrder' symmetry of {a,b,c,d}.
 
bool is_NormalOrdered (nkIndex a, nkIndex b, nkIndex c, nkIndex d) const
 Checks if set {a,b,c,d} are already in NormalOrder.
 
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.
 
void write (const std::string &fname, bool verbose=true) const
 Writes coulomb integrals to disk.
 
bool read (const std::string &fname, bool verbose=true)
 Reads coulomb integrals to disk. Returns false if none read in.
 
double * get (int k, nk4Index index)
 Directly gets one of the stored elements, given normal-ordered nk4Index.
 
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,b,c,d} are already in 'NormalOrder'.
 
nk4Index CurrentOrder (nkIndex a, nkIndex b, nkIndex c, nkIndex d) const
 Creates single 'nk4Index', WITHOUT accounting for 'NormalOrder'. Can be used to check if {a,b,c,d} are already in 'NormalOrder'.
 
std::array< nkIndex, 4 > UnFormIndex (const nk4Index &index) const
 Breaks nk4Index back into {ia,ib,ic,id}. Not often used.
 

Static Public Member Functions

static nk4Index FormIndex (nkIndex a, nkIndex b, nkIndex c, nkIndex d)
 Converts given set of nkIndex's (in any order) to nk4Index.
 

Member Function Documentation

◆ fill() [1/2]

template<Symmetry S>
void Coulomb::CoulombTable< S >::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.

Fills the table with Q^k_abcd for all orbitals in basis, exploiting the 8-fold Qk symmetry to avoid redundant calculations. Uses a precomputed yk for the radial integrals.

Filling proceeds in four stages:

  1. Count non-zero integrals per k (for map reservation)
  2. Reserve map capacity
  3. Initialise all entries to zero (parallelised over k)
  4. Fill values in parallel (safe since no new map insertions occur)
Parameters
basisBasis set of orbitals.
ykPrecomputed Yk table for radial integrals.
k_cutMaximum multipolarity k. Set to <=0 for no cut-off.
printIf true, prints timing and summary.
Note
Only valid for QkTable ( Symmetry::Qk ) (enforced via static_assert).
Warning
Does not update existing entries; only adds new ones.

◆ fill_if()

template<Symmetry S>
void Coulomb::CoulombTable< S >::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.

Same as default fill(basis, yk, k_cut, print), but only computes and stores integrals for which SelectionFunction returns true. Useful for restricting to a particular subset of integrals (e.g., CI- or MBPT-relevant).

Uses the same four-stage filling strategy as fill(basis, yk, ...).

Parameters
basisBasis set of orbitals.
ykPrecomputed Yk table for radial integrals.
SelectionFunctionReturns true for integrals to include.
k_cutMaximum multipolarity k. Set to <=0 for no cut-off.
printIf true, prints timing and summary.
Note
Only valid for QkTable ( Symmetry::Qk ) (enforced via static_assert).
Warning
Does not update existing entries; only adds new ones.

◆ fill() [2/2]

template<Symmetry S>
void Coulomb::CoulombTable< S >::fill ( const std::vector< DiracSpinor > &  basis,
const CoulombFunction Fk,
const SelectionRules Fk_SR,
int  k_cut = -1,
bool  print = true 
)

Fill table using a general CoulombFunction and selection rules.

Fills the table with Fk(k,a,b,c,d) for all orbitals in basis, restricted to entries for which Fk_SR returns true, accounting for the symmetry of the table. Unlike the YkTable overloads, this version is general-purpose and does not assume Qk angular selection rules.

Uses the same four-stage filling strategy as fill(basis, yk, ...).

Parameters
basisBasis set of orbitals.
FkFunction to compute the integral value.
Fk_SRSelection rule; only integrals passing this are stored.
k_cutMaximum multipolarity k. Set to <=0 for no cut-off.
printIf true, prints timing and summary.
Warning
Does not update existing entries; only adds new ones.

◆ update() [1/4]

template<Symmetry S>
void Coulomb::CoulombTable< S >::update ( const std::vector< DiracSpinor > &  basis,
const CoulombFunction Fk,
double  damp,
bool  print = true 
)

Re-calculate all existing table entries using a CoulombFunction.

Updates all integrals currently stored in the table by re-evaluating Fk(k,a,b,c,d) using the provided CoulombFunction() for each entry. Unlike fill(), this recalculates all existing entries and does not add new ones. Useful for iterating.

Warning
Note: does not calculate any new integrals; only re-calculates existing ones. Table must have corect size/shape before calling.

Damping: With \(\eta = \text{damp}\), integrals are updated as:

\[ Q^k_{abcd} \to \eta Q^k_{abcd} + (1-\eta) Fk(k,a,b,c,d) \]

where Fk is a CoulombFunction. \(\eta=0\) means no damping. Must be between 0 and 1 (0 is allowed, 1 is not).

Parameters
basisBasis set of orbitals.
FkFunction to compute the integral value.
dampDamping factor, \(\eta \in [0,1)\). 0 means no damping.
printIf true, prints a progress bar.

◆ operator->()

template<Symmetry S>
auto Coulomb::CoulombTable< S >::operator-> ( )
inline

Gives arrow access to all underlying vector<unordered_map> functions.

◆ summary()

template<Symmetry S>
void Coulomb::CoulombTable< S >::summary ( ) const

For testing: prints details of coulomb integrals stored.

◆ count()

template<Symmetry S>
std::size_t Coulomb::CoulombTable< S >::count ( ) const

Returns number of stored integrals.

◆ max_k()

template<Symmetry S>
int Coulomb::CoulombTable< S >::max_k ( ) const
inline

Maximum k stored in table. If table is empty, returns '-1'

Note
Based on size of map only; doesn't check for values. May sometimes have a map with no non-zero integrals for a given k. Guarenteed to be no integrals with k larger than this; no guarentee that there are non-zero integrals at or below this k.

◆ add() [1/3]

template<Symmetry S>
void Coulomb::CoulombTable< S >::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

◆ add() [2/3]

template<Symmetry S>
void Coulomb::CoulombTable< S >::add ( int  k,
nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d,
Real  value 
)

adds a new value. Note: does nothing if {k,a,b,c,d} already exists

◆ add() [3/3]

template<Symmetry S>
void Coulomb::CoulombTable< S >::add ( int  k,
nk4Index  index,
Real  value 
)

adds a new value. Note: does nothing if {k,a,b,c,d} already exists

◆ update() [2/4]

template<Symmetry S>
void Coulomb::CoulombTable< S >::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.

◆ update() [3/4]

template<Symmetry S>
void Coulomb::CoulombTable< S >::update ( int  k,
nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d,
Real  value 
)

Updates value in table. If not present, adds new value.

◆ update() [4/4]

template<Symmetry S>
void Coulomb::CoulombTable< S >::update ( int  k,
nk4Index  index,
Real  value 
)

Updates value in table. If not present, adds new value.

◆ contains() [1/3]

template<Symmetry S>
bool Coulomb::CoulombTable< S >::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.

◆ contains() [2/3]

template<Symmetry S>
bool Coulomb::CoulombTable< S >::contains ( int  k,
nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d 
) const

Checks if given {k,a,b,c,d} is in the table.

◆ contains() [3/3]

template<Symmetry S>
bool Coulomb::CoulombTable< S >::contains ( int  k,
nk4Index  index 
) const

Checks if given {k,a,b,c,d} is in the table.

◆ emptyQ()

template<Symmetry S>
bool Coulomb::CoulombTable< S >::emptyQ ( ) const
inline

Returns true if table is empty.

◆ count_non_zero_integrals()

template<Symmetry S>
std::array< std::size_t, 4 > Coulomb::CoulombTable< S >::count_non_zero_integrals ( const std::vector< DiracSpinor > &  basis,
std::size_t  max_k = 99,
double  eF = 0.0 
) const

Counts number of non-zero Coulomb integrals, accounting for symmetry

Note
Specifically assumes Qk selection rules, and therefore only works for Qk.

In theory, easily updated for general symmetry/selection rules, but this is usually the bottle-neck!

◆ Q() [1/3]

template<Symmetry S>
double Coulomb::CoulombTable< S >::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.)

◆ Q() [2/3]

template<Symmetry S>
double Coulomb::CoulombTable< S >::Q ( int  k,
nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d 
) const

Retrieve a stored Q. If not present, returns 0. (Returns exactly as stored in table.)

◆ Q() [3/3]

template<Symmetry S>
double Coulomb::CoulombTable< S >::Q ( int  k,
nk4Index  index 
) const

Retrieve a stored Q. If not present, returns 0. (Returns exactly as stored in table.)

◆ R() [1/2]

template<Symmetry S>
double Coulomb::CoulombTable< S >::R ( int  k,
const DiracSpinor a,
const DiracSpinor b,
const DiracSpinor c,
const DiracSpinor d 
) const

Returns 'R', defined via: R := Q / (angular_coef)

◆ R() [2/2]

template<Symmetry S>
double Coulomb::CoulombTable< S >::R ( int  k,
nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d 
) const

Returns 'R', defined via: R := Q / (angular_coef)

◆ P() [1/2]

template<Symmetry S>
double Coulomb::CoulombTable< S >::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.

Computes the exchange part of the antisymmetrised W = Q + P integral. Optionally uses a precomputed 6J table for faster evaluation.

Note
For Symmetry::Qk, l is iterated using Coulomb angular selection rule bounds (k_minmax_Q); for all other symmetries, l runs over [0, max_k()]. See CoulombTable.

◆ P() [2/2]

template<Symmetry S>
double Coulomb::CoulombTable< S >::P ( int  k,
nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d,
const Angular::SixJTable *const  sj = nullptr 
) const

Exchange integral P^k_abcd = (2k+1) sum_l {6j} Q^l_abdc.

As P(int, const DiracSpinor&, ...) but takes nkIndex arguments.

Note
For Symmetry::Qk, l is iterated using Coulomb angular selection rule bounds (k_minmax_Q); for all other symmetries, l runs over [0, max_k()]. See CoulombTable.

◆ P2()

template<Symmetry S>
double Coulomb::CoulombTable< S >::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.

As P(), but each l term is weighted by a screening factor fk[l].

Note
For Symmetry::Qk, l is iterated using Coulomb angular selection rule bounds (k_minmax_Q); for all other symmetries, l runs over [0, max_k()]. See CoulombTable.

◆ W() [1/2]

template<Symmetry S>
double Coulomb::CoulombTable< S >::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.

Returns Q + P. Optionally uses a precomputed 6J table.

Note
l iteration in P() depends on symmetry S; see P() and CoulombTable.

◆ W() [2/2]

template<Symmetry S>
double Coulomb::CoulombTable< S >::W ( int  k,
nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d,
const Angular::SixJTable *const  sj = nullptr 
) const

Antisymmetrised integral W^k_abcd = Q^k_abcd + P^k_abcd.

As W(int, const DiracSpinor&, ...) but takes nkIndex arguments.

Note
l iteration in P() depends on symmetry S; see P() and CoulombTable.

◆ g()

template<Symmetry S>
double Coulomb::CoulombTable< S >::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.

Sums over k, weighted by 3j symbols, using the stored Q^k values.

Note
For Symmetry::Qk, k is iterated using Coulomb angular selection rule bounds (step 2); for all other symmetries, k runs over [0, max_k()] with step 1. See CoulombTable.

◆ NormalOrder() [1/2]

template<Symmetry S>
nk4Index Coulomb::CoulombTable< S >::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}.

◆ NormalOrder() [2/2]

template<Symmetry S>
nk4Index Coulomb::CoulombTable< S >::NormalOrder ( nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d 
) const

Creates single 'nk4Index' corresponding to 'NormalOrder' symmetry of {a,b,c,d}.

◆ is_NormalOrdered() [1/2]

template<Symmetry S>
bool Coulomb::CoulombTable< S >::is_NormalOrdered ( nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d 
) const
inline

Checks if set {a,b,c,d} are already in NormalOrder.

◆ is_NormalOrdered() [2/2]

template<Symmetry S>
bool Coulomb::CoulombTable< S >::is_NormalOrdered ( const DiracSpinor a,
const DiracSpinor b,
const DiracSpinor c,
const DiracSpinor d 
) const
inline

Checks if set {a,b,c,d} are already in NormalOrder.

◆ write()

template<Symmetry S>
void Coulomb::CoulombTable< S >::write ( const std::string &  fname,
bool  verbose = true 
) const

Writes coulomb integrals to disk.

◆ read()

template<Symmetry S>
bool Coulomb::CoulombTable< S >::read ( const std::string &  fname,
bool  verbose = true 
)

Reads coulomb integrals to disk. Returns false if none read in.

◆ get()

template<Symmetry S>
double * Coulomb::CoulombTable< S >::get ( int  k,
nk4Index  index 
)

Directly gets one of the stored elements, given normal-ordered nk4Index.

◆ CurrentOrder() [1/2]

template<Symmetry S>
nk4Index Coulomb::CoulombTable< S >::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,b,c,d} are already in 'NormalOrder'.

◆ CurrentOrder() [2/2]

template<Symmetry S>
nk4Index Coulomb::CoulombTable< S >::CurrentOrder ( nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d 
) const

Creates single 'nk4Index', WITHOUT accounting for 'NormalOrder'. Can be used to check if {a,b,c,d} are already in 'NormalOrder'.

◆ FormIndex()

template<Symmetry S>
nk4Index Coulomb::CoulombTable< S >::FormIndex ( nkIndex  a,
nkIndex  b,
nkIndex  c,
nkIndex  d 
)
static

Converts given set of nkIndex's (in any order) to nk4Index.

◆ UnFormIndex()

template<Symmetry S>
std::array< nkIndex, 4 > Coulomb::CoulombTable< S >::UnFormIndex ( const nk4Index index) const

Breaks nk4Index back into {ia,ib,ic,id}. Not often used.


The documentation for this class was generated from the following files: