TRIQS/triqs_modest 3.3.0
Brillouin zone summation
Loading...
Searching...
No Matches
density.hpp
Go to the documentation of this file.
1// Copyright (c) 2025--present, The Simons Foundation
2// This file is part of TRIQS/modest and is licensed under the terms of GPLv3 or later.
3// SPDX-License-Identifier: GPL-3.0-or-later
4// See LICENSE in the root of this distribution for details.
5
6#pragma once
7//#include <mpi/generic_communication.hpp>
8#include <nda/nda.hpp>
9#include <triqs/gfs.hpp>
10#include <itertools/omp_chunk.hpp>
11#include <triqs/lattice/brillouin_zone.hpp>
12#include "./downfolding.hpp"
13#include "./root_finder.hpp"
14#include "utils/nda_supp.hpp"
15
16namespace triqs::modest {
17
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()})
20
21 // ============================================
22 namespace detail {
23 // impl detail : compute (1- Y1 Sigma)^{-1} Y2 in M x M space, with Sigma by block
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) {
26 // Y Sigma. NB Sigma is by blocks.
27 // NB: F_layout because of getrs in Ainv_B below
28 auto YS = nda::matrix<dcomplex, nda::F_layout>::zeros(M, M);
29
30 for (auto &&[alpha, R] : enumerated_sub_slices(embedding_decomp)) {
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));
33 }
34
35 // Z = (1 -YS)^{-1} * Y
36 for (auto m : range(M)) YS(m, m) += 1;
37 auto B = nda::matrix<dcomplex, nda::F_layout>{Y2(r_all, r_all)};
38 //B = inverse(YS) * B;
39 Ainv_B(YS, B);
40 return B;
41 }
42
43 //-------------------------------------------------------------------------------------------
44 // Returns a lambda (sigma, k_idx ) -> [Tr_nu G_B(k, omega) - Tr_nu G_KS(k, omega) for omega in Sigma.mesh] as nda::array
45 template <typename Mesh>
46 auto trace_G_B_m_G_KS(one_body_elements_on_grid const &obe, double mu,
47 // add magnetic field,
48 block2_gf<Mesh, matrix_valued> const &Sigma_dynamic, nda::array<nda::matrix<dcomplex>, 2> const &Sigma_static) {
49
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>();
54
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); // Y1 = G0_𝓒
58 auto Y2 = detail::G0_C_k_sigma(obe, mu, k_idx, sigma, omegas, true); // Y2 = + ∂_μ G0_𝓒
59
60 for (auto &&[n, om] : itertools::enumerate(mesh)) {
61 // Compute (1- Y1 Sigma)^{-1} Y2
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));
63
64 // Tr (Sigma * B)
65 dcomplex tr_Sigma_B = 0;
66 for (auto &&[alpha, R] : enumerated_sub_slices(embedding_decomp)) {
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);
69 auto C = B(R, R);
70 for (auto m1 : range(m))
71 for (auto m2 : range(mp)) tr_Sigma_B += A(m1, m2) * C(m2, m1);
72 }
73 result(n) -= obe.H.k_weights(k_idx) * tr_Sigma_B;
74 }
75 return result;
76 };
77 }
78 } // namespace detail
79
80 //-------------------------------------------------------------------------------------------
92 template <typename Mesh>
93 double density(one_body_elements_on_grid const &obe, double mu,
94 // add magnetic field,
95 block2_gf<Mesh, matrix_valued> const &Sigma_dynamic, nda::array<nda::matrix<dcomplex>, 2> const &Sigma_static) {
96
97 if (obe.H.matrix_valued) return density_for_matrix_valued_impl(obe, mu, Sigma_dynamic, Sigma_static);
98
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}; // Compute the correction term on all dlr mesh points
104 double KS_term = 0; // The first Kohn Sham term, cf Notes
105
106 // OP : we don't have a Fermi function in TRIQS ??
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);
109
110 // ---------
111 mpi::communicator comm = {}; // for now using default comm in MPI
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)) {
116
117 // 1- KS term
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))) // we must cut at N_nu, do not add nu with eps =0
121 KS_term_acc += Fermi(beta * real(eps(nu, nu) - mu)); // FIXME : H SHOULD BE REAL in OBE !!
122 KS_term += obe.H.k_weights(k_idx) * KS_term_acc;
123
124 // 2- Correction term
125 corr.data() += calc_correction_term(sigma, k_idx);
126 }
127 }
128 KS_term = mpi::all_reduce(KS_term);
129 corr = mpi::all_reduce(corr);
130 return KS_term + real(density(corr));
131 }
132
133 // ------------------------------------------------------------------------------------
135 double density_nk(one_body_elements_on_grid const &obe, double mu, double beta);
136
137 // ------------------------------------------------------------------------------------
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) {
152 // nda::array<nda::matrix<double>, 2> const &Sigma_DC) {
153 auto const &mesh = Sigma_dynamic(0, 0).mesh();
154 auto beta = mesh.beta();
155
156 auto result = gf{mesh}; // Compute the correction term on all dlr mesh points
157
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>();
162 for (auto &&[alpha, R] : enumerated_sub_slices(embedding_decomp)) {
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;
165 }
166 return out;
167 };
168
169 auto Glatt = [&](auto &k_idx, auto &sigma) {
170 auto out = gf{mesh};
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)));
173 }
174 return out;
175 };
176
177 mpi::communicator comm = {}; // for now using default comm in MPI
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(); }
181 }
182 result = mpi::all_reduce(result);
183 return density_nk(obe, mu, beta) + real(density(result));
184 }
199 inline double find_chemical_potential(double const target_density, one_body_elements_on_grid const &obe, double beta,
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));
203 }
204
219 template <typename Mesh>
220 double find_chemical_potential(double const target_density, one_body_elements_on_grid const &obe,
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));
225 }
226
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 find_chemical_potential(double const target_density, one_body_elements_on_grid const &obe,
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);
236} // namespace triqs::modest
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.
Definition density.hpp:199
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.
Definition density.hpp:93
void Ainv_B(nda::matrix< dcomplex, nda::F_layout > A, nda::matrix_view< dcomplex, nda::F_layout > B)
Definition nda_supp.hpp:12
gf_struct_t get_struct(block_gf< Mesh > const &g)
Definition gf_supp.hpp:51
double density_nk(one_body_elements_on_grid const &obe, double mu, double beta)
Compute number of particles n = ∑f(β(e(k)-μ))
Definition density.cpp:29
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
Definition defs.hpp:40
generator< std::pair< long, nda::range > > enumerated_sub_slices(auto sub_div)
std::complex< double > dcomplex
Definition nda_supp.hpp:8
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.