TRIQS/triqs_modest 3.3.0
Modular Electronic Structure Toolkit
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 <triqs/utility/root_finder.hpp>
14
15namespace triqs::modest {
16
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()})
19
20 // ============================================
21 namespace detail {
22 // impl detail : compute (1- Y1 Sigma)^{-1} Y2 in M x M space, with Sigma by block
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) {
25 // Y Sigma. NB Sigma is by blocks.
26 // NB: F_layout because of getrs in Ainv_B below
27 auto YS = nda::matrix<dcomplex, nda::F_layout>::zeros(M, M);
28
29 for (auto &&[alpha, R] : enumerated_sub_slices(embedding_decomp)) {
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));
32 }
33
34 // Z = (1 -YS)^{-1} * Y
35 for (auto m : range(M)) YS(m, m) += 1;
36 auto B = nda::matrix<dcomplex, nda::F_layout>{Y2(r_all, r_all)};
37 //B = inverse(YS) * B;
38 Ainv_B(YS, B);
39 return B;
40 }
41
42 //-------------------------------------------------------------------------------------------
43 // 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
44 template <typename Mesh>
45 auto trace_G_B_m_G_KS(one_body_elements_on_grid const &obe, double mu,
46 // add magnetic field,
47 block2_gf<Mesh, matrix_valued> const &Sigma_dynamic, nda::array<nda::matrix<dcomplex>, 2> const &Sigma_static) {
48
49 auto M = obe.C_space.dim();
50 auto &mesh = Sigma_dynamic(0, 0).mesh();
51 auto omegas = mesh | tl::to<std::vector<dcomplex>>();
52 auto embedding_decomp = get_struct(Sigma_dynamic).dims(r_all, 0) | tl::to<std::vector>();
53
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); // Y1 = G0_𝓒
57 auto Y2 = detail::G0_C_k_sigma(obe, mu, k_idx, sigma, omegas, true); // Y2 = + ∂_μ G0_𝓒
58
59 for (auto &&[n, om] : itertools::enumerate(mesh)) {
60 // Compute (1- Y1 Sigma)^{-1} Y2
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));
62
63 // Tr (Sigma * B)
64 dcomplex tr_Sigma_B = 0;
65 for (auto &&[alpha, R] : enumerated_sub_slices(embedding_decomp)) {
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);
68 auto C = B(R, R);
69 for (auto m1 : range(m))
70 for (auto m2 : range(mp)) tr_Sigma_B += A(m1, m2) * C(m2, m1);
71 }
72 result(n) -= obe.H.k_weights(k_idx) * tr_Sigma_B;
73 }
74 return result;
75 };
76 }
77 } // namespace detail
78
79 //-------------------------------------------------------------------------------------------
91 template <typename Mesh>
92 double density(one_body_elements_on_grid const &obe, double mu,
93 // add magnetic field,
94 block2_gf<Mesh, matrix_valued> const &Sigma_dynamic, nda::array<nda::matrix<dcomplex>, 2> const &Sigma_static) {
95
96 if (obe.H.matrix_valued) return density_for_matrix_valued_impl(obe, mu, Sigma_dynamic, Sigma_static);
97
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}; // Compute the correction term on all dlr mesh points
103 double KS_term = 0; // The first Kohn Sham term, cf Notes
104
105 // OP : we don't have a Fermi function in TRIQS ??
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);
108
109 // ---------
110 mpi::communicator comm = {}; // for now using default comm in MPI
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)) {
115
116 // 1- KS term
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))) // we must cut at N_nu, do not add nu with eps =0
120 KS_term_acc += Fermi(beta * real(eps(nu, nu) - mu)); // FIXME : H SHOULD BE REAL in OBE !!
121 KS_term += obe.H.k_weights(k_idx) * KS_term_acc;
122
123 // 2- Correction term
124 corr.data() += calc_correction_term(sigma, k_idx);
125 }
126 }
127 KS_term = mpi::all_reduce(KS_term);
128 corr = mpi::all_reduce(corr);
129 return KS_term + real(density(corr));
130 }
131
132 // ------------------------------------------------------------------------------------
134 double density_nk(one_body_elements_on_grid const &obe, double mu, double beta);
135
136 // ------------------------------------------------------------------------------------
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) {
151 // nda::array<nda::matrix<double>, 2> const &Sigma_DC) {
152 auto const &mesh = Sigma_dynamic(0, 0).mesh();
153 auto beta = mesh.beta();
154
155 auto result = gf{mesh}; // Compute the correction term on all dlr mesh points
156
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);
160 auto embedding_decomp = get_struct(Sigma_dynamic).dims(r_all, 0) | tl::to<std::vector>();
161 for (auto &&[alpha, R] : enumerated_sub_slices(embedding_decomp)) {
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;
164 }
165 return out;
166 };
167
168 auto Glatt = [&](auto &k_idx, auto &sigma) {
169 using nda::linalg::inv;
170 auto out = gf{mesh};
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)));
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::utility::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::utility::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 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);
232 template double find_chemical_potential(double const target_density, one_body_elements_on_grid const &obe,
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);
236 template double find_chemical_potential(double const target_density, one_body_elements_on_grid const &obe,
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);
242} // 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:92
void Ainv_B(nda::matrix< dcomplex, nda::F_layout > A, nda::matrix_view< dcomplex, nda::F_layout > B)
Definition nda_supp.hpp:12
gf_struct2_t get_struct(block2_gf< Mesh, matrix_valued > const &g)
Definition gf_supp.hpp:52
double density_nk(one_body_elements_on_grid const &obe, double mu, double beta)
Compute number of particles .
Definition density.cpp:29
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
nda::array< long, 2 > dims
Definition gf_supp.hpp:37
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.