TRIQS/triqs_cthyb 4.0.0
A TRIQS application
Loading...
Searching...
No Matches
nfft_buf.hpp
1/*******************************************************************************
2 *
3 * TRIQS: a Toolbox for Research in Interacting Quantum Systems
4 *
5 * Copyright (C) 2017, N. Wentzell, I. Krivenko
6 *
7 * TRIQS is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later
10 * version.
11 *
12 * TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 * details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * TRIQS. If not, see <http://www.gnu.org/licenses/>.
19 *
20 ******************************************************************************/
21#pragma once
22
23#include <cmath>
24#include <array>
25#include <numeric>
26#include <utility>
27#include <nda/nda.hpp>
28#include <triqs/gfs.hpp>
29#include <triqs/mesh.hpp>
30#include <triqs/utility/tuple_tools.hpp>
31
32#include <nfft3.h>
33
34namespace triqs {
35 namespace experimental {
36
37 using nda::stdutil::sum;
38 using namespace nda;
39 using namespace triqs::gfs;
40 using namespace triqs::mesh;
41
42 using dcomplex = std::complex<double>;
43
44 // NFFT buffer
45 template <int Rank> class nfft_buf_t {
46
47 template <typename = std::make_index_sequence<Rank>> struct imfreq_product;
48 template <std::size_t... Is> struct imfreq_product<std::index_sequence<Is...>> { using type = prod<decltype(Is, imfreq{})...>; };
49
50 public:
51 using freq_mesh_t = typename imfreq_product<>::type;
52
53 nfft_buf_t(freq_mesh_t const &fiw_mesh, array_view<dcomplex, Rank> fiw_arr, int buf_size, bool do_checks = false)
54 : fiw_mesh(fiw_mesh), fiw_arr(fiw_arr), buf_size(buf_size), do_checks(do_checks), plan_ptr(std::make_unique<nfft_plan>()), buf_counter(0) {
55
56 // Initialize NFFT plans
57 all_fermion = true;
58 common_factor = 1;
59 triqs::tuple::for_each_enumerate(fiw_mesh.components(), [this](int r, mesh::imfreq const &m) {
60 if (m.statistic() == Fermion) {
61 index_shifts[r] = 0;
62 common_factor *= (m.size() / 2) % 2 ? -1 : 1;
63 } else {
64 all_fermion = false;
65 index_shifts[r] = 1; // For bosons we discard the most negative frequency
66 common_factor *= ((m.size() - 1) / 2) % 2 ? -1 : 1;
67 if (m.size() < 5) {
68 std::cerr << " ERROR: nfft_buf_t needs more bosonic frequencies.\n";
69 exit(0);
70 }
71 }
72 });
73 std::array<long, Rank> buf_extents = fiw_mesh.size_of_components() + index_shifts;
74
75 if (!all_fermion) nfft_indexmap = idx_map_t(buf_extents);
76
77 // -- Default init
78 //nfft_init(plan_ptr.get(), Rank, buf_extents.ptr(), buf_size);
79
81 auto next_power_of_two = [](unsigned int v) {
82 v--;
83 v |= v >> 1;
84 v |= v >> 2;
85 v |= v >> 4;
86 v |= v >> 8;
87 v |= v >> 16;
88 v++;
89 return v;
90 };
91
92 // Init nfft_plan
93 std::array<long, Rank> extents_fftw;
94 for (int i = 0; i < Rank; i++) extents_fftw[i] = 2 * next_power_of_two(buf_extents[i]);
95
96 unsigned nfft_flags = PRE_PHI_HUT | PRE_PSI | MALLOC_X | MALLOC_F_HAT | MALLOC_F | FFTW_INIT | FFT_OUT_OF_PLACE | NFFT_SORT_NODES;
97 unsigned fftw_flags = FFTW_ESTIMATE | FFTW_DESTROY_INPUT;
98
99 int m = 6; // Truncation order for the window functions
100 std::vector<int> buf_extents_int(buf_extents.begin(), buf_extents.end());
101 std::vector<int> extents_fftw_int(extents_fftw.begin(), extents_fftw.end());
102 nfft_init_guru(plan_ptr.get(), Rank, buf_extents_int.data(), buf_size, extents_fftw_int.data(), m, nfft_flags, fftw_flags);
103 }
104
105 ~nfft_buf_t() {
106 if (buf_counter != 0) std::cerr << " WARNING: Points in NFFT Buffer lost \n";
107 if (plan_ptr) nfft_finalize(plan_ptr.get());
108 }
109
110 // nfft_buffer needs to be uncopyable, because nfft_plan contains raw pointers
111 nfft_buf_t(nfft_buf_t const &) = delete;
112 nfft_buf_t(nfft_buf_t &&) = default;
113 nfft_buf_t &operator=(nfft_buf_t const &) = delete;
114 nfft_buf_t &operator=(nfft_buf_t &&) = default;
115
117 void rebind(array_view<dcomplex, Rank> new_fiw_arr) {
118 flush();
119 assert(get_shape(new_fiw_arr) == get_shape(fiw_arr) || get_shape(fiw_arr) == get_shape(array_view<dcomplex, Rank>{}));
120 fiw_arr.rebind(new_fiw_arr);
121 }
122
123 // Insert tau-vector {\tau_1, \tau_2, ... } \in [0,\beta_1)x[0,\beta_2)x...
124 // and the corresponding f(tau) into the NFFT buffer
125 void push_back(std::array<double, Rank> const &tau_arr, dcomplex ftau) {
126
127 assert(plan_ptr);
128
129 // Write the set of shifted and normalized tau values (i. e. x values) to
130 // the NFFT buffer and sum \tau/\beta for fermions
131 double sum_tau_beta = 0;
132 triqs::tuple::for_each_enumerate(fiw_mesh.components(), [&tau_arr, &sum_tau_beta, this](int r, mesh::imfreq const &m) {
133 double tau = tau_arr[r];
134 double beta = m.beta();
135
136 // Note: Nfft multi-arrays are stored in flattened arrays (c-order)
137 x_arr()[buf_counter * Rank + r] = tau_arr[r] / beta - 0.5; // \in [-0.5, 0.5)
138
139 if (m.statistic() == Fermion) sum_tau_beta += tau / beta;
140 });
141
142 // Write f(x) to nfft_plan-> The prefactor accounts for the Pi/beta offset
143 // in fermionic Matsubaras
144 fx_arr()[buf_counter] = std::exp(1i * M_PI * sum_tau_beta) * ftau;
145
146 ++buf_counter;
147
148 // If buffer is full, perform transform
149 if (is_full()) {
150 do_nfft();
151 buf_counter = 0;
152 }
153 }
154
156 void flush() {
157
158 assert(plan_ptr);
159
160 if (is_empty()) return;
161
162 // Trivial initialization of the remaining points
163 for (int i = buf_counter; i < buf_size; ++i) {
164 fx_arr()[i] = 0.0;
165 for (int r = 0; r < Rank; ++r) x_arr()[i * Rank + r] = -0.5 + double(i) / buf_size;
166 }
167 do_nfft();
168 buf_counter = 0;
169 }
170
171 // Function to check whether buffer is empty
172 bool is_empty() const { return buf_counter == 0; }
173
174 // Function to check whether buffer is filled
175 bool is_full() const { return buf_counter >= buf_size; }
176
177 private:
178 // Imaginary frequency (multi-)mesh
179 freq_mesh_t fiw_mesh;
180
181 // TRIQS array to contain the final NFFT output in matsubara frequencies
182 array_view<dcomplex, Rank> fiw_arr;
183
184 // Are all mesh components fermionic?
185 bool all_fermion;
186
187 // Index map for plan_ptr->f_hat
188 using idx_map_t = nda::idx_map<Rank, 0, C_stride_order<Rank>, layout_prop_e::none>;
189 idx_map_t nfft_indexmap;
190
191 // Bosonic components of fiw_mesh indices must be shifted by 1,
192 // since we want to ignore the most negative frequencies in plan_ptr->f_hat
193 std::array<long, Rank> index_shifts;
194
195 // Common prefactor for the transformation result
196 int common_factor;
197
198 // Number of tau points for the nfft
199 int buf_size;
200
201 // Switch for testing in nfft
202 bool do_checks;
203
204 // Pointer to NFFT plan
205 std::unique_ptr<nfft_plan> plan_ptr;
206
207 // Counter for elements currently in the buffer
208 int buf_counter;
209
210 // Get pointer to array containing x values for the NFFT transform
211 double *x_arr() { return plan_ptr->x; }
212
213 // Get pointer to array containing f(x) values for the NFFT transform
214 dcomplex *fx_arr() { return reinterpret_cast<dcomplex *>(plan_ptr->f); }
215
216 // Get pointer to array containing the NFFT output h(k)
217 const dcomplex *fk_arr() const { return reinterpret_cast<dcomplex *>(plan_ptr->f_hat); }
218
219 // Perform NFFT transform and accumulate inside fiw_arr
220 void do_nfft() {
221
222 // nfft_adjoint() uses a window function (Kaiser-Bessel by default)
223 // that cannot be constructed for plan_ptr->N[i] < plan_ptr->m.
224 // In the small N[i] case one has to call nfft_adjoint_direct() instead,
225 // which is also faster for the smaller N[i].
226 //
227 // C.f. https://github.com/NFFT/nfft/issues/34
228 auto N_min = *std::min_element(plan_ptr->N, plan_ptr->N + plan_ptr->d);
229 if (N_min <= plan_ptr->m) {
230
231 // Execute transform
232 nfft_adjoint_direct(plan_ptr.get());
233
234 } else {
235
236// NFFT Library precomputation and checks
237#ifdef NFFT_OLD_API
238 if (plan_ptr->nfft_flags & PRE_ONE_PSI) nfft_precompute_one_psi(plan_ptr.get());
239#else
240 if (plan_ptr->flags & PRE_ONE_PSI) nfft_precompute_one_psi(plan_ptr.get());
241#endif
242 if (do_checks) { // Check validity of NFFT parameters
243 const char *error_str = nfft_check(plan_ptr.get());
244 if (error_str != 0) TRIQS_RUNTIME_ERROR << "Error in NFFT module: " << error_str << "\n";
245 }
246
247 // Execute transform
248 nfft_adjoint(plan_ptr.get());
249 }
250
251 // Accumulate results in fiw_arr. Care to normalize results afterwards
252 if (all_fermion) {
253 int count = 0;
254 for (auto it = fiw_arr.begin(); it != fiw_arr.end(); ++it, ++count) {
255 int factor = (sum(it.indices()) % 2 ? -1 : 1);
256 *it += fk_arr()[count] * factor * common_factor;
257 }
258 } else {
259 for (auto it = fiw_arr.begin(); it != fiw_arr.end(); ++it) {
260 int count = std::apply(nfft_indexmap, it.indices() + index_shifts);
261 int factor = (sum(it.indices()) % 2 ? -1 : 1);
262 *it += fk_arr()[count] * factor * common_factor;
263 }
264 }
265 }
266 };
267 }
268}