9#include <triqs/gfs.hpp>
10#include <itertools/omp_chunk.hpp>
11#include <triqs/lattice/brillouin_zone.hpp>
13#include <triqs/utility/root_finder.hpp>
17#pragma omp declare reduction(gf_sum : gf<imfreq, scalar_valued> : omp_out += omp_in) initializer(omp_priv = gf{omp_orig.mesh()})
18#pragma omp declare reduction(gf_sum : gf<dlr_imfreq, scalar_valued> : omp_out += omp_in) initializer(omp_priv = gf{omp_orig.mesh()})
23 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,
24 long sigma, nda::matrix_view<dcomplex> Y1, nda::matrix_view<dcomplex> Y2) {
27 auto YS = nda::matrix<dcomplex, nda::F_layout>::zeros(M, M);
30 auto Sigma = nda::matrix<dcomplex>{Sigma_dynamic(alpha, sigma)[om] + Sigma_static(alpha, sigma)};
31 nda::blas::gemm(-1, Y1(
r_all, R), Sigma, 0, YS(
r_all, R));
35 for (
auto m : range(M)) YS(m, m) += 1;
36 auto B = nda::matrix<dcomplex, nda::F_layout>{Y2(
r_all,
r_all)};
44 template <
typename Mesh>
45 auto trace_G_B_m_G_KS(one_body_elements_on_grid
const &obe,
double mu,
47 block2_gf<Mesh, matrix_valued>
const &Sigma_dynamic, nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static) {
49 auto M = obe.C_space.dim();
50 auto &mesh = Sigma_dynamic(0, 0).mesh();
51 auto omegas = mesh | tl::to<std::vector<dcomplex>>();
54 return [=, &obe, &Sigma_dynamic, &Sigma_static](
long sigma,
long k_idx) {
55 auto result = nda::zeros<dcomplex>(omegas.size());
56 auto Y1 = detail::G0_C_k_sigma(obe, mu, k_idx, sigma, omegas,
false);
57 auto Y2 = detail::G0_C_k_sigma(obe, mu, k_idx, sigma, omegas,
true);
59 for (
auto &&[n, om] : itertools::enumerate(mesh)) {
61 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));
66 auto [m, mp] = Sigma_dynamic(alpha, sigma).target_shape();
67 auto A = Sigma_dynamic(alpha, sigma).data()(n,
r_all,
r_all) + Sigma_static(alpha, sigma);
69 for (
auto m1 : range(m))
70 for (auto m2 : range(mp)) tr_Sigma_B += A(m1, m2) * C(m2, m1);
72 result(n) -= obe.H.k_weights(k_idx) * tr_Sigma_B;
91 template <
typename Mesh>
94 block2_gf<Mesh, matrix_valued>
const &Sigma_dynamic, nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static) {
96 if (obe.
H.
matrix_valued)
return density_for_matrix_valued_impl(obe, mu, Sigma_dynamic, Sigma_static);
98 auto n_sigma = Sigma_dynamic.size2();
99 auto n_k = obe.
H.
n_k();
100 auto const &mesh = Sigma_dynamic(0, 0).mesh();
101 double beta = mesh.beta();
102 auto corr = gf{mesh};
106 auto Fermi = [](
double x) {
return (x > 0 ? exp(-x) / (1 + exp(-x)) : 1 / (1 + exp(x))); };
107 auto calc_correction_term = detail::trace_G_B_m_G_KS(obe, mu, Sigma_dynamic, Sigma_static);
110 mpi::communicator comm = {};
111#pragma omp parallel for collapse(2) reduction(gf_sum : corr) reduction(+ : KS_term) default(none) \
112 shared(n_k, comm, n_sigma, obe, Fermi, mu, calc_correction_term, beta)
113 for (
auto k_idx : mpi::chunk(range(n_k), comm)) {
114 for (
auto sigma : range(n_sigma)) {
117 double KS_term_acc = 0;
118 auto eps = obe.
H.
H(sigma, k_idx);
119 for (
auto nu : range(obe.
H.
N_nu(sigma, k_idx)))
120 KS_term_acc += Fermi(beta * real(eps(nu, nu) - mu));
121 KS_term += obe.
H.
k_weights(k_idx) * KS_term_acc;
124 corr.data() += calc_correction_term(sigma, k_idx);
127 KS_term = mpi::all_reduce(KS_term);
128 corr = mpi::all_reduce(corr);
129 return KS_term + real(
density(corr));
134 double density_nk(one_body_elements_on_grid
const &obe,
double mu,
double beta);
148 template <
typename Mesh>
149 double density_for_matrix_valued_impl(one_body_elements_on_grid
const &obe,
double mu, block2_gf<Mesh, matrix_valued>
const &Sigma_dynamic,
150 nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static) {
152 auto const &mesh = Sigma_dynamic(0, 0).mesh();
153 auto beta = mesh.beta();
155 auto result = gf{mesh};
157 auto PSP = [&](
auto &iw,
auto &k_idx,
auto &sigma) {
158 auto N_nu = obe.H.N_nu(sigma, k_idx);
159 auto out = nda::zeros<dcomplex>(N_nu, N_nu);
162 auto P = obe.P.P(sigma, k_idx)(R,
r_all);
163 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;
168 auto Glatt = [&](
auto &k_idx,
auto &sigma) {
169 using nda::linalg::inv;
171 for (
auto &&[n, w] : enumerate(mesh)) {
172 out.data()(n) = trace(inv(w + mu - obe.H.H(sigma, k_idx) - PSP(n, k_idx, sigma)) - inv(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::utility::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::utility::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);
230 template double density(one_body_elements_on_grid
const &obe,
double mu, block2_gf<dlr_imfreq, matrix_valued>
const &Sigma_dynamic,
231 nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static);
233 block2_gf<imfreq, matrix_valued>
const &Sigma_dynamic,
234 nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static, std::string method =
"dichotomy",
235 double precision = 1.e-5,
bool verbosity =
true);
237 block2_gf<dlr_imfreq, matrix_valued>
const &Sigma_dynamic,
238 nda::array<nda::matrix<dcomplex>, 2>
const &Sigma_static, std::string method =
"dichotomy",
239 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_struct2_t get_struct(block2_gf< Mesh, matrix_valued > const &g)
double density_nk(one_body_elements_on_grid const &obe, double mu, double beta)
Compute number of particles .
static constexpr auto r_all
generator< std::pair< long, nda::range > > enumerated_sub_slices(auto sub_div)
std::complex< double > dcomplex
nda::array< long, 2 > dims
bool matrix_valued
Is the dispersion matrix-valued?
nda::array< double, 1 > k_weights
Weight in the BZ for each k-point.
long n_k() const
Number of k-points in the grid.
long N_nu(long sigma, long k_idx) const
Number of bands for a given k-point and spin .
nda::matrix_const_view< dcomplex > H(long sigma, long k_idx) const
Get for a given and .
A one-body elements struct where all of the underlying data exists on a fixed momentum grid.
band_dispersion H
Band dispersion.