Advanced Features

Green’s functions for systems with many orbitals

greensfunc_op_t diverge_largemat_gf(const diverge_model_t *model, complex128_t Lambda, complex128_t *buf)
[source]

Green’s function generator that is faster than the default diverge_greensfunc_generator_default() for systems with a significant number of orbitals. Performs actual BLAS calls to GEMM instead of manually passing through the (o,b,p) loop. Shared memory GFs as well as 32bit floating point precision not supported.

Green’s functions for subspace systems

When only a certain orbital subspace is considered as “correlated”, a custom Green’s function generator can be defined to only calculate the Green’s function for those orbitals. We provide a GPU (and CPU) subspace GF generator that is most conveniently used when both the full system and the subspace system are separate diverge_model_t instances.

Due to the GPU capability, the functions provided here must operate on a handle (subspace_gfill_multi_t). The actual Green’s function calculation is carried out using subspace_gfill_multi_exec(); the documentation includes an example of how to use it as a greensfunc_generator_t.

type subspace_gfill_multi_t
[source]

opaque typedef for hanlde used in subspace Green’s functions

subspace_gfill_multi_t *subspace_gfill_multi_init(double *E, complex128_t *U, index_t nk, const index_t *orbs, index_t n_orbs, index_t nb, index_t b_per_bb)
[source]

initialize the subspace_gfill_multi_t handle, which must be free’d using subspace_gfill_multi_free().

Parameters:
  • E (double*) – dispersion array of the full model

  • U (complex128_t*) – orbital to band transformation of the full model

  • nk (index_t) – number of kpoints (kdimtot(nk, nkf))

  • orbs (const index_t*) – selection of orbital/spin indices to construct subspace for

  • n_orbs (index_t) – size of subspace

  • nb (index_t) – number of bands in the full model

  • b_per_bb (index_t) – number of bands to consider in a single CUDA thread (for the hand-written GEMM). Note that the maximum number of threads per block is not checked against, so make this large enough to accomodate dim3(nb/b_per_bb, n_orbs, n_orbs) threads per block.

void subspace_gfill_multi_free(subspace_gfill_multi_t *m)
[source]

free the subspace_gfill_multi_t handle.

void subspace_gfill_multi_exec(subspace_gfill_multi_t *m, complex128_t *dest, complex128_t Lambda)
[source]

calculate subspace GFs using the handle obtained with subspace_gfill_multi_init(). Safe to call multiple times, but not safe to use in a scenario where the target buffer resides in shared memory.

Example:

static greensfunc_op_t subspace_gf( const diverge_model_t* model, complex128_t Lambda, complex128_t* buf );

int main(int argc, char** argv) {
    diverge_init( &argc, &argv );

    // some initialization, other code, ...

    diverge_model_t* fullmodel = ...; // set up the full model for kinetics
    diverge_model_t* subspace = ...; // set up the subspace model for interactions

    index_t orbital_selection[] = { 0, 1, 5, 6 }; // choice of orbitals
    index_t n_orbital_selection = sizeof(orbital_selection)/sizeof(orbital_selection[0]);

    index_t b_per_bb = 3; // adjust accordingly
    // before calling the function below, diverge_model_internals_common()
    // must have been called on fullmodel
    subspace_gfill_multi_t* subspace_gf_data = subspace_gfill_multi_init(
        diverge_model_internals_get_E(fullmodel),
        diverge_model_internals_get_U(fullmodel),
        kdimtot(fullmodel->nk, fullmodel->nkf),
        orbital_selection,
        n_orbital_selection,
        fullmodel->n_orb * fullmodel->n_spin,
        b_per_bb
    );

    subspace->data = subspace_gf_data;
    subspace->gfill = &subspace_gf;

    // do something with the subspace model

    subspace_gfill_multi_free( subspace_gf_data );
    diverge_finalize();
}

static greensfunc_op_t subspace_gf( const diverge_model_t* model, complex128_t Lambda, complex128_t* buf ) {
    subspace_gfill_multi_exec( model->data, buf, Lambda );
    return greensfunc_op_cpu;
}
void subspace_gfill_multi_exec_mu(subspace_gfill_multi_t *m, complex128_t *dest, complex128_t Lambda, double mu_add)
[source]

same as subspace_gfill_multi_exec() except for an additional chemical potential that is subtracted from the eigenvalues. Useful for GPU calculations, because there, the eigenvalue buffer is copied to the device and not automatically updated.

Unique Distances

Sometimes it may be useful to generate all uniquely defined distances in a divERGe model, i.e., in order to set on-site, nearest-neighbor, next-nearest-neighbor, etc. interactions. We provide a function to do so in the unstable interface, which can be included with #include <misc/unique_distances.h>. This header comprises two functions with the following signatures:

double *diverge_model_unique_distances(diverge_model_t *m, int Rmax, int dim)
[source]

given a diverge_model_t with lattice vectors and positions, this function calculates all unique distances. Useful if e.g. interactions of first, second, etc. nearest neighbors should be initialized.

Parameters:
  • m (diverge_model_t*) – model to operate on

  • Rmax (int) – maximum integer distance of lattice vectors. For each dimension, the loop walks through \(-R_\mathrm{max}..R_\mathrm{max}\) for the calculation of all possible distances

  • dim (int) – dimension of the model. The first dim lattice vectors are considered in the iteration via Rmax.

Returns:

allocated vector of unique distances terminated by an IEEE 754 Quiet NaN. Must be free’d manually using free.

void diverge_model_unique_distances_set_precision(double prec)
[source]

set the precision used to calculate unique distances. not thread safe. useful in case the model has very large values as lattice constant.

Parameters:
  • prec (double) – precision to use. if prec > 0, use absolute value. if prec < 0, use relative value with respect to the model’s first lattice vector length. if qnan_isnan(prec) (see qnan_isnan()), reset to initial value DIVERGE_EPS_MESH (see Compilation DEFINES).

index_t diverge_model_unique_distances_length(double *dists)
[source]

return the length of the unique distance array by checking for quiet NaN (see src/misc/qnan.h). In practice, unique distances can be obtained as follows:

diverge_model_t* m = ...;

double* dists = diverge_model_unique_distances( m, 3, 2 ); // for a 2D model
index_t n_dists = diverge_model_unique_distances_length( dists );

// do sth with dists here

free( dists );

Hopping Parameters from Hamiltonians

The inverse Fourier transform to get hopping parameters from Hamiltonian arrays is implemented in the following function:

rs_hopping_t *diverge_model_ham2hop(const diverge_model_t *m, double hoplim, index_t *n_hop)
[source]

transforms hamiltonian \(H(\boldsymbol{k})\) of a diverge_model_t into an array of realspace hopping parameters rs_hopping_t, i.e., an inverse Fourier transform.

Parameters:
  • model (const diverge_model_t*) – the divERGe model to operate on. needs to have common internals set (diverge_model_internals_common()), such that the hamiltonian buffer is allocated and filled.

  • hoplim (double) – minimum absolute value that a hopping element must have in order to be included.

  • n_hop (index_t*) – output for the resulting length of the rs_hopping_t array

Returns:

rs_hopping_t array that contains the real-space version of the input Bloch Hamiltonian (obtained via lattice Fourier transform).

rs_hopping_t *diverge_model_ham2hop_fg(const diverge_model_t *m, double hoplim, double distlim, index_t *n_hop, const complex128_t *H0)
[source]

same as diverge_model_ham2hop(), but with explicit control over the buffer which is used for the Hamiltonian (H0) and distance limitation (distlim; set negative to ignore). Very useful to, e.g., extract the selfenergy in efficient representation at the end of the flow.

int diverge_model_ham2hop_swap_orbs(void)
[source]

not thread-safe, swap orbital convention. return whether swapping in future calls to diverge_model_ham2hop().

Hamiltonians at Specific Momentum Points

To obtain a tight-binding Hamiltonian (from hopping parameters diverge_model_t.hop) at a specific point in momentum space without setting it up in the entire BZ (as done in diverge_model_t.hfill), one can use the function diverge_model_ham_at_kpt(). In addition, we provide wrappers for the coarse and fine mesh indices—these wrappers rely on the model internals structure to be set up (via diverge_model_internals_common()).

void diverge_model_ham_at_kpt(const diverge_model_t *m, const double *k, complex128_t *buf)
[source]

generate the Hamiltonian at the momentum vector \(\boldsymbol{k}\) (described by the array k[3]) and write it into buf (which needs to be of the size POW2(m->n_orb * m->n_spin)). The chemical potential \(\mu\) is not included; to do so you have to subtract it from the diagonal. The matrix pointed to by buf is overwritten.

void diverge_model_ham_at_kcidx(const diverge_model_t *m, index_t k, complex128_t *buf)
[source]

generate the Hamiltonian at the coarse index k and write it into buf. k refers to a coarse mesh index, and buf must be sized the same way as in diverge_model_ham_at_kpt().

void diverge_model_ham_at_kfidx(const diverge_model_t *m, index_t k, complex128_t *buf)
[source]

generate the Hamiltonian at the fine index k and write it into buf. k refers to a fine mesh index, and buf must be sized the same way as in diverge_model_ham_at_kpt().

Density of States

double *diverge_linspace(double min, double max, index_t num)
[source]

create a linearly spaced array from min to max with num entries. returned array must be free’d by user with free or diverge_mem_free(). When num is negative, the endpoint is excluded. Otherwise it’s included.

double *diverge_filling_to_energy(const diverge_model_t *m, const double *E, index_t nb, const double *nu, index_t n_nu)
[source]

Calculate an energy array given a filling array, i.e., do the transformation \(\nu \rightarrow E(\nu)\) for all n_nu values in the nu array. Allocates and frees sorted copy of energy array.

Parameters:
  • m (const diverge_model_t*) – the model to operate on with common internals set via diverge_model_internals_common().

  • E (const double*) – an optional energy array, may be NULL in which case the internal energy array is used.

  • nb (index_t) – an optional number of bands. is ignored if E == NULL or if nb <= 0

  • nu (const double*) – filling array of size n_nu with values between zero and one (everything else is truncated to zero-one)

  • n_nu (index_t) – length of the filling array.

Returns:

double array with the energies corresponding to the fillings (size n_nu) that must be free’d by the user via free or diverge_mem_free().

double *diverge_energy_fill_gaps(double *omega, index_t *p_n_omega, double d_omega_min)
[source]

fill the gaps of a frequency array such that the spacing is always larger than d_omega_min. return the reallocated array, and change *p_n_omega according to the new size

double *diverge_model_dos(const diverge_model_t *m, const double *E, index_t nb, double *omega, index_t n_omega, double eta)
[source]

calculate the DOS given an array of energies or a model that has its internals set: \(\rho(\omega) = \sum_{k,b} \Im[ 1/(\omega - i\eta - E_{kb}) ]\).

Parameters:
  • m (const diverge_model_t*) – the model to operate on with common internals set via diverge_model_internals_common().

  • E (const double*) – an optional energy array, may be NULL in which case the internal energy array is used.

  • nb (index_t) – an optional number of bands. is ignored if E == NULL or if nb <= 0

  • omega (const double*) – frequency array in ascending order (else it is modified and sorted).

  • n_omega (index_t) – number of frequencies. If negative, don’t allocate the memory for the result but instead reallocate omega to a region large enough to hold omega and the result (subsequently in memory). The new pointer is returned. Works only for the case where omega is allocated with malloc and friends.

  • eta (double) – smearing. If eta < 0, use an optimized algorithm that only considers energies within a small window for DOS calculation at each frequency, assumes linearly spaced array for omega.

Returns:

double array with the density of states (shape n_omega). Result must be free’d with call to free or diverge_mem_free().

double *diverge_model_ldos(const diverge_model_t *m, const double *E, const complex128_t *U, index_t nb, double *omega, index_t n_omega, double eta)
[source]

calculate the LDOS given an array of energies and orbital to band matrices or a model that has its internals set: \(\rho(\omega,o) = \sum_{k,b} \Im[ |U_{kb}^o|^2/(\omega - i\eta - E_{kb}) ]\).

Parameters:
  • m (const diverge_model_t*) – the model to operate on with common internals set via diverge_model_internals_common().

  • E (const double*) – an optional energy array, may be NULL in which case the internal energy array is used.

  • U (const complex128_t*) – an optional orbital to band array, may be NULL in which case the internal o2b array is used.

  • nb (index_t) – an optional number of bands. is ignored if E == NULL or if nb <= 0

  • omega (const double*) – frequency array in ascending order (else it is modified and sorted).

  • n_omega (index_t) – number of frequencies. If negative, don’t allocate the memory for the result but instead reallocate omega to a region large enough to hold omega and the result (subsequently in memory). The new pointer is returned. Works only for the case where omega is allocated with malloc and friends.

  • eta (double) – smearing. If eta < 0, use an optimized algorithm that only considers energies within a small window for LDOS calculation at each frequency, assumes linearly spaced array for omega.

Returns:

double array with the local density of states (shape (n_omega,nb)). Result must be free’d with call to free or diverge_mem_free().

void diverge_dos_set_eta_factor(double eta_factor)
[source]

set the factor \(e\) that determines the width of energies \(W\) to be considered for summation at a given frequency \(\omega\), i.e.,

\(W = [\omega - e\eta, \omega + e\eta]\)

Defaults to \(e = 15\), which is good enough for most features in a regular DOS, but not good enough for excellent convergence of the Lorentzian Kernel (which only falls off quadratically).

void diverge_dos_use_gpu(int use_gpu)
[source]

set whether GPU is used for DOS calc

Hartree Fock

Since divERGe includes self-energy diagrams in the TU backend, we chose to also provide building blocks for self-consistent Hartree Fock simulations using models built in divERGe. In order to use these facilities, you must initialize the model with diverge_model_internals_common() and diverge_model_internals_tu(). Moreover, only standard Hamiltonian-based models (i.e., default GF generator, no shared-memory GFs, etc) are supported; each HF step requires a full rediagonalization of \(H(\boldsymbol{k}) + \Sigma(\boldsymbol{k})\) and setting a filling value \(\nu\).

type diverge_hartree_fock_t
[source]

opaque typedef for the handle

diverge_hartree_fock_t *diverge_hartree_fock_init(diverge_model_t *model, double nu, const char *chans)
[source]

initialize a diverge_hartree_fock_t handle given a model that has both the TU and common internals set. The user additionally has to provide a filling value \(\nu\) and the interaction channels that should be taken into account (equivalent to the vertex specific channels in diverge_flow_step_init()). Must be freed with a call to diverge_hartree_fock_free().

Parameters:
  • m (diverge_model_t*) – diverge model

  • nu (double) – filling value (0…1)

  • chans (const char*) – interaction channels as string

int diverge_hartree_fock_normalize_configure(int normalize)
[source]

change default behavior of always calling diverge_hartree_fock_normalize_sigma() within diverge_hartree_fock_step() to run slightly faster and be compatible with previous versions. returns previous setting.

Note

The default behavior is now changed to always normalize; you’d have to explicitly turn it off before performing the first diverge_hartree_fock_step().

diverge_flow_step_t *diverge_hartree_fock_get_flow_step(diverge_hartree_fock_t *h)
[source]

return the flow step backend of a HF calculation, s.t. one can access the vertices etc

void diverge_hartree_fock_step(diverge_hartree_fock_t *h, double T)
[source]

perform a step of the Hartre-Fock self-consistency iteration (at temperature T). “Step” here refers to the fact that the current self-energy (see diverge_hartree_fock_get_sigma()) is used for calculating the Green’s function via \(G(\boldsymbol{k})^{-1} = i\omega - H(\boldsymbol{k}) - \Sigma(\boldsymbol{k})\), which is used to evaluate the diagrams. On exit, the resulting “new” self-energy is stored in the buffer pointed to by diverge_hartree_fock_get_sigma(), and the (previous) self-energy that acted as input to calculating the diagrams gets copied to the buffer pointed to by diverge_hartree_fock_get_sigma_prev().

void diverge_hartree_fock_normalize_sigma(diverge_hartree_fock_t *h)
[source]

move the constant part (\(\mu\)) from the selfenergy to the dispersion

complex128_t *diverge_hartree_fock_get_sigma(diverge_hartree_fock_t *h)
[source]

return the current self-energy buffer. Can be used to e.g. set a seed or output of the final self-energy.

complex128_t *diverge_hartree_fock_get_sigma_prev(diverge_hartree_fock_t *h)
[source]

return the self-energy buffer of the previous iteration. Do not put a seed in here. Useful to combine with diverge_hartree_fock_get_sigma() for mixing schemes or assessing convergence.

double diverge_hartree_fock_get_E_tot(diverge_hartree_fock_t *h)
[source]

return the total energy of the state evaluated during the previous step. Does probably not work together with the normalization in diverge_hartree_fock_normalize_sigma() (untested).

void diverge_hartree_fock_free(diverge_hartree_fock_t *h)
[source]

free the diverge_hartree_fock_t handle.