TRIQS/triqs_tprf 4.0.0
A TRIQS application
Loading...
Searching...
No Matches
fourier_matsubara.cpp
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-2020 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, tayral
19
20#include "./fourier_common.hpp"
21
22#include <fftw3.h>
23
24using namespace triqs;
25
26namespace triqs_tprf::fourier {
27
28 namespace {
29
30 // NB : will return an expression template, but compute the number after a*
31 template <typename A> auto oneFermion(A &&a, double b, double tau, double beta) {
32 return -a * (b >= 0 ? exp(-b * tau) / (1 + exp(-beta * b)) : exp(b * (beta - tau)) / (1 + exp(beta * b)));
33 }
34
35 template <typename A> auto oneBoson(A &&a, double b, double tau, double beta) {
36 return a * (b >= 0 ? exp(-b * tau) / (exp(-beta * b) - 1) : exp(b * (beta - tau)) / (1 - exp(b * beta)));
37 }
38 } // namespace
39
40 //-------------------------------------
41
42 array<dcomplex, 2> fit_derivatives(gf_const_view<imtime, tensor_valued<1>> gt) {
43 using matrix_t = arrays::matrix<dcomplex>;
44 int fit_order = 8;
45 auto _ = range::all;
46 auto d_vec_left = matrix_t(fit_order, gt.target_shape()[0]);
47 auto d_vec_right = d_vec_left;
48 int n_tau = gt.mesh().size();
49 for (int m : range(1, fit_order + 1)) {
50 d_vec_left(m - 1, _) = (gt[m] - gt[0]) / gt.mesh()[m]; // Values around 0
51 d_vec_right(m - 1, _) = (gt[n_tau - 1] - gt[n_tau - 1 - m]) / gt.mesh()[m]; // Values around beta
52 }
53
54 // Inverse of the Vandermonde matrix V_{m,j} = m^{j-1}
55 // FIXME : add here teh python code that generated it
56 auto V_inv = matrix_t{{8.000000000008853, -28.000000000055913, 56.000000000151815, -70.00000000021936, 56.00000000020795, -28.000000000115904,
57 8.00000000003683, -1.0000000000049232},
58 {-13.742857142879107, 62.10000000013776, -133.5333333337073, 172.75000000055635, -141.00000000052023, 71.43333333362274,
59 -20.600000000091182, 2.592857142869304},
60 {9.694444444465196, -50.98055555568518, 119.55000000035201, -161.8888888894241, 135.86111111160685, -70.12500000027573,
61 20.494444444530682, -2.6055555555670558},
62 {-3.6555555555654573, 21.23472222228416, -53.60000000016842, 76.34027777803497, -66.27777777801586, 35.03750000013273,
63 -10.422222222263619, 1.3430555555610917},
64 {0.7986111111137331, -4.97222222223865, 13.312500000044732, -19.888888888957325, 17.923611111174495, -9.750000000035401,
65 2.9652777777888164, -0.38888888889036743},
66 {-0.1013888888892789, 0.6638888888913357, -1.862500000006671, 2.902777777787997, -2.71527777778725, 1.5250000000052975,
67 -0.47638888889054154, 0.06388888888911043},
68 {0.0069444444444749, -0.047222222222413485, 0.13750000000052204, -0.22222222222302282, 0.2152777777785205,
69 -0.12500000000041575, 0.04027777777790756, -0.00555555555557296},
70 {-0.0001984126984136688, 0.0013888888888949882, -0.00416666666668333, 0.006944444444470023, -0.006944444444468193,
71 0.004166666666679969, -0.0013888888888930434, 0.00019841269841325577}};
72
73 /*auto V_inv = matrix_t{
74 {7.000000000000351, -21.00000000000213, 35.00000000000503, -35.00000000000608, 21.000000000003606, -7.000000000001135, 1.000000000000174},
75 {-11.150000000000887, 43.95000000000505, -79.08333333334492, 82.00000000001403, -50.25000000000872, 16.98333333333621, -2.450000000000441},
76 {7.088888888889711, -32.74166666667111, 64.83333333334329, -70.69444444445642, 44.666666666674445, -15.408333333336019, 2.255555555555966},
77 {-2.312500000000358, 11.833333333335242, -25.395833333337578, 29.333333333338373, -19.270833333336668, 6.83333333333452,
78 -1.0208333333335127},
79 {0.409722222222303, -2.25000000000043, 5.145833333334286, -6.277777777778903, 4.3125000000007505, -1.5833333333336026,
80 0.24305555555559613},
81 {-0.037500000000009116, 0.21666666666671525, -0.5208333333334408, 0.6666666666667935, -0.47916666666675145, 0.18333333333336377,
82 -0.029166666666671254},
83 {0.001388888888889295, -0.008333333333335498, 0.02083333333333812, -0.027777777777783428, 0.020833333333337107, -0.008333333333334688,
84 0.0013888888888890932}};
85 */
86
87 // Calculate the 2nd
88 matrix_t g_vec_left = V_inv * d_vec_left;
89 matrix_t g_vec_right = V_inv * d_vec_right;
90 double sign = (gt.mesh().statistic() == Fermion) ? -1 : 1;
91 array<dcomplex, 2> m23(2, g_vec_left.shape()[1]);
92 m23(0, _) = g_vec_left(0, _) - sign * g_vec_right(0, _);
93 m23(1, _) = -(g_vec_left(1, _) + sign * g_vec_right(1, _)) * 2 / gt.mesh().delta();
94 // TRIQS_PRINT(m23(0,_));
95 // TRIQS_PRINT(m23(1,_));
96 return m23;
97 }
98
99 // ------------------------ DIRECT TRANSFORM
100 // --------------------------------------------
101
102 fourier_plan _fourier_plan(mesh::imfreq const &iw_mesh, gf_vec_cvt<imtime> gt) {
103
104 auto L = gt.mesh().size() - 1;
105 if (L < 2 * (iw_mesh.last_index() + 1))
106 TRIQS_RUNTIME_ERROR << "Fourier: The time mesh mush be at least twice as long as the "
107 "number of positive frequencies :\n gt.mesh().size() = "
108 << gt.mesh().size() << " gw.mesh().last_index()" << iw_mesh.last_index();
109
110 int n_others = gt.data().shape()[1];
111
112 array<dcomplex, 2> _gout(L,
113 n_others); // FIXME Why do we need this dimension to
114 // be one less than gt.mesh().size() ?
115 array<dcomplex, 2> _gin(L + 1, n_others);
116
117 int dims[] = {int(L)};
118 auto plan = _fourier_base_plan(_gin, _gout, 1, dims, n_others, FFTW_BACKWARD);
119 //return std::move(plan);
120 return plan;
121 }
122
123 gf_vec_t<imfreq> _fourier_impl(mesh::imfreq const &iw_mesh, gf_vec_cvt<imtime> gt, fourier_plan &p, arrays::array_const_view<dcomplex, 2> mom_23) {
124 if (mom_23.is_empty()) return _fourier_impl(iw_mesh, gt, p, fit_derivatives(gt));
125
126 double beta = gt.mesh().beta();
127 auto L = gt.mesh().size() - 1;
128 if (L < 2 * (iw_mesh.last_index() + 1))
129 TRIQS_RUNTIME_ERROR << "Fourier: The time mesh mush be at least twice as long as the "
130 "number of positive frequencies :\n gt.mesh().size() = "
131 << gt.mesh().size() << " gw.mesh().last_index()" << iw_mesh.last_index();
132
133 int n_others = gt.data().shape()[1];
134
135 array<dcomplex, 2> _gout(L,
136 n_others); // FIXME Why do we need this dimension to
137 // be one less than gt.mesh().size() ?
138 array<dcomplex, 2> _gin(L + 1, n_others);
139
140 bool is_fermion = (iw_mesh.statistic() == Fermion);
141 double fact = beta / L;
142 dcomplex iomega = M_PI * 1i / beta;
143
144 double b1, b2, b3;
145 array<dcomplex, 1> m1, a1, a2, a3;
146 auto _ = range::all;
147 auto m2 = mom_23(0, _);
148 auto m3 = mom_23(1, _);
149
150 if (is_fermion) {
151 m1 = -(gt[0] + gt[L]);
152 b1 = 0;
153 b2 = 1;
154 b3 = -1;
155 a1 = m1 - m3;
156 a2 = (m2 + m3) / 2;
157 a3 = (m3 - m2) / 2;
158
159 for (auto t : gt.mesh())
160 _gin(t.index(), _) = fact * exp(iomega * t) * (gt[t] - (oneFermion(a1, b1, t, beta) + oneFermion(a2, b2, t, beta) + oneFermion(a3, b3, t, beta)));
161
162 } else {
163 m1 = -(gt[0] - gt[L]);
164 b1 = -0.5;
165 b2 = -1;
166 b3 = 1;
167 a1 = 4 * (m1 - m3) / 3;
168 a2 = m3 - (m1 + m2) / 2;
169 a3 = m1 / 6 + m2 / 2 + m3 / 3;
170
171 for (auto t : gt.mesh()) _gin(t.index(), _) = fact * (gt[t] - (oneBoson(a1, b1, t, beta) + oneBoson(a2, b2, t, beta) + oneBoson(a3, b3, t, beta)));
172 }
173
174 // int dims[] = {int(L)};
175 //_fourier_base(_gin, _gout, 1, dims, n_others, FFTW_BACKWARD);
176 _fourier_base(_gin, _gout, p);
177
178 auto gw = gf_vec_t<imfreq>{iw_mesh, {int(n_others)}};
179
180 // FIXME Avoid copy, by doing proper in-place operation
181 for (auto w : iw_mesh) gw[w] = _gout((w.index() + L) % L, _) + a1 / (w - b1) + a2 / (w - b2) + a3 / (w - b3);
182
183 return gw;
184 }
185
186 gf_vec_t<imfreq> _fourier_impl(mesh::imfreq const &iw_mesh, gf_vec_cvt<imtime> gt, arrays::array_const_view<dcomplex, 2> mom_23) {
187 auto p = _fourier_plan(iw_mesh, gt);
188 auto gw = _fourier_impl(iw_mesh, gt, p, mom_23);
189 return gw;
190 }
191 // ------------------------ INVERSE TRANSFORM
192 // --------------------------------------------
193
194 fourier_plan _fourier_plan(mesh::imtime const &tau_mesh, gf_vec_cvt<imfreq> gw) {
195
196 TRIQS_ASSERT2(!gw.mesh().positive_only(),
197 "Fourier is only implemented for g(i omega_n) with full mesh "
198 "(positive and negative frequencies)");
199
200 long L = tau_mesh.size() - 1;
201 if (L < 2 * (gw.mesh().last_index() + 1))
202 TRIQS_RUNTIME_ERROR << "Inverse Fourier: The time mesh mush be at least twice as long as "
203 "the freq mesh :\n gt.mesh().size() = "
204 << tau_mesh.size() << " gw.mesh().last_index()" << gw.mesh().last_index();
205
206 int n_others = gw.data().shape()[1];
207
208 array<dcomplex, 2> _gin(L,
209 n_others); // FIXME Why do we need this dimension to
210 // be one less than gt.mesh().size() ?
211 array<dcomplex, 2> _gout(L + 1, n_others);
212
213 int dims[] = {int(L)};
214 auto plan = _fourier_base_plan(_gin, _gout, 1, dims, n_others, FFTW_FORWARD);
215 return plan;
216 }
217
218 gf_vec_t<imtime> _fourier_impl(mesh::imtime const &tau_mesh, gf_vec_cvt<imfreq> gw, fourier_plan &p,
219 arrays::array_const_view<dcomplex, 2> mom_123) {
220
221 TRIQS_ASSERT2(!gw.mesh().positive_only(),
222 "Fourier is only implemented for g(i omega_n) with full mesh "
223 "(positive and negative frequencies)");
224
225 if (mom_123.is_empty()) {
226 auto [tail, error] = fit_tail(gw);
227 TRIQS_ASSERT2((error < 1e-3),
228 "ERROR: High frequency moments have an error "
229 "greater than 1e-3.\n Error = "
230 + std::to_string(error));
231 if (error > 1e-6)
232 std::cerr << "WARNING: High frequency moments have an error greater than "
233 "1e-6.\n Error = "
234 << error;
235 TRIQS_ASSERT2((tail.shape()[0] > 4),
236 "ERROR: Inverse Fourier implementation requires at least a "
237 "proper 3rd high-frequency moment\n");
238 double _abs_tail0 = max_element(abs(tail(0, range::all)));
239 if (_abs_tail0 > 1e-6) std::cerr << "WARNING: High frequency tail is not zero: " << _abs_tail0;
240 /*
241 TRIQS_ASSERT2((_abs_tail0 < 1e-10),
242 "ERROR: Inverse Fourier implementation requires vanishing "
243 "0th moment\n error is :" +
244 std::to_string(_abs_tail0));
245 */
246 return _fourier_impl(tau_mesh, gw, p, tail(range(1, 4), range::all));
247 }
248
249 double beta = tau_mesh.beta();
250 long L = tau_mesh.size() - 1;
251 if (L < 2 * (gw.mesh().last_index() + 1))
252 TRIQS_RUNTIME_ERROR << "Inverse Fourier: The time mesh mush be at least twice as long as "
253 "the freq mesh :\n gt.mesh().size() = "
254 << tau_mesh.size() << " gw.mesh().last_index()" << gw.mesh().last_index();
255
256 int n_others = gw.data().shape()[1];
257
258 array<dcomplex, 2> _gin(L,
259 n_others); // FIXME Why do we need this dimension to
260 // be one less than gt.mesh().size() ?
261 array<dcomplex, 2> _gout(L + 1, n_others);
262
263 bool is_fermion = (gw.mesh().statistic() == Fermion);
264 double fact = 1.0 / beta;
265 dcomplex iomega = M_PI * 1i / beta;
266
267 double b1, b2, b3;
268 array<dcomplex, 1> a1, a2, a3;
269 auto _ = range::all;
270 auto m1 = mom_123(0, _);
271 auto m2 = mom_123(1, _);
272 auto m3 = mom_123(2, _);
273
274 if (is_fermion) {
275 b1 = 0;
276 b2 = 1;
277 b3 = -1;
278 a1 = m1 - m3;
279 a2 = (m2 + m3) / 2;
280 a3 = (m3 - m2) / 2;
281 } else {
282 b1 = -0.5;
283 b2 = -1;
284 b3 = 1;
285 a1 = 4 * (m1 - m3) / 3;
286 a2 = m3 - (m1 + m2) / 2;
287 a3 = m1 / 6 + m2 / 2 + m3 / 3;
288 }
289
290 for (auto w : gw.mesh()) _gin((w.index() + L) % L, _) = fact * (gw[w] - (a1 / (w - b1) + a2 / (w - b2) + a3 / (w - b3)));
291
292 // int dims[] = {int(L)};
293 //_fourier_base(_gin, _gout, 1, dims, n_others, FFTW_FORWARD);
294 _fourier_base(_gin, _gout, p);
295
296 auto gt = gf_vec_t<imtime>{tau_mesh, {int(n_others)}};
297
298 if (is_fermion)
299 for (auto t : tau_mesh)
300 gt[t] = _gout(t.index(), _) * exp(-iomega * t) + oneFermion(a1, b1, t, beta) + oneFermion(a2, b2, t, beta) + oneFermion(a3, b3, t, beta);
301 else
302 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);
303
304 double pm = (is_fermion ? -1 : 1);
305 gt[L] = pm * (gt[0] + m1);
306
307 return gt;
308 }
309
310 gf_vec_t<imtime> _fourier_impl(mesh::imtime const &tau_mesh, gf_vec_cvt<imfreq> gw, arrays::array_const_view<dcomplex, 2> mom_123) {
311 auto p = _fourier_plan(tau_mesh, gw);
312 auto gt = _fourier_impl(tau_mesh, gw, p, mom_123);
313 return gt;
314 }
315
316} // namespace triqs_tprf::fourier