9#include <triqs/gfs.hpp>
10#include <itertools/omp_chunk.hpp>
11#include <triqs/lattice/brillouin_zone.hpp>
18#pragma omp declare reduction(gf_sum : gf<imfreq, scalar_valued> : omp_out += omp_in) initializer(omp_priv = gf{omp_orig.mesh()})
19#pragma omp declare reduction(gf_sum : gf<dlr_imfreq, scalar_valued> : omp_out += omp_in) initializer(omp_priv = gf{omp_orig.mesh()})
24 nda::matrix<dcomplex, nda::F_layout> calc_inv_G_G0(
long M,
auto embedding_decomp,
auto const &Sigma_dynamic,
auto const &Sigma_static,
auto om,
25 long sigma, nda::matrix_view<dcomplex> Y1, nda::matrix_view<dcomplex> Y2) {
28 auto YS = nda::matrix<dcomplex, nda::F_layout>::zeros(M, M);
31 auto Sigma = nda::matrix<dcomplex>{Sigma_dynamic(alpha, sigma)[om] + Sigma_static(alpha, sigma)};
32 nda::blas::gemm(-1, Y1(
r_all, R), Sigma, 0, YS(
r_all, R));
36 for (
auto m : range(M)) YS(m, m) += 1;
37 auto B = nda::matrix<dcomplex, nda::F_layout>{Y2(
r_all,
r_all)};
45 template <
typename Mesh>
46 auto trace_G_B_m_G_KS(one_body_elements_on_grid
const &obe,
double mu,
48 block2_gf<Mesh, matrix_valued>
const &Sigma_dynamic, nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static) {
50 auto M = obe.C_space.dim();
51 auto &mesh = Sigma_dynamic(0, 0).mesh();
52 auto omegas = mesh | tl::to<std::vector<dcomplex>>();
53 auto embedding_decomp =
get_struct(Sigma_dynamic).dims(
r_all, 0) | tl::to<std::vector>();
55 return [=, &obe, &Sigma_dynamic, &Sigma_static](
long sigma,
long k_idx) {
56 auto result = nda::zeros<dcomplex>(omegas.size());
57 auto Y1 = detail::G0_C_k_sigma(obe, mu, k_idx, sigma, omegas,
false);
58 auto Y2 = detail::G0_C_k_sigma(obe, mu, k_idx, sigma, omegas,
true);
60 for (
auto &&[n, om] : itertools::enumerate(mesh)) {
62 auto B = calc_inv_G_G0(M, embedding_decomp, Sigma_dynamic, Sigma_static, om, sigma, Y1(n,
r_all,
r_all), Y2(n,
r_all,
r_all));
67 auto [m, mp] = Sigma_dynamic(alpha, sigma).target_shape();
68 auto A = Sigma_dynamic(alpha, sigma).data()(n,
r_all,
r_all) + Sigma_static(alpha, sigma);
70 for (
auto m1 : range(m))
71 for (auto m2 : range(mp)) tr_Sigma_B += A(m1, m2) * C(m2, m1);
73 result(n) -= obe.H.k_weights(k_idx) * tr_Sigma_B;
92 template <
typename Mesh>
95 block2_gf<Mesh, matrix_valued>
const &Sigma_dynamic, nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static) {
97 if (obe.
H.
matrix_valued)
return density_for_matrix_valued_impl(obe, mu, Sigma_dynamic, Sigma_static);
99 auto n_sigma = Sigma_dynamic.size2();
100 auto n_k = obe.
H.
n_k();
101 auto const &mesh = Sigma_dynamic(0, 0).mesh();
102 double beta = mesh.beta();
103 auto corr = gf{mesh};
107 auto Fermi = [](
double x) {
return (x > 0 ? exp(-x) / (1 + exp(-x)) : 1 / (1 + exp(x))); };
108 auto calc_correction_term = detail::trace_G_B_m_G_KS(obe, mu, Sigma_dynamic, Sigma_static);
111 mpi::communicator comm = {};
112#pragma omp parallel for collapse(2) reduction(gf_sum : corr) reduction(+ : KS_term) default(none) \
113 shared(n_k, comm, n_sigma, obe, Fermi, mu, calc_correction_term, beta)
114 for (
auto k_idx : mpi::chunk(range(n_k), comm)) {
115 for (
auto sigma : range(n_sigma)) {
118 double KS_term_acc = 0;
119 auto eps = obe.
H.
H(sigma, k_idx);
120 for (
auto nu : range(obe.
H.
N_nu(sigma, k_idx)))
121 KS_term_acc += Fermi(beta * real(eps(nu, nu) - mu));
122 KS_term += obe.
H.
k_weights(k_idx) * KS_term_acc;
125 corr.data() += calc_correction_term(sigma, k_idx);
128 KS_term = mpi::all_reduce(KS_term);
129 corr = mpi::all_reduce(corr);
130 return KS_term + real(
density(corr));
135 double density_nk(one_body_elements_on_grid
const &obe,
double mu,
double beta);
149 template <
typename Mesh>
150 double density_for_matrix_valued_impl(one_body_elements_on_grid
const &obe,
double mu, block2_gf<Mesh, matrix_valued>
const &Sigma_dynamic,
151 nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static) {
153 auto const &mesh = Sigma_dynamic(0, 0).mesh();
154 auto beta = mesh.beta();
156 auto result = gf{mesh};
158 auto PSP = [&](
auto &iw,
auto &k_idx,
auto &sigma) {
159 auto N_nu = obe.H.N_nu(sigma, k_idx);
160 auto out = nda::zeros<dcomplex>(N_nu, N_nu);
161 auto embedding_decomp =
get_struct(Sigma_dynamic).dims(
r_all, 0) | tl::to<std::vector>();
163 auto P = obe.P.P(sigma, k_idx)(R,
r_all);
164 out(
r_all,
r_all) += dagger(P) * nda::matrix<dcomplex>{Sigma_dynamic(alpha, sigma).data()(iw,
r_all,
r_all) + Sigma_static(alpha, sigma)} * P;
169 auto Glatt = [&](
auto &k_idx,
auto &sigma) {
171 for (
auto &&[n, w] : enumerate(mesh)) {
172 out.data()(n) = trace(inverse(w + mu - obe.H.H(sigma, k_idx) - PSP(n, k_idx, sigma)) - inverse(w + mu - obe.H.H(sigma, k_idx)));
177 mpi::communicator comm = {};
178#pragma omp parallel for collapse(2) reduction(gf_sum : result) default(none) shared(obe, Glatt, comm)
179 for (
auto k_idx : mpi::chunk(range(obe.H.n_k()), comm)) {
180 for (
auto sigma : range(obe.C_space.n_sigma())) { result.data() += obe.H.k_weights(k_idx) * Glatt(k_idx, sigma).data(); }
182 result = mpi::all_reduce(result);
200 std::string method =
"dichotomy",
double precision = 1.e-5,
bool verbosity =
true) {
201 std::function<double(
double)> f = [&obe, beta](
double x) {
return density_nk(obe, x, beta); };
202 return std::get<0>(
triqs::root_finder(method, f, 0.0, target_density, precision, 0.5, 1000,
"Chemical Potential",
"Total Density", verbosity));
219 template <
typename Mesh>
221 block2_gf<Mesh, matrix_valued>
const &Sigma_dynamic, nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static,
222 std::string method =
"dichotomy",
double precision = 1.e-5,
bool verbosity =
true) {
223 std::function<double(
double)> f = [&obe, &Sigma_dynamic, &Sigma_static](
double x) {
return density(obe, x, Sigma_dynamic, Sigma_static); };
224 return std::get<0>(
triqs::root_finder(method, f, 0.0, target_density, precision, 0.5, 1000,
"Chemical Potential",
"Total Density", verbosity));
228 template double density(one_body_elements_on_grid
const &obe,
double mu, block2_gf<imfreq, matrix_valued>
const &Sigma_dynamic,
229 nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static);
231 block2_gf<imfreq, matrix_valued>
const &Sigma_dynamic,
232 nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static, std::string method =
"dichotomy",
233 double precision = 1.e-5,
bool verbosity =
true);
double find_chemical_potential(double const target_density, one_body_elements_on_grid const &obe, double beta, std::string method="dichotomy", double precision=1.e-5, bool verbosity=true)
Find the chemical potenital from the local Green's function given a target density.
double density(one_body_elements_on_grid const &obe, double mu, block2_gf< Mesh, matrix_valued > const &Sigma_dynamic, nda::array< nda::matrix< dcomplex >, 2 > const &Sigma_static)
Compute the density of the lattice Green's function with a self-energy using Woodbury.
void Ainv_B(nda::matrix< dcomplex, nda::F_layout > A, nda::matrix_view< dcomplex, nda::F_layout > B)
gf_struct_t get_struct(block_gf< Mesh > const &g)
double density_nk(one_body_elements_on_grid const &obe, double mu, double beta)
Compute number of particles n = ∑f(β(e(k)-μ))
std::pair< double, double > root_finder(std::string method, std::function< double(double)> f, double x_init, double y_value, double precision, double delta_x, long max_loops=1000, std::string x_name="", std::string y_name="", bool verbosity=false)
Root finder f(x) = 0.
static constexpr auto r_all
generator< std::pair< long, nda::range > > enumerated_sub_slices(auto sub_div)
std::complex< double > dcomplex
bool matrix_valued
Is the dispersion matrix-valued?
nda::array< double, 1 > k_weights
k_weights[k_idx]
long n_k() const
Number of k points in the grid.
long N_nu(long sigma, long k_idx) const
Number of bands #ν
nda::matrix_const_view< dcomplex > H(long sigma, long k_idx) const
H^σ(k)_ν, returned as a MATRIX in (ν, ν)
A one-body elements struct where all of the underlying data exists on a fixed momentum grid.