TRIQS/TRIQS 4.0.0
Researching Interacting Quantum Systems
Loading...
Searching...
No Matches
fourier_matsubara.cpp
Go to the documentation of this file.
1// Copyright (c) 2013-2018 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2// Copyright (c) 2013-2018 Centre national de la recherche scientifique (CNRS)
3// Copyright (c) 2018-2023 Simons Foundation
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You may obtain a copy of the License at
16// https://www.gnu.org/licenses/gpl-3.0.txt
17//
18// Authors: Michel Ferrero, Laura Messio, Olivier Parcollet, Hugo U. R. Strand, Nils Wentzell
19
24
25#include "./fourier.hpp"
26#include "./fourier_common.hpp"
27
29
30#include <algorithm>
31#include <array>
32#include <cmath>
33#include <iostream>
34#include <string>
35
36namespace triqs::gfs {
37
38 namespace {
39
40 // a is read into an nda expression template (the result is computed lazily after the a* product), so it is
41 // deliberately not std::forward-ed into a sink.
42 // NOLINTBEGIN(cppcoreguidelines-missing-std-forward)
43 // NB : will return an expression template, but compute the number after a*
44 template <typename A> auto oneFermion(A &&a, double b, double tau, double beta) {
45 return -a * (b >= 0 ? exp(-b * tau) / (1 + exp(-beta * b)) : exp(b * (beta - tau)) / (1 + exp(beta * b)));
46 }
47
48 template <typename A> auto oneBoson(A &&a, double b, double tau, double beta) {
49 return a * (b >= 0 ? exp(-b * tau) / (exp(-beta * b) - 1) : exp(b * (beta - tau)) / (1 - exp(b * beta)));
50 }
51 // NOLINTEND(cppcoreguidelines-missing-std-forward)
52 } // namespace
53
54 //-------------------------------------
55
56 array<dcomplex, 2> fit_tail(gf_const_view<mesh::imtime, tensor_valued<1>> gt) {
57 using matrix_t = nda::matrix<dcomplex>;
58 int fit_order = 8;
59 auto _ = range::all;
60 auto d_vec_left = matrix_t(fit_order, gt.target_shape()[0]);
61 auto d_vec_right = d_vec_left;
62 long n_tau = gt.mesh().size();
63 for (long m : range(1, fit_order + 1)) {
64 d_vec_left(m - 1, _) = (gt[m] - gt[0]) / gt.mesh()[m]; // Values around 0
65 d_vec_right(m - 1, _) = (gt[n_tau - 1] - gt[n_tau - 1 - m]) / gt.mesh()[m]; // Values around beta
66 }
67
68 // Inverse of the Vandermonde matrix V_{m,j} = m^{j-1}
69 auto V_inv = matrix_t{{8.000000000008853, -28.000000000055913, 56.000000000151815, -70.00000000021936, 56.00000000020795, -28.000000000115904,
70 8.00000000003683, -1.0000000000049232},
71 {-13.742857142879107, 62.10000000013776, -133.5333333337073, 172.75000000055635, -141.00000000052023, 71.43333333362274,
72 -20.600000000091182, 2.592857142869304},
73 {9.694444444465196, -50.98055555568518, 119.55000000035201, -161.8888888894241, 135.86111111160685, -70.12500000027573,
74 20.494444444530682, -2.6055555555670558},
75 {-3.6555555555654573, 21.23472222228416, -53.60000000016842, 76.34027777803497, -66.27777777801586, 35.03750000013273,
76 -10.422222222263619, 1.3430555555610917},
77 {0.7986111111137331, -4.97222222223865, 13.312500000044732, -19.888888888957325, 17.923611111174495, -9.750000000035401,
78 2.9652777777888164, -0.38888888889036743},
79 {-0.1013888888892789, 0.6638888888913357, -1.862500000006671, 2.902777777787997, -2.71527777778725, 1.5250000000052975,
80 -0.47638888889054154, 0.06388888888911043},
81 {0.0069444444444749, -0.047222222222413485, 0.13750000000052204, -0.22222222222302282, 0.2152777777785205,
82 -0.12500000000041575, 0.04027777777790756, -0.00555555555557296},
83 {-0.0001984126984136688, 0.0013888888888949882, -0.00416666666668333, 0.006944444444470023, -0.006944444444468193,
84 0.004166666666679969, -0.0013888888888930434, 0.00019841269841325577}};
85
86 /*auto V_inv = matrix_t{
87 {7.000000000000351, -21.00000000000213, 35.00000000000503, -35.00000000000608, 21.000000000003606, -7.000000000001135, 1.000000000000174},
88 {-11.150000000000887, 43.95000000000505, -79.08333333334492, 82.00000000001403, -50.25000000000872, 16.98333333333621, -2.450000000000441},
89 {7.088888888889711, -32.74166666667111, 64.83333333334329, -70.69444444445642, 44.666666666674445, -15.408333333336019, 2.255555555555966},
90 {-2.312500000000358, 11.833333333335242, -25.395833333337578, 29.333333333338373, -19.270833333336668, 6.83333333333452,
91 -1.0208333333335127},
92 {0.409722222222303, -2.25000000000043, 5.145833333334286, -6.277777777778903, 4.3125000000007505, -1.5833333333336026,
93 0.24305555555559613},
94 {-0.037500000000009116, 0.21666666666671525, -0.5208333333334408, 0.6666666666667935, -0.47916666666675145, 0.18333333333336377,
95 -0.029166666666671254},
96 {0.001388888888889295, -0.008333333333335498, 0.02083333333333812, -0.027777777777783428, 0.020833333333337107, -0.008333333333334688,
97 0.0013888888888890932}};
98 */
99
100 // Calculate the 2nd
101 matrix_t g_vec_left = V_inv * d_vec_left;
102 matrix_t g_vec_right = V_inv * d_vec_right;
103 double sign = (gt.mesh().statistic() == Fermion) ? -1 : 1;
104 auto tail = make_zero_tail(gt, 4);
105 tail(1, _) = -(gt[0] - sign * gt[n_tau - 1]); // 1st order moment
106 tail(2, _) = g_vec_left(0, _) - sign * g_vec_right(0, _); // 2nd order moment
107 tail(3, _) = -(g_vec_left(1, _) + sign * g_vec_right(1, _)) * 2 / gt.mesh().delta(); // 3rd order moment
108 return tail;
109 }
110
111 // ------------------------ DIRECT TRANSFORM --------------------------------------------
112
113 gf_vec_t<mesh::imfreq> _fourier_impl(mesh::imfreq const &iw_mesh, gf_vec_cvt<mesh::imtime> gt, nda::array_const_view<dcomplex, 2> known_moments) {
114
115 nda::array<dcomplex, 2> tail;
116
117 if (known_moments.is_empty()) {
118 // A simple check on whether or not we are dealing with noisy data
119 auto dat = gt.data();
120 long n_tau = gt.mesh().size();
121 auto der_1 =
122 max_element(abs(dat(1, range::all) - dat(0, range::all)) + abs(dat(n_tau - 2, range::all) - dat(n_tau - 1, range::all))) / gt.mesh().delta();
123 auto der_2 = 0.5 * max_element(abs(dat(2, range::all) - dat(0, range::all)) + abs(dat(n_tau - 3, range::all) - dat(n_tau - 1, range::all)))
124 / gt.mesh().delta();
125 if (der_1 < 0.95 * der_2 or der_1 > 1.05 * der_2) {
126 std::cerr << "WARNING: Direct Fourier cannot deduce the high-frequency moments of G(tau) due to noise or a coarse tau-grid. \
127 Please specify the high-frequency moments for higher accuracy.\n";
128 return _fourier_impl(iw_mesh, gt, make_zero_tail(gt, 4));
129 } else {
130 return _fourier_impl(iw_mesh, gt, fit_tail(gt));
131 }
132 } else {
133 double _abs_tail0 = max_element(abs(known_moments(0, range::all)));
134 TRIQS_ASSERT2((_abs_tail0 < 1e-8),
135 "ERROR: Direct Fourier implementation requires vanishing 0th moment\n error is :" + std::to_string(_abs_tail0));
136
137 long n_known_moments = std::min<long>(known_moments.shape()[0], 4);
138 tail = make_zero_tail(gt, 4);
139 tail(range(n_known_moments), range::all) = known_moments(range(n_known_moments), range::all);
140 }
141
142 double beta = gt.mesh().beta();
143 auto L = gt.mesh().size() - 1;
144 if (L < 2 * (iw_mesh.last_index() + 1))
145 TRIQS_RUNTIME_ERROR << "Fourier: The time mesh mush be at least twice as long as the number of positive frequencies :\n gt.mesh().size() = "
146 << gt.mesh().size() << " gw.mesh().last_index()" << iw_mesh.last_index();
147
148 if (L < 6 * (iw_mesh.last_index() + 1))
149 std::cerr << "[Direct Fourier] WARNING: The imaginary time mesh is less than six times as long as the number of positive frequencies.\n"
150 << "This can lead to substantial numerical inaccuracies at the boundary of the frequency mesh.\n";
151
152 long n_others = second_dim(gt.data());
153
154 array<dcomplex, 2> _gout(L, n_others); // FIXME Why do we need this dimension to be one less than gt.mesh().size() ?
155 array<dcomplex, 2> _gin(L + 1, n_others);
156
157 bool is_fermion = (iw_mesh.statistic() == Fermion);
158 double fact = beta / static_cast<double>(L);
159 dcomplex iomega = M_PI * 1i / beta;
160
161 double b1 = 0, b2 = 0, b3 = 0;
162 array<dcomplex, 1> a1, a2, a3;
163 auto _ = range::all;
164 auto m1 = tail(1, _);
165 auto m2 = tail(2, _);
166 auto m3 = tail(3, _);
167
168 if (is_fermion) {
169 b1 = 0;
170 b2 = 1;
171 b3 = -1;
172 a1 = m1 - m3;
173 a2 = (m2 + m3) / 2;
174 a3 = (m3 - m2) / 2;
175
176 for (auto t : gt.mesh())
177 _gin(t.index(), _) =
178 fact * exp(iomega * t) * (gt[t] - (oneFermion(a1, b1, t, beta) + oneFermion(a2, b2, t, beta) + oneFermion(a3, b3, t, beta)));
179
180 } else {
181 b1 = -0.5;
182 b2 = -1;
183 b3 = 1;
184 a1 = 4 * (m1 - m3) / 3;
185 a2 = m3 - (m1 + m2) / 2;
186 a3 = m1 / 6 + m2 / 2 + m3 / 3;
187
188 for (auto t : gt.mesh())
189 _gin(t.index(), _) = fact * (gt[t] - (oneBoson(a1, b1, t, beta) + oneBoson(a2, b2, t, beta) + oneBoson(a3, b3, t, beta)));
190 }
191
192 std::array<int, 1> dims{static_cast<int>(L)};
193 _fourier_base(_gin, _gout, 1, dims.data(), static_cast<int>(n_others), FFTW_BACKWARD);
194
195 auto gw = gf_vec_t<mesh::imfreq>{iw_mesh, {n_others}};
196
197 // Correction term to account for proper Trapezoidal integration
198 // FIXME Avoid copy, by doing proper in-place operation
199 auto corr = -0.5 * fact * (gt[0] + m1 + (is_fermion ? 1 : -1) * gt[L]);
200 for (auto iw : iw_mesh) gw[iw] = _gout((iw.n + L) % L, _) + corr + a1 / (iw - b1) + a2 / (iw - b2) + a3 / (iw - b3);
201
202 return gw;
203 }
204
205 // ------------------------ INVERSE TRANSFORM --------------------------------------------
206
207 gf_vec_t<mesh::imtime> _fourier_impl(mesh::imtime const &tau_mesh, gf_vec_cvt<mesh::imfreq> gw, nda::array_const_view<dcomplex, 2> known_moments) {
208
209 TRIQS_ASSERT2(!gw.mesh().positive_only(), "Fourier is only implemented for g(i omega_n) with full mesh (positive and negative frequencies)");
210
211 nda::array_const_view<dcomplex, 2> tail;
212
213 // Assume vanishing 0th moment in tail fit
214 if (known_moments.is_empty()) return _fourier_impl(tau_mesh, gw, make_zero_tail(gw, 1));
215
216 double _abs_tail0 = max_element(abs(known_moments(0, range::all)));
217 TRIQS_ASSERT2((_abs_tail0 < 1e-8),
218 "ERROR: Inverse Fourier implementation requires vanishing 0th moment\n error is :" + std::to_string(_abs_tail0) + "\n");
219
220 if (known_moments.shape()[0] < 4) {
221 auto [t, err] = fit_tail(gw, known_moments);
222 TRIQS_ASSERT2((err < 1e-2),
223 "ERROR: High frequency moments have an error greater than 1e-2.\n Error = " + std::to_string(err)
224 + "\n Please make sure you treat the constant offset analytically!\n");
225 if (err > 1e-4)
226 std::cerr << "WARNING: High frequency moments have an error greater than 1e-4.\n Error = " << err
227 << "\n Please make sure you treat the constant offset analytically!\n";
228 TRIQS_ASSERT2((first_dim(t) > 3), "ERROR: Inverse Fourier implementation requires at least a proper 3rd high-frequency moment\n");
229 return _fourier_impl(tau_mesh, gw, t);
230 } else
231 tail.rebind(known_moments); // known_moments is fine
232
233 double beta = tau_mesh.beta();
234 long L = tau_mesh.size() - 1;
235 if (L < 2 * (gw.mesh().last_index() + 1))
236 TRIQS_RUNTIME_ERROR << "Inverse Fourier: The time mesh mush be at least twice as long as the freq mesh :\n gt.mesh().size() = "
237 << tau_mesh.size() << " gw.mesh().last_index()" << gw.mesh().last_index() << "\n";
238
239 long n_others = second_dim(gw.data());
240
241 array<dcomplex, 2> _gin(L, n_others); // FIXME Why do we need this dimension to be one less than gt.mesh().size() ?
242 array<dcomplex, 2> _gout(L + 1, n_others);
243
244 bool is_fermion = (gw.mesh().statistic() == Fermion);
245 double fact = 1.0 / beta;
246 dcomplex iomega = M_PI * 1i / beta;
247
248 double b1 = 0, b2 = 0, b3 = 0;
249 array<dcomplex, 1> a1, a2, a3;
250 auto _ = range::all;
251 auto m1 = tail(1, _);
252 auto m2 = tail(2, _);
253 auto m3 = tail(3, _);
254
255 if (is_fermion) {
256 b1 = 0;
257 b2 = 1;
258 b3 = -1;
259 a1 = m1 - m3;
260 a2 = (m2 + m3) / 2;
261 a3 = (m3 - m2) / 2;
262 } else {
263 b1 = -0.5;
264 b2 = -1;
265 b3 = 1;
266 a1 = 4 * (m1 - m3) / 3;
267 a2 = m3 - (m1 + m2) / 2;
268 a3 = m1 / 6 + m2 / 2 + m3 / 3;
269 }
270
271 for (auto iw : gw.mesh()) _gin((iw.n + L) % L, _) = fact * (gw[iw] - (a1 / (iw - b1) + a2 / (iw - b2) + a3 / (iw - b3)));
272
273 std::array<int, 1> dims{static_cast<int>(L)};
274 _fourier_base(_gin, _gout, 1, dims.data(), static_cast<int>(n_others), FFTW_FORWARD);
275
276 auto gt = gf_vec_t<mesh::imtime>{tau_mesh, {n_others}};
277
278 if (is_fermion)
279 for (auto t : tau_mesh)
280 gt[t] = _gout(t.index(), _) * exp(-iomega * t) + oneFermion(a1, b1, t, beta) + oneFermion(a2, b2, t, beta) + oneFermion(a3, b3, t, beta);
281 else
282 for (auto t : tau_mesh) gt[t] = _gout(t.index(), _) + oneBoson(a1, b1, t, beta) + oneBoson(a2, b2, t, beta) + oneBoson(a3, b3, t, beta);
283
284 double pm = (is_fermion ? -1 : 1);
285 gt[L] = pm * (gt[0] + m1);
286
287 return gt;
288 }
289
290} // namespace triqs::gfs
A read-only, non-owning view of a Green's function.
Provides the Fourier transform factories and the lazy fourier(...) assignment for Green's functions.
Declares the low-level FFTW wrapper shared by the Fourier transform implementations.
Provides tail fitting, slicing, inversion, reality and matrix-multiplication functions for Green's fu...
auto make_zero_tail(G const &g, int n_moments=10)
Create a zero-initialized tail object for a given Green function object.
std::pair< typename A::regular_type, double > fit_tail(G const &g, A const &known_moments={})
Fit the high-frequency tail of a Green's function using a least-squares procedure.
nda::matrix< double > matrix_t
Matrix type for transformations involving real and reciprocal space vectors.
int sign(statistic_enum s)
Get the sign associated with the given particle statistics.
Definition utils.hpp:171
#define TRIQS_RUNTIME_ERROR
Throw a triqs::runtime_error with the current source location.
#define TRIQS_ASSERT2(X,...)
Like TRIQS_ASSERT but lets the caller add a custom message.
string to_string(string const &str)
Identity overload for std::string.
Target type for a complex tensor-valued Green's function.
Definition targets.hpp:83