TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
gf.cpp
1// Copyright (c) 2017-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2017-2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2023 Simons Foundation
4// Copyright (c) 2017 Igor Krivenko
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You may obtain a copy of the License at
17// https://www.gnu.org/licenses/gpl-3.0.txt
18//
19// Authors: Philipp Dumitrescu, Michel Ferrero, Igor Krivenko, Olivier Parcollet, Nils Wentzell
20
21#include "../atom_diag.hpp"
22#include "../gf.hpp"
23#include "../../arrays.hpp"
24#include "../../gfs.hpp"
26
27#include <algorithm>
28#include <cmath>
29#include <limits>
30#include <utility>
31#include <vector>
32
33namespace triqs::atom_diag {
34
35 using namespace triqs::arrays;
36
37#define ATOM_DIAG atom_diag<Complex>
38#define ATOM_DIAG_R atom_diag<false>
39#define ATOM_DIAG_C atom_diag<true>
40#define ATOM_DIAG_T typename atom_diag<Complex>
41
42 // -----------------------------------------------------------------
43
44 // Generate Lehmann representation of GF defined by gf_struct
45 // passing every term to proc(int bl, int n1, int n2, double pole, scalar_t residue)
46 template <bool Complex, typename ProcessTerm>
47 inline void atomic_g_lehmann_impl(ATOM_DIAG const &atom, double beta, gf_struct_t const &gf_struct, excluded_states_t excluded_states,
48 ProcessTerm proc) {
49 // Sort excluded states to speed up lookups
50 std::sort(excluded_states.begin(), excluded_states.end());
51 auto is_excluded = [&excluded_states](int A, int ia) {
52 return std::binary_search(excluded_states.begin(), excluded_states.end(), std::make_pair(A, ia));
53 };
54
55 // Gibbs weights, exp(-beta E_i) / Z
56 std::vector<vector<double>> weights;
57 // Partition function
58 double z = 0;
59 int n_sp = atom.n_subspaces();
60 for (int A = 0; A < n_sp; ++A) {
61 int sp_dim = atom.get_subspace_dim(A);
62 weights.emplace_back(nda::zeros<double>(sp_dim));
63 for (int ia = 0; ia < sp_dim; ++ia) {
64 double w = std::exp(-beta * atom.get_eigenvalue(A, ia));
65 weights[A](ia) = w;
66 z += w;
67 }
68 }
69
70 for (auto &w : weights) w /= z;
71
72 auto const &fops = atom.get_fops();
73
74 // Generate all terms in Lehmann representation
75 int bl = 0;
76 for (auto const &[block, bl_size] : gf_struct) {
77
78 for (auto inner_index1 : range(bl_size))
79 for (auto inner_index2 : range(bl_size)) {
80 int n1 = fops[{block, inner_index1}]; // linear_index of c
81 int n2 = fops[{block, inner_index2}]; // linear_index of c_dag
82
83 for (int A = 0; A < n_sp; ++A) { // index of the A block. sum over all
84 int B = atom.cdag_connection(n2, A); // index of the block connected to A by operator c_n
85 if (B == -1 || atom.c_connection(n1, B) != A) continue; // no matrix element
86 for (int ia = 0; ia < atom.get_subspace_dim(A); ++ia) {
87 if (is_excluded(A, ia)) continue;
88 for (int ib = 0; ib < atom.get_subspace_dim(B); ++ib) {
89 if (is_excluded(B, ib)) continue;
90 auto residue = (weights[A](ia) + weights[B](ib)) * atom.c_matrix(n1, B)(ia, ib) * atom.cdag_matrix(n2, A)(ib, ia);
91 auto Ea = atom.get_eigenvalue(A, ia);
92 auto Eb = atom.get_eigenvalue(B, ib);
93
94 if (std::abs(residue) < std::numeric_limits<double>::epsilon()) continue;
95 proc(bl, inner_index1, inner_index2, Eb - Ea, residue);
96 }
97 }
98 }
99 }
100 ++bl;
101 }
102 }
103
104 // -----------------------------------------------------------------
105
106 // Construct and return Lehmann representation
107 template <bool Complex>
108 gf_lehmann_t<Complex> atomic_g_lehmann(ATOM_DIAG const &atom, double beta, gf_struct_t const &gf_struct, excluded_states_t excluded_states) {
109 // Prepare Lehmann GF container
110 gf_lehmann_t<Complex> lehmann;
111 lehmann.reserve(gf_struct.size());
112 for (auto const &[block, bl_size] : gf_struct) { lehmann.emplace_back(bl_size, bl_size); }
113
114 // Fill container
115 auto fill = [&lehmann](int bl, int n1, int n2, double pole, ATOM_DIAG_T::scalar_t residue) { lehmann[bl](n1, n2).emplace_back(pole, residue); };
116 atomic_g_lehmann_impl(atom, beta, gf_struct, excluded_states, fill);
117
118 return lehmann;
119 }
120 template gf_lehmann_t<false> atomic_g_lehmann(ATOM_DIAG_R const &, double, gf_struct_t const &, excluded_states_t);
121 template gf_lehmann_t<true> atomic_g_lehmann(ATOM_DIAG_C const &, double, gf_struct_t const &, excluded_states_t);
122
123 // -----------------------------------------------------------------
124
125 // In debug mode, check that Lehmann representation object is compatible with gf_struct
126 template <bool Complex, typename T>
127 inline void check_lehmann_struct([[maybe_unused]] gf_lehmann_t<Complex> const &lehmann, [[maybe_unused]] block_gf_view<T> g) {
128#ifndef NDEBUG
129 assert(lehmann.size() == g.size());
130 int bl = 0;
131 for (auto const &block : g) {
132 assert(block.target_shape() == lehmann[bl].shape());
133 ++bl;
134 }
135#endif
136 }
137
138 // -----------------------------------------------------------------
139
140 // Fill block_gf<T> object using precomputed Lehmann representation
141 template <bool Complex, typename T, typename ProcessTerm>
142 inline void fill_block_gf_from_lehmann(block_gf_view<T> g, gf_lehmann_t<Complex> const &lehmann, ProcessTerm proc) {
143 check_lehmann_struct<Complex>(lehmann, g);
144
145 int bl = 0;
146 for (auto &block : g) {
147 auto shape = block.target_shape();
148 for (auto n1 : range(shape[0]))
149 for (auto n2 : range(shape[1])) {
150 for (auto const &term : lehmann[bl](n1, n2)) proc(bl, n1, n2, term.first, term.second);
151 }
152 ++bl;
153 }
154 }
155
156 // -----------------------------------------------------------------
157
158 // Returns lambda-function compatible with atomic_g_lehmann_impl
159 // The lambda-function captures 'gf' and fills it when passed to atomic_g_lehmann_impl
160 template <bool Complex> auto make_term_proc(double beta, block_gf_view<imtime> gf) {
161 return [gf, beta](int bl, int n1, int n2, double pole, ATOM_DIAG_T::scalar_t residue) mutable {
163
164 auto w = -residue / (pole > 0 ? (1 + std::exp(-beta * pole)) : (std::exp(beta * pole) + 1));
165
166 // Set data points
167 for (auto tau : g.mesh()) g[tau] += w * (pole > 0 ? std::exp(-double(tau) * pole) : std::exp((beta - double(tau)) * pole));
168 };
169 }
170
171 // -----------------------------------------------------------------
172
173 // G(\tau) from Lehmann representation
174 template <bool Complex>
175 block_gf<imtime> atomic_g_tau(gf_lehmann_t<Complex> const &lehmann, gf_struct_t const &gf_struct, mesh::imtime const &mesh) {
176 double beta = mesh.beta();
177 auto g = block_gf{mesh, gf_struct};
178 fill_block_gf_from_lehmann<Complex>(g(), lehmann, make_term_proc<Complex>(beta, g()));
179 return g;
180 }
181 template block_gf<imtime> atomic_g_tau<false>(gf_lehmann_t<false> const &, gf_struct_t const &, mesh::imtime const &);
182 template block_gf<imtime> atomic_g_tau<true>(gf_lehmann_t<true> const &, gf_struct_t const &, mesh::imtime const &);
183
184 // -----------------------------------------------------------------
185
186 // G(\tau) from atom_diag
187 template <bool Complex>
188 block_gf<imtime> atomic_g_tau(ATOM_DIAG const &atom, double beta, gf_struct_t const &gf_struct, int n_tau,
189 excluded_states_t const &excluded_states) {
190 auto g = block_gf<imtime>{{beta, Fermion, n_tau}, gf_struct};
191 atomic_g_lehmann_impl(atom, beta, gf_struct, excluded_states, make_term_proc<Complex>(beta, g()));
192 return g;
193 }
194 template block_gf<imtime> atomic_g_tau(ATOM_DIAG_R const &, double, gf_struct_t const &, int, excluded_states_t const &);
195 template block_gf<imtime> atomic_g_tau(ATOM_DIAG_C const &, double, gf_struct_t const &, int, excluded_states_t const &);
196
197 // -----------------------------------------------------------------
198
199 // Returns lambda-function compatible with atomic_g_lehmann_impl
200 // The lambda-function captures 'gf' and fills it when passed to atomic_g_lehmann_impl
201 template <bool Complex> auto make_term_proc(block_gf_view<imfreq> gf) {
202 return [gf](int bl, int n1, int n2, double pole, ATOM_DIAG_T::scalar_t residue) mutable {
203 gf_view<imfreq, scalar_valued> g = slice_target_to_scalar(gf[bl], n1, n2);
204
205 // Set data points
206 for (auto iw : g.mesh()) g[iw] += residue / (iw - pole);
207 };
208 }
209
210 // -----------------------------------------------------------------
211
212 // G(i\omega) from Lehmann representation
213 template <bool Complex> block_gf<imfreq> atomic_g_iw(gf_lehmann_t<Complex> const &lehmann, gf_struct_t const &gf_struct, mesh::imfreq const &mesh) {
214 auto g = block_gf{mesh, gf_struct};
215 fill_block_gf_from_lehmann<Complex>(g(), lehmann, make_term_proc<Complex>(g()));
216 return g;
217 }
218 template block_gf<imfreq> atomic_g_iw<false>(gf_lehmann_t<false> const &, gf_struct_t const &, mesh::imfreq const &);
219 template block_gf<imfreq> atomic_g_iw<true>(gf_lehmann_t<true> const &, gf_struct_t const &, mesh::imfreq const &);
220
221 // -----------------------------------------------------------------
222
223 // G(i\omega) from atom_diag
224 template <bool Complex>
225 block_gf<imfreq> atomic_g_iw(ATOM_DIAG const &atom, double beta, gf_struct_t const &gf_struct, int n_iw, excluded_states_t const &excluded_states) {
226 auto g = block_gf<imfreq>{{beta, Fermion, n_iw}, gf_struct};
227 atomic_g_lehmann_impl(atom, beta, gf_struct, excluded_states, make_term_proc<Complex>(g()));
228 return g;
229 }
230 template block_gf<imfreq> atomic_g_iw(ATOM_DIAG_R const &, double, gf_struct_t const &, int, excluded_states_t const &);
231 template block_gf<imfreq> atomic_g_iw(ATOM_DIAG_C const &, double, gf_struct_t const &, int, excluded_states_t const &);
232
233 // -----------------------------------------------------------------
234
235 // Returns lambda-function compatible with atomic_g_lehmann_impl
236 // The lambda-function captures 'gf' and fills it when passed to atomic_g_lehmann_impl
237 template <bool Complex> auto make_term_proc(double beta, block_gf_view<legendre> gf) {
238 return [gf, beta](int bl, int n1, int n2, double pole, ATOM_DIAG_T::scalar_t residue) mutable {
239 gf_view<legendre, scalar_valued> g = slice_target_to_scalar(gf[bl], n1, n2);
240
241 // Set data points
242 double x = beta * pole / 2;
243 double w = -beta / (2 * std::cosh(x));
244 for (auto l : g.mesh()) {
245 g[l] += residue * w * std::sqrt(2 * l.index() + 1) * (l.index() % 2 == 0 ? 1 : std::copysign(1, -x))
246 * triqs::utility::mod_cyl_bessel_i(static_cast<int>(l.index()), std::abs(x));
247 }
248 };
249 }
250
251 // -----------------------------------------------------------------
252
253 // G_\ell from Lehmann representation
254 template <bool Complex>
255 block_gf<legendre> atomic_g_l(gf_lehmann_t<Complex> const &lehmann, gf_struct_t const &gf_struct, mesh::legendre const &mesh) {
256 double beta = mesh.beta();
257 auto g = block_gf{mesh, gf_struct};
258 fill_block_gf_from_lehmann<Complex>(g(), lehmann, make_term_proc<Complex>(beta, g()));
259 return g;
260 }
261 template block_gf<legendre> atomic_g_l<false>(gf_lehmann_t<false> const &, gf_struct_t const &, mesh::legendre const &);
262 template block_gf<legendre> atomic_g_l<true>(gf_lehmann_t<true> const &, gf_struct_t const &, mesh::legendre const &);
263
264 // -----------------------------------------------------------------
265
266 // G_\ell from atom_diag
267 template <bool Complex>
268 block_gf<legendre> atomic_g_l(ATOM_DIAG const &atom, double beta, gf_struct_t const &gf_struct, int n_l, excluded_states_t const &excluded_states) {
269 auto g = block_gf<legendre>{{beta, Fermion, n_l}, gf_struct};
270 atomic_g_lehmann_impl(atom, beta, gf_struct, excluded_states, make_term_proc<Complex>(beta, g()));
271 return g;
272 }
273 template block_gf<legendre> atomic_g_l(ATOM_DIAG_R const &, double, gf_struct_t const &, int, excluded_states_t const &);
274 template block_gf<legendre> atomic_g_l(ATOM_DIAG_C const &, double, gf_struct_t const &, int, excluded_states_t const &);
275
276 // -----------------------------------------------------------------
277
278 // Returns lambda-function compatible with atomic_g_lehmann_impl
279 // The lambda-function captures 'gf' and fills it when passed to atomic_g_lehmann_impl
280 template <bool Complex> auto make_term_proc(block_gf_view<refreq> gf, double broadening) {
281 return [gf, broadening](int bl, int n1, int n2, double pole, ATOM_DIAG_T::scalar_t residue) mutable {
282 gf_view<refreq, scalar_valued> g = slice_target_to_scalar(gf[bl], n1, n2);
283
284 // Set data points
285 for (auto w : g.mesh()) g[w] += residue / (w + 1i * broadening - pole);
286 };
287 }
288
289 // -----------------------------------------------------------------
290
291 // G(\omega) from Lehmann representation
292 template <bool Complex>
293 block_gf<refreq> atomic_g_w(gf_lehmann_t<Complex> const &lehmann, gf_struct_t const &gf_struct, mesh::refreq const &mesh, double broadening) {
294 auto g = block_gf{mesh, gf_struct};
295 fill_block_gf_from_lehmann<Complex>(g(), lehmann, make_term_proc<Complex>(g(), broadening));
296 return g;
297 }
298 template block_gf<refreq> atomic_g_w<false>(gf_lehmann_t<false> const &, gf_struct_t const &, mesh::refreq const &, double);
299 template block_gf<refreq> atomic_g_w<true>(gf_lehmann_t<true> const &, gf_struct_t const &, mesh::refreq const &, double);
300
301 // -----------------------------------------------------------------
302
303 // G(\omega) from atom_diag
304 template <bool Complex>
305 block_gf<refreq> atomic_g_w(ATOM_DIAG const &atom, double beta, gf_struct_t const &gf_struct, std::pair<double, double> const &energy_window,
306 int n_w, double broadening, excluded_states_t const &excluded_states) {
307 auto g = block_gf<refreq>{{energy_window.first, energy_window.second, n_w}, gf_struct};
308 atomic_g_lehmann_impl(atom, beta, gf_struct, excluded_states, make_term_proc<Complex>(g(), broadening));
309 return g;
310 }
311 template block_gf<refreq> atomic_g_w(ATOM_DIAG_R const &, double, gf_struct_t const &, std::pair<double, double> const &, int, double,
312 excluded_states_t const &);
313 template block_gf<refreq> atomic_g_w(ATOM_DIAG_C const &, double, gf_struct_t const &, std::pair<double, double> const &, int, double,
314 excluded_states_t const &);
315
316} // namespace triqs::atom_diag
gf_struct_t gf_struct() const
Get the block structure.
Backward-compatibility umbrella header pulling in the nda array library.
Provides a lightweight exact diagonalization solver for fermionic Hamiltonians.
Build the atomic Green's function from a solved diagonalization problem on different meshes.
A non-owning view of a block Green's function.
The owning block Green's function container.
Definition block_gf.hpp:259
A mutable, non-owning view of a Green's function.
Definition gf_view.hpp:48
The owning Green's function container.
Definition gf.hpp:194
double beta() const noexcept
Get the inverse temperature .
Definition imtime.hpp:88
double beta() const noexcept
Get the inverse temperature .
Definition legendre.hpp:188
Umbrella header for the Green's function library.
std::vector< matrix< gf_scalar_lehmann_t< Complex > > > gf_lehmann_t
Lehmann representation of a block matrix-valued Green's function.
Definition gf.hpp:66
std::vector< std::pair< int, int > > excluded_states_t
List of excluded eigenstates.
Definition gf.hpp:74
block_gf< legendre > atomic_g_l(gf_lehmann_t< Complex > const &lehmann, gf_struct_t const &gf_struct, legendre const &mesh)
Build the atomic Green's function in the Legendre basis from a precomputed Lehmann representation.
block_gf< refreq > atomic_g_w(gf_lehmann_t< Complex > const &lehmann, gf_struct_t const &gf_struct, refreq const &mesh, double broadening=0)
Build the atomic retarded Green's function on a real-frequency mesh from a precomputed Lehmann repres...
block_gf< imtime > atomic_g_tau(gf_lehmann_t< Complex > const &lehmann, gf_struct_t const &gf_struct, imtime const &mesh)
Build the atomic imaginary-time Green's function from a precomputed Lehmann representation.
block_gf< imfreq > atomic_g_iw(gf_lehmann_t< Complex > const &lehmann, gf_struct_t const &gf_struct, imfreq const &mesh)
Build the atomic Matsubara Green's function from a precomputed Lehmann representation.
gf_lehmann_t< Complex > atomic_g_lehmann(atom_diag< Complex > const &atom, double beta, gf_struct_t const &gf_struct, excluded_states_t excluded_states={})
Build the Lehmann representation of the atomic Green's function.
Definition gf.cpp:108
std::vector< std::pair< std::string, long > > gf_struct_t
Type describing the structure of a block Green's function.
Definition gf_struct.hpp:41
auto slice_target_to_scalar(G &&g, Args &&...1)
Slice the target of a matrix-valued Green's function down to a scalar-valued one.
double mod_cyl_bessel_i(int n, double x)
Get the modified spherical bessel function of the first kind of order evaluated at .
Definition legendre.cpp:68
constexpr nda::clef::placeholder< 3 > w
Placeholder for the frequency .
Provides Legendre polynomials and related functions.