TRIQS/triqs_ctint unstable
A TRIQS application
Loading...
Searching...
No Matches
nfft_buf.hpp
1// Copyright (c) 2017--present, The Simons Foundation
2// This file is part of TRIQS/ctint and is licensed under the terms of GPLv3 or later.
3// SPDX-License-Identifier: GPL-3.0-or-later
4// See LICENSE in the root of this distribution for details.
5
6#pragma once
7#include <nda/nda.hpp>
8#include <array>
9#include <cmath>
10#include <memory>
11#include <type_traits>
12#include <utility>
13#include <variant>
14#include <vector>
15#include <triqs/mesh/matsubara_freq.hpp>
16#include <triqs/utility/exceptions.hpp> // TRIQS_ASSERT
17
18namespace triqs::utility {
19
20 namespace detail {
21 // Opaque handle for the FINUFFT plan defined in nfft_buf.cpp to avoid dependency on FINUFFT headers
22 struct nfft_plan;
23 struct nfft_plan_deleter {
24 void operator()(nfft_plan *) const;
25 };
26
27 // Helper to convert vector<T> to vector<array<T, 1>> for the Rank=1 convenience constructor
28 inline std::vector<std::array<mesh::matsubara_freq, 1>> to_array_vector(std::vector<mesh::matsubara_freq> const &v) {
29 std::vector<std::array<mesh::matsubara_freq, 1>> result;
30 result.reserve(v.size());
31 for (auto const &mf : v) result.push_back({mf});
32 return result;
33 }
34 } // namespace detail
35
36 enum class nfft_type_t { type1, type3, direct };
37
38 template <int Rank> struct nfft_buf_t {
39
40 static_assert(Rank >= 1 and Rank <= 3, "nfft_buf_t only supports Rank 1, 2, and 3");
41
43 nfft_buf_t() = default;
44
58 nfft_buf_t(nda::array_view<dcomplex, Rank> fiw_arr_, int buf_size_, double beta_, double tol_ = 1e-15);
59
78 nfft_buf_t(nda::array_view<dcomplex, 1> fiw_vec_, std::vector<std::array<mesh::matsubara_freq, Rank>> target_mf_, int buf_size_, nfft_type_t type,
79 double tol_ = 1e-13);
80
87 nfft_buf_t(nda::array_view<dcomplex, 1> fiw_vec_, std::vector<mesh::matsubara_freq> const &target_mf_, int buf_size_, nfft_type_t type,
88 double tol_ = 1e-13)
89 requires(Rank == 1)
90 : nfft_buf_t(std::move(fiw_vec_), detail::to_array_vector(target_mf_), buf_size_, type, tol_) {}
91
92 ~nfft_buf_t();
93
94 // nfft_buffer is move-only (unique_ptr member)
95 nfft_buf_t(nfft_buf_t const &) = delete;
96 nfft_buf_t &operator=(nfft_buf_t const &) = delete;
97 nfft_buf_t(nfft_buf_t &&) = default;
98 nfft_buf_t &operator=(nfft_buf_t &&rhs) noexcept;
99
101 void rebind(nda::array_view<dcomplex, Rank> new_fiw_arr) {
102 flush();
103 TRIQS_ASSERT((new_fiw_arr.shape() == fiw_arr.shape() or fiw_arr.empty())
104 and " Nfft Buffer: Rebind to array of different shape not allowed ");
105 fiw_arr.rebind(new_fiw_arr);
106 }
107
109 void push_back(std::array<double, Rank> const &tau_arr, dcomplex ftau) {
110
111 // Check if buffer has been properly initialized
112 if (x_arr.empty()) NDA_RUNTIME_ERROR << " Using a default-constructed NFFT Buffer is not allowed\n";
113
114 if (nfft_type == nfft_type_t::type1) {
115 // Type 1: normalize tau to [-PI, PI) and apply phase correction
116 double tau_sum = 0.0;
117 for (int r = 0; r < Rank; ++r) {
118 x_arr(r, buf_counter) = 2 * M_PI * (tau_arr[r] / beta - 0.5); // \in [-PI, PI)
119 tau_sum += tau_arr[r];
120 }
121 fx_arr[buf_counter] = std::exp(dcomplex(0, M_PI * tau_sum / beta)) * ftau;
122 } else {
123 // Type 3 / direct: raw tau values, no normalization or phase correction needed
124 for (int r = 0; r < Rank; ++r) x_arr(r, buf_counter) = tau_arr[r];
125 fx_arr[buf_counter] = ftau;
126 }
127
128 ++buf_counter;
129
130 // If buffer is full, perform transform
131 if (is_full()) {
132 do_nfft();
133 buf_counter = 0;
134 }
135 }
136
138 void flush() {
139
140 // Check if buffer has been properly initialized
141 if (x_arr.empty()) NDA_RUNTIME_ERROR << " Using a default-constructed NFFT Buffer is not allowed\n";
142
143 // Don't do anything if buffer is empty
144 if (is_empty()) return;
145
146 // Execute the transform
147 do_nfft();
148 buf_counter = 0;
149 }
150
151 private:
152 // Transform type
153 nfft_type_t nfft_type = nfft_type_t::type1;
154
155 // Type 1: output array in matsubara frequencies (uniform grid)
156 nda::array_view<dcomplex, Rank> fiw_arr;
157
158 // 1D output vector at target frequencies (type 3 and direct)
159 nda::array_view<dcomplex, 1> fiw_vec;
160
161 // Dimensions of the output array (type 1 only)
162 std::array<int64_t, Rank> niws{};
163
164 // Finufft plan (RAII-managed)
165 std::unique_ptr<detail::nfft_plan, detail::nfft_plan_deleter> plan;
166
167 // Number of tau points for the nfft
168 int buf_size = 0;
169
170 // Inverse temperature (type 1 and direct)
171 double beta = 0;
172
173 // Counter for elements currently in the buffer
174 int buf_counter = 0;
175
176 // Common factor in container assignment (type 1 only)
177 int common_factor = 1;
178
179 // Number of target frequencies (type 3 and direct)
180 int64_t n_targets = 0;
181
182 // Array containing x values for the NFFT transform
183 nda::array<double, 2> x_arr;
184
185 // Array containing f(x) values for the NFFT transform
186 nda::vector<dcomplex> fx_arr;
187
188 // Array containing the NFFT output h(k) (type 1)
189 nda::array<dcomplex, Rank> fk_arr;
190
191 // Target frequencies, shape (Rank, n_targets) (type 3 only)
192 nda::array<double, 2> s_arr;
193
194 // Type 3 NFFT output buffer
195 nda::vector<dcomplex> fk_vec;
196
197 // Integer Matsubara indices per target, shape (Rank, n_targets) (direct only)
198 nda::array<long, 2> target_n;
199
200 // Binary exponentiation approach: store z^(2^k) for k=0,1,2,...
201 int num_power2_levels = 0;
202
203 // Preallocated power-of-2 table for Rank==1 bitwise direct kernel.
204 // Shape: (num_power2_levels, buf_size).
205 // pow2_tbl(k, j) = z^(2^k) for buffer element j.
206 std::conditional_t<Rank == 1, nda::array<dcomplex, 2>, std::monostate> pow2_tbl;
207
208 // Per-target list of power-of-two exponents for Rank==1 bitwise kernel.
209 // target_pow2_bits[d] contains k such that |2*n_d+1| has bit k set.
210 std::conditional_t<Rank == 1, std::vector<std::vector<int>>, std::monostate> target_pow2_bits;
211
212 // Rank>1 prime-sum direct kernel data
213 std::vector<int> primes;
214 std::array<nda::array<dcomplex, 2>, Rank> prime_pow_tbl;
215 std::array<std::vector<std::vector<int>>, Rank> target_prime_sums;
216
217 // Tolerance for the transformation
218 double tol = 1e-15;
219
220 // Function to check whether buffer is filled
221 bool is_full() const { return buf_counter >= buf_size; }
222
223 // Function to check whether buffer is empty
224 bool is_empty() const { return buf_counter == 0; }
225
226 // Perform NFFT transform and accumulate
227 void do_nfft();
228
229 // Set source points and optional target points on the FINUFFT plan, dispatching by Rank.
230 // FINUFFT expects coordinates in reverse rank order (x=last dim, y=second-to-last, z=first).
231 // For type 1, pass nullptr for tgt. For type 3, pass target coordinate array.
232 void set_pts(nda::array<double, 2> *tgt = nullptr);
233
234 // Type 1: non-uniform tau -> uniform grid
235 void do_nfft_type1();
236
237 // Type 3: non-uniform tau -> non-uniform target frequencies
238 void do_nfft_type3();
239
240 // Rank-1 direct NUDFT via bitwise power-of-two decomposition.
241 // Constrained rather than asserted: the explicit instantiations in nfft_buf.cpp
242 // instantiate every unconstrained member, and this body is valid for Rank 1 only.
243 void do_direct_bitwise()
244 requires(Rank == 1);
245
246 // Rank>1 direct NUDFT via prime-sum decomposition
247 void do_direct_prime();
248
249 // Direct DFT dispatcher: bitwise decomposition for Rank 1, prime-sum for Rank>1
250 void do_direct();
251 };
252
253} // namespace triqs::utility