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)));
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)));
57 using matrix_t = nda::matrix<dcomplex>;
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];
65 d_vec_right(m - 1, _) = (gt[n_tau - 1] - gt[n_tau - 1 - m]) / gt.mesh()[m];
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}};
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;
105 tail(1, _) = -(gt[0] -
sign * gt[n_tau - 1]);
106 tail(2, _) = g_vec_left(0, _) -
sign * g_vec_right(0, _);
107 tail(3, _) = -(g_vec_left(1, _) +
sign * g_vec_right(1, _)) * 2 / gt.mesh().delta();
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) {
115 nda::array<dcomplex, 2> tail;
117 if (known_moments.is_empty()) {
119 auto dat = gt.data();
120 long n_tau = gt.mesh().size();
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)))
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";
130 return _fourier_impl(iw_mesh, gt,
fit_tail(gt));
133 double _abs_tail0 = max_element(abs(known_moments(0, range::all)));
135 "ERROR: Direct Fourier implementation requires vanishing 0th moment\n error is :" +
std::to_string(_abs_tail0));
137 long n_known_moments = std::min<long>(known_moments.shape()[0], 4);
139 tail(range(n_known_moments), range::all) = known_moments(range(n_known_moments), range::all);
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();
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";
152 long n_others = second_dim(gt.data());
154 array<dcomplex, 2> _gout(L, n_others);
155 array<dcomplex, 2> _gin(L + 1, n_others);
157 bool is_fermion = (iw_mesh.statistic() == Fermion);
158 double fact = beta /
static_cast<double>(L);
159 dcomplex iomega = M_PI * 1i / beta;
161 double b1 = 0, b2 = 0, b3 = 0;
162 array<dcomplex, 1> a1, a2, a3;
164 auto m1 = tail(1, _);
165 auto m2 = tail(2, _);
166 auto m3 = tail(3, _);
176 for (
auto t : gt.mesh())
178 fact * exp(iomega * t) * (gt[t] - (oneFermion(a1, b1, t, beta) + oneFermion(a2, b2, t, beta) + oneFermion(a3, b3, t, beta)));
184 a1 = 4 * (m1 - m3) / 3;
185 a2 = m3 - (m1 + m2) / 2;
186 a3 = m1 / 6 + m2 / 2 + m3 / 3;
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)));
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);
195 auto gw = gf_vec_t<mesh::imfreq>{iw_mesh, {n_others}};
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);
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) {
209 TRIQS_ASSERT2(!gw.mesh().positive_only(),
"Fourier is only implemented for g(i omega_n) with full mesh (positive and negative frequencies)");
211 nda::array_const_view<dcomplex, 2> tail;
214 if (known_moments.is_empty())
return _fourier_impl(tau_mesh, gw,
make_zero_tail(gw, 1));
216 double _abs_tail0 = max_element(abs(known_moments(0, range::all)));
218 "ERROR: Inverse Fourier implementation requires vanishing 0th moment\n error is :" +
std::to_string(_abs_tail0) +
"\n");
220 if (known_moments.shape()[0] < 4) {
221 auto [t, err] =
fit_tail(gw, known_moments);
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");
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);
231 tail.rebind(known_moments);
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";
239 long n_others = second_dim(gw.data());
241 array<dcomplex, 2> _gin(L, n_others);
242 array<dcomplex, 2> _gout(L + 1, n_others);
244 bool is_fermion = (gw.mesh().statistic() == Fermion);
245 double fact = 1.0 / beta;
246 dcomplex iomega = M_PI * 1i / beta;
248 double b1 = 0, b2 = 0, b3 = 0;
249 array<dcomplex, 1> a1, a2, a3;
251 auto m1 = tail(1, _);
252 auto m2 = tail(2, _);
253 auto m3 = tail(3, _);
266 a1 = 4 * (m1 - m3) / 3;
267 a2 = m3 - (m1 + m2) / 2;
268 a3 = m1 / 6 + m2 / 2 + m3 / 3;
271 for (
auto iw : gw.mesh()) _gin((iw.n + L) % L, _) = fact * (gw[iw] - (a1 / (iw - b1) + a2 / (iw - b2) + a3 / (iw - b3)));
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);
276 auto gt = gf_vec_t<mesh::imtime>{tau_mesh, {n_others}};
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);
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);
284 double pm = (is_fermion ? -1 : 1);
285 gt[L] = pm * (gt[0] + m1);
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.
#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.