OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
GSLFFT.h
Go to the documentation of this file.
1//
2// FFT implementation to replace GSL FFT
3//
4// Copyright (c) 2023, Paul Scherrer Institute, Villigen PSI, Switzerland
5// All rights reserved
6//
7// This file is part of OPAL.
8//
9// OPAL is free software: you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// You should have received a copy of the GNU General License
15// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
16//
17
18#ifndef OPAL_GSL_FFT_HH
19#define OPAL_GSL_FFT_HH
20
21#include <algorithm>
22#include <cmath>
23#include <complex>
24#include <vector>
25
28// Helper functions for FFT
29namespace {
30 // Cooley-Tukey FFT
31 void fft(std::vector<std::complex<double>>& x) {
32 size_t n = x.size();
33 if (n <= 1) return;
34
35 // Divide
36 std::vector<std::complex<double>> even(n / 2);
37 std::vector<std::complex<double>> odd(n / 2);
38 for (size_t i = 0; i < n / 2; ++i) {
39 even[i] = x[2 * i];
40 odd[i] = x[2 * i + 1];
41 }
42
43 // Conquer
44 fft(even);
45 fft(odd);
46
47 // Combine
48 for (size_t i = 0; i < n / 2; ++i) {
49 std::complex<double> t = std::polar(1.0, -2.0 * M_PI * i / n) * odd[i];
50 x[i] = even[i] + t;
51 x[i + n / 2] = even[i] - t;
52 }
53 }
54
55 // Inverse FFT
56 void ifft(std::vector<std::complex<double>>& x) {
57 size_t n = x.size();
58 if (n <= 1) return;
59
60 // Conjugate input
61 for (auto& val : x) {
62 val = std::conj(val);
63 }
64
65 // Forward FFT
66 fft(x);
67
68 // Conjugate and scale
69 for (auto& val : x) {
70 val = std::conj(val) / static_cast<double>(n);
71 }
72 }
73} // namespace
74
77namespace FFT {
78
85 inline void fft_real_transform(double* data, size_t stride, size_t n) {
86 if (n == 0) return;
87
88 // Convert real data to complex
89 std::vector<std::complex<double>> complex_data(n);
90 for (size_t i = 0; i < n; ++i) {
91 complex_data[i] = std::complex<double>(data[i * stride], 0.0);
92 }
93
94 // Perform FFT
95 fft(complex_data);
96
97 // Convert back to real (packed format like GSL)
98 // GSL packs the result: [r0, r1, i1, r2, i2, ..., r(n/2), i(n/2)]
99 data[0] = complex_data[0].real();
100 if (n > 1) {
101 size_t half = n / 2;
102 for (size_t i = 1; i < half; ++i) {
103 data[2 * i - 1] = complex_data[i].real();
104 data[2 * i] = complex_data[i].imag();
105 }
106 if (n % 2 == 0) {
107 data[n - 1] = complex_data[half].real();
108 }
109 }
110 }
111} // namespace FFT
112
116 size_t n;
117};
118
122 size_t n;
123 std::vector<double> work;
124};
125
131 w->n = n;
132 return w;
133}
134
140 w->n = n;
141 w->work.resize(n);
142 return w;
143}
144
152 double* data, size_t stride, size_t n, gsl_fft_real_wavetable* /* wavetable */,
153 gsl_fft_real_workspace* /* workspace */) {
154 FFT::fft_real_transform(data, stride, n);
155}
156
160
164
170
174 size_t n;
175 std::vector<double> work;
176};
177
186
192 w->n = n;
193 w->work.resize(n);
194 return w;
195}
196
204 double* data, size_t stride, size_t n, gsl_fft_halfcomplex_wavetable* /* wavetable */,
205 gsl_fft_halfcomplex_workspace* /* workspace */) {
206 // Inverse FFT for halfcomplex data (packed format)
207 // Convert halfcomplex to complex, perform inverse FFT, convert back
208 std::vector<std::complex<double>> complex_data(n);
209 complex_data[0] = std::complex<double>(data[0], 0.0);
210
211 size_t half = n / 2;
212 for (size_t i = 1; i < half; ++i) {
213 complex_data[i] = std::complex<double>(data[2 * i - 1], data[2 * i]);
214 complex_data[n - i] = std::conj(complex_data[i]);
215 }
216 if (n % 2 == 0) {
217 complex_data[half] = std::complex<double>(data[n - 1], 0.0);
218 }
219
220 // Inverse FFT (GSL convention: scale by n)
221 // Our ifft scales by 1/n, so we multiply by n to get n scaling
222 ifft(complex_data);
223 double scale = static_cast<double>(n);
224
225 // Convert back to real
226 for (size_t i = 0; i < n; ++i) {
227 data[i * stride] = complex_data[i].real() * scale;
228 }
229}
230
234
238
246 double* data, size_t stride, size_t n, gsl_fft_halfcomplex_wavetable* wavetable,
248 gsl_fft_halfcomplex_transform(data, stride, n, wavetable, workspace);
249}
250
266
270 size_t n;
271};
272
276 size_t n;
277 std::vector<std::complex<double>> work;
278};
279
288
294 w->n = n;
295 w->work.resize(n);
296 return w;
297}
298
307 double* data, size_t stride, size_t n, gsl_fft_complex_wavetable* /* wavetable */,
308 gsl_fft_complex_workspace* /* workspace */) {
309 std::vector<std::complex<double>> complex_data(n);
310 for (size_t i = 0; i < n; ++i) {
311 complex_data[i] = std::complex<double>(data[2 * i * stride], data[(2 * i + 1) * stride]);
312 }
313 fft(complex_data);
314 for (size_t i = 0; i < n; ++i) {
315 data[2 * i * stride] = complex_data[i].real();
316 data[(2 * i + 1) * stride] = complex_data[i].imag();
317 }
318}
319
328 double* data, size_t stride, size_t n, gsl_fft_complex_wavetable* /* wavetable */,
329 gsl_fft_complex_workspace* /* workspace */) {
330 std::vector<std::complex<double>> complex_data(n);
331 for (size_t i = 0; i < n; ++i) {
332 complex_data[i] = std::complex<double>(data[2 * i * stride], data[(2 * i + 1) * stride]);
333 }
334 // GSL convention: inverse FFT scales by n (not 1/n like standard FFT)
335 // Our ifft scales by 1/n, so we multiply by n to get n scaling
336 ifft(complex_data);
337 double scale = static_cast<double>(n);
338 for (size_t i = 0; i < n; ++i) {
339 data[2 * i * stride] = complex_data[i].real() * scale;
340 data[(2 * i + 1) * stride] = complex_data[i].imag() * scale;
341 }
342}
343
347
351
363
375
376#endif // OPAL_GSL_FFT_HH
std::vector< double > work
Definition GSLFFT.h:175
std::vector< std::complex< double > > work
Definition GSLFFT.h:277
void gsl_fft_complex_wavetable_free(gsl_fft_complex_wavetable *w)
Free a complex FFT wavetable.
Definition GSLFFT.h:346
gsl_fft_halfcomplex_wavetable * gsl_fft_halfcomplex_wavetable_alloc(size_t n)
Allocate a halfcomplex FFT wavetable of size .
Definition GSLFFT.h:181
gsl_fft_complex_workspace * gsl_fft_complex_workspace_alloc(size_t n)
Allocate a complex FFT workspace of size .
Definition GSLFFT.h:292
void gsl_fft_halfcomplex_transform(double *data, size_t stride, size_t n, gsl_fft_halfcomplex_wavetable *, gsl_fft_halfcomplex_workspace *)
Inverse transform from halfcomplex packed data.
Definition GSLFFT.h:203
void gsl_fft_real_wavetable_free(gsl_fft_real_wavetable *w)
Free a real FFT wavetable.
Definition GSLFFT.h:159
std::vector< double > work
Definition GSLFFT.h:123
gsl_fft_real_workspace * gsl_fft_real_workspace_alloc(size_t n)
Allocate a real FFT workspace of size .
Definition GSLFFT.h:138
gsl_fft_halfcomplex_workspace * gsl_fft_halfcomplex_workspace_alloc(size_t n)
Allocate a halfcomplex FFT workspace of size .
Definition GSLFFT.h:190
void gsl_fft_halfcomplex_inverse(double *data, size_t stride, size_t n, gsl_fft_halfcomplex_wavetable *wavetable, gsl_fft_halfcomplex_workspace *workspace)
Alias for halfcomplex inverse transform.
Definition GSLFFT.h:245
void gsl_fft_complex_inverse(double *data, size_t stride, size_t n, gsl_fft_complex_wavetable *, gsl_fft_complex_workspace *)
Inverse complex FFT (GSL scaling: multiplies by ).
Definition GSLFFT.h:327
void gsl_fft_complex_forward(double *data, size_t stride, size_t n, gsl_fft_complex_wavetable *, gsl_fft_complex_workspace *)
Forward complex FFT.
Definition GSLFFT.h:306
void gsl_fft_complex_workspace_free(gsl_fft_complex_workspace *w)
Free a complex FFT workspace.
Definition GSLFFT.h:350
void gsl_fft_real_workspace_free(gsl_fft_real_workspace *w)
Free a real FFT workspace.
Definition GSLFFT.h:163
void gsl_fft_complex_radix2_inverse(double *data, size_t stride, size_t n)
Radix-2 inverse complex FFT (GSL scaling, allocates temporaries).
Definition GSLFFT.h:368
void gsl_fft_halfcomplex_workspace_free(gsl_fft_halfcomplex_workspace *w)
Free a halfcomplex FFT workspace.
Definition GSLFFT.h:237
gsl_fft_complex_wavetable * gsl_fft_complex_wavetable_alloc(size_t n)
Allocate a complex FFT wavetable of size .
Definition GSLFFT.h:283
void gsl_fft_real_transform(double *data, size_t stride, size_t n, gsl_fft_real_wavetable *, gsl_fft_real_workspace *)
Forward real FFT with GSL-compatible packed output.
Definition GSLFFT.h:151
gsl_fft_real_wavetable * gsl_fft_real_wavetable_alloc(size_t n)
Allocate a real FFT wavetable of size .
Definition GSLFFT.h:129
void gsl_fft_halfcomplex_wavetable_free(gsl_fft_halfcomplex_wavetable *w)
Free a halfcomplex FFT wavetable.
Definition GSLFFT.h:233
void gsl_fft_complex_radix2_forward(double *data, size_t stride, size_t n)
Radix-2 forward complex FFT (allocates temporary wavetable/workspace).
Definition GSLFFT.h:356
Complex FFT types.
Definition GSLFFT.h:269
Workspace for complex FFT routines.
Definition GSLFFT.h:275
Halfcomplex FFT types for inverse transforms.
Definition GSLFFT.h:167
Workspace for halfcomplex FFT routines.
Definition GSLFFT.h:173
GSL-compatible interface for FFT routines.
Definition GSLFFT.h:115
Workspace for real FFT routines.
Definition GSLFFT.h:121
Simple FFT implementation using Cooley-Tukey algorithm.
Definition GSLFFT.h:77
void fft_real_transform(double *data, size_t stride, size_t n)
Forward FFT for real data (packed output).
Definition GSLFFT.h:85