CosmoGRaPH v0.0
Fourier.h
1 #ifndef COSMO_UTILS_FOURIER_H
2 #define COSMO_UTILS_FOURIER_H
3 
4 // FFT indexing
5 #define FFT_INDEX(i,j,k) ((NZ/2+1)*NY*((i+NX)%NX) + (NZ/2+1)*((j+NY)%NY) + ((k+NZ)%NZ))
6 // FFT indexing without periodicity
7 #define FFT_NP_INDEX(i,j,k) ((NZ/2+1)*NY*(i) + (NZ/2+1)*(j) + (k))
8 
9 #include <fftw3.h>
10 #include <zlib.h>
11 #include <math.h>
12 #include <string>
13 #include <iostream>
14 #include "../cosmo_macros.h"
15 #include "../cosmo_types.h"
16 #include "../cosmo_globals.h"
17 
18 namespace cosmo
19 {
20 
21 class Fourier
22 {
23 public:
24 #if USE_LONG_DOUBLES
25  typedef long double fft_rt;
26  typedef fftwl_complex fft_ct;
27 #else
28  typedef double fft_rt;
29  typedef fftw_complex fft_ct;
30 #endif
31 
32  // FFT field
33  fft_ct *f_field;
34  fft_rt *double_field;
35 
36  // plans for taking FFTs
37 #if USE_LONG_DOUBLES
38  fftwl_plan p_c2r;
39  fftwl_plan p_r2c;
40 #else
41  fftw_plan p_c2r;
42  fftw_plan p_r2c;
43 #endif
44 
45  Fourier();
46  ~Fourier();
47 
48  template<typename IT>
49  void Initialize(IT nx, IT ny, IT nz);
50 
51  template<typename IT>
52  void Initialize_1D(IT n);
53 
54 #if USE_LONG_DOUBLES
55  void execute_f_r2c() { fftwl_execute(p_r2c); }
56  void execute_f_c2r() { fftwl_execute(p_c2r); }
57 #else
58  void execute_f_r2c() { fftw_execute(p_r2c); }
59  void execute_f_c2r() { fftw_execute(p_c2r); }
60 #endif
61 
62  template<typename RT, typename IOT>
63  void powerDump(RT *in, IOT *iodata);
64 
65  template<typename IT, typename RT>
66  void inverseLaplacian(RT *field);
67 };
68 
69 
78 template<typename IT>
79 void Fourier::Initialize(IT nx, IT ny, IT nz)
80 {
81  // create plans
82 #if USE_LONG_DOUBLES
83  //fftw_malloc
84  f_field = (fft_ct *) fftwl_malloc(nx*ny*(nz/2+1)
85  *((long long) sizeof(fft_ct)));
86  double_field = new fft_rt[nx*ny*nz];
87 
88  p_r2c = fftwl_plan_dft_r2c_3d(nx, ny, nz,
89  double_field, f_field,
90  FFTW_MEASURE);
91  p_c2r = fftwl_plan_dft_c2r_3d(nx, ny, nz,
92  f_field, double_field,
93  FFTW_MEASURE);
94 #else
95  //fftw_malloc
96  f_field = (fft_ct *) fftw_malloc(nx*ny*(nz/2+1)
97  *((long long) sizeof(fft_ct)));
98  double_field = new fft_rt[nx*ny*nz];
99 
100  p_r2c = fftw_plan_dft_r2c_3d(nx, ny, nz,
101  double_field, f_field,
102  FFTW_MEASURE);
103  p_c2r = fftw_plan_dft_c2r_3d(nx, ny, nz,
104  f_field, double_field,
105  FFTW_MEASURE);
106 #endif
107 }
108 
109 template<typename IT>
110 void Fourier::Initialize_1D(IT n)
111 {
112  // create plans
113 #if USE_LONG_DOUBLES
114  //fftw_malloc
115  f_field = (fft_ct *) fftwl_malloc((n/2 +1)*((long long) sizeof(fft_ct)));
116  double_field = new fft_rt[n];
117 
118  p_r2c = fftwl_plan_dft_r2c_1d(n,
119  double_field, f_field,FFTW_ESTIMATE
120  );
121  p_c2r = fftwl_plan_dft_c2r_1d(n,
122  f_field, double_field,FFTW_ESTIMATE
123  );
124 #else
125  //fftw_malloc
126  f_field = (fft_ct *) fftw_malloc((n/2 +1)*((long long) sizeof(fft_ct)));
127  double_field = new fft_rt[n];
128 
129  p_r2c = fftw_plan_dft_r2c_1d(n,
130  double_field, f_field,FFTW_ESTIMATE
131  );
132  p_c2r = fftw_plan_dft_c2r_1d(n,
133  f_field, double_field,FFTW_ESTIMATE
134  );
135 #endif
136 }
137 
138 
146 template<typename RT, typename IOT>
147 void Fourier::powerDump(RT *in, IOT *iodata)
148 {
149  // Transform input array
150  for(long int i=0; i<POINTS; ++i)
151  double_field[i] = (fft_rt) in[i];
152 
153 #if USE_LONG_DOUBLES
154  fftwl_execute_dft_r2c(p_r2c, double_field, f_field);
155 #else
156  fftw_execute_dft_r2c(p_r2c, double_field, f_field);
157 #endif
158 
159  // average power over angles
160  const int numbins = (int) (sqrt(NX*NX + NY*NY + NZ*NZ)/2.0) + 1; // Actual number of bins
161  RT * array_out = new RT[numbins];
162  int * numpoints = new int[numbins]; // Number of points in each momentum bin
163  RT * p = new RT[numbins];
164  RT * f2 = new RT[numbins]; // Values for each bin: Momentum, |F-k|^2, n_k
165 
166  fft_rt pmagnitude; // Total momentum (p) in units of lattice spacing, pmagnitude = Sqrt(px^2+py^2+pz^2).
167  // This also gives the bin index since bin spacing is set to equal lattice spacing.
168  fft_rt fp2;
169  int i, j, k, px, py, pz; // px, py, and pz are components of momentum in units of grid spacing
170 
171  // Initial magnitude of momentum in each bin
172  for(i=0; i<numbins; i++) {
173  f2[i] = 0.0;
174  numpoints[i] = 0;
175  }
176 
177  // Perform average over all angles here (~integral d\Omega).
178  for(i=0; i<NX; i++)
179  {
180  px = (i<=NX/2 ? i : i-NX);
181  for(j=0; j<NY; j++)
182  {
183  py = (j<=NY/2 ? j : j-NY);
184  for(k=1; k<NZ/2; k++)
185  {
186  pz = k;
187  pmagnitude = sqrt((RT) (pw2(px) + pw2(py) + pw2(pz)));
188  fp2 = pw2(C_RE((f_field)[FFT_NP_INDEX(i,j,k)])) + pw2(C_IM((f_field)[FFT_NP_INDEX(i,j,k)]));
189  numpoints[(int)pmagnitude] += 2;
190  f2[(int)pmagnitude] += 2.*fp2;
191  }
192 
193  pz = 0;
194  k = 0;
195  pmagnitude = sqrt((RT) (pw2(px) + pw2(py) + pw2( pz)));
196  fp2 = pw2(C_RE((f_field)[FFT_NP_INDEX(i,j,k)])) + pw2(C_IM((f_field)[FFT_NP_INDEX(i,j,k)]));
197  numpoints[(int)pmagnitude] += 1;
198  f2[(int)pmagnitude] += fp2;
199 
200  pz = NZ/2;
201  k = NZ/2;
202  pmagnitude = sqrt((RT) (pw2(px) + pw2(py) + pw2(pz)));
203  fp2 = pw2(C_RE((f_field)[FFT_NP_INDEX(i,j,k)])) + pw2(C_IM((f_field)[FFT_NP_INDEX(i,j,k)]));
204  numpoints[(int)pmagnitude] += 1;
205  f2[(int)pmagnitude] += fp2;
206  }
207  }
208 
209  for(i=0; i<numbins; i++)
210  {
211  // Converts sums to averages. (numpoints[i] should always be greater than zero.)
212  if(numpoints[i] > 0)
213  {
214  array_out[i] = f2[i]/((fft_rt) numpoints[i]);
215  }
216  else
217  {
218  array_out[i] = 0.;
219  }
220  }
221 
222  // write data
223  std::string filename = iodata->dir() + "spec.dat.gz";
224  char data[20];
225 
226  gzFile datafile = gzopen(filename.c_str(), "ab");
227  if(datafile == Z_NULL) {
228  printf("Error opening file: %s\n", filename.c_str());
229  return;
230  }
231 
232  for(i=0; i<numbins; i++)
233  {
234  // field values
235  sprintf(data, "%g\t", (fft_rt) array_out[i]);
236  gzwrite(datafile, data, std::char_traits<char>::length(data));
237  }
238  gzwrite(datafile, "\n", std::char_traits<char>::length("\n"));
239 
240  gzclose(datafile);
241  delete[] array_out;
242  delete[] numpoints;
243  delete[] p;
244  delete[] f2;
245 
246  return;
247 }
248 
249 
250 // compute inverse laplacian for input array
251 template<typename IT, typename RT>
252 void Fourier::inverseLaplacian(RT *field)
253 {
254  IT i, j, k;
255  RT px, py, pz, pmag;
256 
257  for(long int i=0; i<POINTS; ++i)
258  double_field[i] = (fft_rt) field[i];
259 
260 #if USE_LONG_DOUBLES
261  fftwl_execute_dft_r2c(p_r2c, double_field, f_field);
262 #else
263  fftw_execute_dft_r2c(p_r2c, double_field, f_field);
264 #endif
265 
266  for(i=0; i<NX; i++)
267  {
268  px = (RT) (i<=NX/2 ? i : i-NX);
269  for(j=0; j<NY; j++)
270  {
271  py = (RT) (j<=NY/2 ? j : j-NY);
272  for(k=0; k<NZ/2+1; k++)
273  {
274  pz = (RT) k;
275 
276  IT fft_index = FFT_NP_INDEX(i,j,k);
277 
278  pmag = sqrt( pw2(px) + pw2(py) + pw2(pz) )*2.0*PI/H_LEN_FRAC;
279 
280  f_field[fft_index][0] /= -pmag*pmag*POINTS;
281  f_field[fft_index][1] /= -pmag*pmag*POINTS;
282  }
283  }
284  }
285  // zero mode?
286  f_field[0][0] = 0;
287  f_field[0][1] = 0;
288 
289 #if USE_LONG_DOUBLES
290  fftwl_execute_dft_c2r(p_c2r, f_field, double_field);
291 #else
292  fftw_execute_dft_c2r(p_c2r, f_field, double_field);
293 #endif
294 
295 
296  for(long int i=0; i<POINTS; ++i)
297  field[i] = (RT) double_field[i];
298 }
299 
300 } // namespace cosmo
301 
302 #endif
Definition: bardeen.cc:5
void powerDump(RT *in, IOT *iodata)
Compute a power spectrum and write to file.
Definition: Fourier.h:147
Definition: Fourier.h:21
void Initialize(IT nx, IT ny, IT nz)
Initialize a fourier class instance.
Definition: Fourier.h:79