ExaDG
Loading...
Searching...
No Matches
spectrum.h
1/* ______________________________________________________________________
2 *
3 * ExaDG - High-Order Discontinuous Galerkin for the Exa-Scale
4 *
5 * Copyright (C) 2021 by the ExaDG authors
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * ______________________________________________________________________
20 */
21
22#ifndef EXADG_POSTPROCESSOR_SPECTRAL_ANALYSIS_SPECTRUM_H_
23#define EXADG_POSTPROCESSOR_SPECTRAL_ANALYSIS_SPECTRUM_H_
24
25// C/C++
26#include <fftw3-mpi.h>
27#include <mpi.h>
28#include <cmath>
29
30// ExaDG
31#include <exadg/postprocessor/spectral_analysis/setup.h>
32
33// shortcuts for accessing linearized arrays
34#define u_comp2(i, j) u_comp[(j - local_start) * (N / 2 + 1) + i]
35#define v_comp2(i, j) v_comp[(j - local_start) * (N / 2 + 1) + i]
36#define u_comp3(i, j, k) u_comp[((k - local_start) * N + j) * (N / 2 + 1) + i]
37#define v_comp3(i, j, k) v_comp[((k - local_start) * N + j) * (N / 2 + 1) + i]
38#define w_comp3(i, j, k) w_comp[((k - local_start) * N + j) * (N / 2 + 1) + i]
39
40namespace dealspectrum
41{
46{
47 MPI_Comm const comm;
48
49 // reference to DEAL.SPECTRUM setup
50 Setup & s;
51 // is initialized?
52 bool initialized;
53 // rank of process which owns row
54 int * _indices_proc_rows;
55
56public:
61 SpectralAnalysis(MPI_Comm const & comm, Setup & s) : comm(comm), s(s), initialized(false)
62 {
63 }
64
71 inline int
73 {
74 return _indices_proc_rows[i];
75 }
76
83 void
84 getLocalRange(int & start, int & end)
85 {
86 start = local_start;
87 end = local_end;
88 }
89
93 void
95 {
96 // check if already initialized
97 if(this->initialized)
98 return;
99 this->initialized = true;
100
101 // extract settings
102 this->N = s.cells * s.points_dst;
103 this->dim = s.dim;
104 this->rank = s.rank;
105 this->size = s.size;
106 this->bins = s.bins;
107
108 // setup global size of output arrays...
109 n = new ptrdiff_t[dim];
110 for(int i = 0; i < dim - 1; i++)
111 n[i] = N;
112 n[dim - 1] = N / 2 + 1;
113
114 // ...get local size of local output arrays
115 ptrdiff_t local_elements = 0;
116 alloc_local = fftw_mpi_local_size(dim, n, comm, &local_elements, &local_start);
117 local_end = local_start + local_elements;
118
119 // determine how many rows each process has
120 int * global_elements = new int[size];
121 MPI_Allgather(&local_elements, 1, MPI_INTEGER, global_elements, 1, MPI_INTEGER, comm);
122
123 // ... save for each row by whom it is owned
124 _indices_proc_rows = new int[N];
125
126 for(int i = 0, c = 0; i < size; i++)
127 for(int j = 0; j < global_elements[i]; j++, c++)
128 _indices_proc_rows[c] = i;
129
130 // ... clean up
131 delete[] global_elements;
132
133 // modify global size for input array
134 n[dim - 1] = N;
135
136 // allocate memory
137 // ... for input array (real) - allocated together for all directions
138 u_real = fftw_alloc_real(2 * alloc_local * dim);
139 // ... and save required size
140 this->bsize = 2 * alloc_local;
141
142 // initialize input array with zero (not needed: only useful for IO -> hard zero)
143 for(int i = 0; i < 2 * alloc_local * dim; i++)
144 u_real[i] = 0;
145
146 // set pointer for v input field
147 v_real = u_real + 2 * alloc_local;
148
149 // allocate memory for output array (complex)
150 u_comp = fftw_alloc_complex(alloc_local);
151 v_comp = fftw_alloc_complex(alloc_local);
152
153 // do the same for 3D
154 if(dim == 3)
155 {
156 w_real = v_real + 2 * alloc_local;
157 w_comp = fftw_alloc_complex(alloc_local);
158 }
159
160 // allocate memory and ...
161 this->e = new double[N];
162 this->E = new double[N];
163 this->k = new double[N];
164 this->K = new double[N];
165 this->c = new double[N];
166 this->C = new double[N];
167 }
168
173 {
174 // not initialized -> nothing to clean up
175 if(not initialized)
176 return;
177
178 // free data structures
179 delete[] _indices_proc_rows;
180
181 free(n);
182 fftw_free(u_comp);
183 fftw_free(v_comp);
184 free(u_real);
185
186 if(dim == 3)
187 {
188 fftw_free(w_comp);
189 }
190
191 delete e;
192 delete E;
193 delete k;
194 delete K;
195 delete c;
196 delete C;
197 }
198
202 void
204 {
205 // perform FFT for u
206 fftw_plan pu = fftw_mpi_plan_dft_r2c(dim, n, u_real, u_comp, comm, FFTW_ESTIMATE);
207 fftw_execute(pu);
208 fftw_destroy_plan(pu);
209
210 // ... for v
211 fftw_plan pv = fftw_mpi_plan_dft_r2c(dim, n, v_real, v_comp, comm, FFTW_ESTIMATE);
212 fftw_execute(pv);
213 fftw_destroy_plan(pv);
214
215 // ... for w
216 if(dim == 3)
217 {
218 fftw_plan pw = fftw_mpi_plan_dft_r2c(dim, n, w_real, w_comp, comm, FFTW_ESTIMATE);
219 fftw_execute(pw);
220 fftw_destroy_plan(pw);
221 }
222 }
223
224 void
225 calculate_energy()
226 {
227 double scaling = pow(N, dim);
228 double e_physical = 0.0, e_spectral = 0.0;
229
230 for(int i = 0; i < 2 * alloc_local * dim; i++)
231 {
232 e_physical += u_real[i] * u_real[i];
233 }
234
235 // scale: integrate cell wise...
236 e_physical /= pow(N, dim);
237 // ... and make to energy 0.5*u^2
238 e_physical *= 0.5;
239
240 if(dim == 2)
241 {
242 for(int j = local_start; j < local_end; j++)
243 {
244 for(int i = 0; i < N; i++)
245 {
246 e_spectral += u_comp2(MIN(i, N - i), j)[0] * u_comp2(MIN(i, N - i), j)[0] +
247 u_comp2(MIN(i, N - i), j)[1] * u_comp2(MIN(i, N - i), j)[1] +
248 v_comp2(MIN(i, N - i), j)[0] * v_comp2(MIN(i, N - i), j)[0] +
249 v_comp2(MIN(i, N - i), j)[1] * v_comp2(MIN(i, N - i), j)[1];
250 }
251 }
252 }
253 else if(dim == 3)
254 {
255 for(int k_ = local_start; k_ < local_end; k_++)
256 {
257 for(int j = 0; j < N; j++)
258 {
259 for(int i = 0; i < N; i++)
260 {
261 e_spectral += u_comp3(MIN(i, N - i), j, k_)[0] * u_comp3(MIN(i, N - i), j, k_)[0] +
262 u_comp3(MIN(i, N - i), j, k_)[1] * u_comp3(MIN(i, N - i), j, k_)[1] +
263 v_comp3(MIN(i, N - i), j, k_)[0] * v_comp3(MIN(i, N - i), j, k_)[0] +
264 v_comp3(MIN(i, N - i), j, k_)[1] * v_comp3(MIN(i, N - i), j, k_)[1] +
265 w_comp3(MIN(i, N - i), j, k_)[0] * w_comp3(MIN(i, N - i), j, k_)[0] +
266 w_comp3(MIN(i, N - i), j, k_)[1] * w_comp3(MIN(i, N - i), j, k_)[1];
267 }
268 }
269 }
270 }
271 else
272 {
273 AssertThrow(false, dealii::ExcMessage("Not implemented."));
274 }
275
276 // scale: due to FFT...
277 e_spectral /= scaling * scaling;
278 // ... and make to energy 0.5*u^2
279 e_spectral *= 0.5;
280
281 MPI_Reduce(&e_physical, &this->e_d, 1, MPI_DOUBLE, MPI_SUM, 0, comm);
282 MPI_Reduce(&e_spectral, &this->e_s, 1, MPI_DOUBLE, MPI_SUM, 0, comm);
283 }
284
288 void
290 {
291 double scaling = pow(N, dim);
292
293 // ... init with zero
294 for(int i = 0; i < N; i++)
295 {
296 e[i] = 0;
297 k[i] = 0;
298 c[i] = 0;
299 }
300
301 // collect energy for local domain...
302 if(dim == 2)
303 {
304 for(int j = local_start; j < local_end; j++)
305 {
306 for(int i = 0; i < N; i++)
307 {
308 // determine wavenumber...
309 double r = sqrt(pow(MIN(i, N - i), 2.0) + pow(MIN(j, N - j), 2.0));
310 // ... use for binning
311 int p = static_cast<int>(std::round(r));
312 // ... update energy
313 e[p] += u_comp2(MIN(i, N - i), j)[0] * u_comp2(MIN(i, N - i), j)[0] +
314 u_comp2(MIN(i, N - i), j)[1] * u_comp2(MIN(i, N - i), j)[1] +
315 v_comp2(MIN(i, N - i), j)[0] * v_comp2(MIN(i, N - i), j)[0] +
316 v_comp2(MIN(i, N - i), j)[1] * v_comp2(MIN(i, N - i), j)[1];
317
318 // ... update kappa results
319 k[p] += r;
320 c[p]++;
321 }
322 }
323 }
324 else if(dim == 3)
325 {
326 for(int k_ = local_start; k_ < local_end; k_++)
327 {
328 for(int j = 0; j < N; j++)
329 {
330 for(int i = 0; i < N; i++)
331 {
332 // determine wavenumber...
333 double r =
334 sqrt(pow(MIN(i, N - i), 2.0) + pow(MIN(j, N - j), 2.0) + pow(MIN(k_, N - k_), 2.0));
335 // ... use for binning
336 int p = static_cast<int>(std::round(r));
337 // ... update energy
338 e[p] += u_comp3(MIN(i, N - i), j, k_)[0] * u_comp3(MIN(i, N - i), j, k_)[0] +
339 u_comp3(MIN(i, N - i), j, k_)[1] * u_comp3(MIN(i, N - i), j, k_)[1] +
340 v_comp3(MIN(i, N - i), j, k_)[0] * v_comp3(MIN(i, N - i), j, k_)[0] +
341 v_comp3(MIN(i, N - i), j, k_)[1] * v_comp3(MIN(i, N - i), j, k_)[1] +
342 w_comp3(MIN(i, N - i), j, k_)[0] * w_comp3(MIN(i, N - i), j, k_)[0] +
343 w_comp3(MIN(i, N - i), j, k_)[1] * w_comp3(MIN(i, N - i), j, k_)[1];
344
345 // ... update kappa results
346 k[p] += r;
347 c[p]++;
348 }
349 }
350 }
351 }
352
353 // ... sum up local results to global result
354 MPI_Reduce(e, E, N, MPI_DOUBLE, MPI_SUM, 0, comm);
355 MPI_Reduce(k, K, N, MPI_DOUBLE, MPI_SUM, 0, comm);
356 MPI_Reduce(c, C, N, MPI_DOUBLE, MPI_SUM, 0, comm);
357
358 // ... normalize results
359 for(int i = 0; i < N; i++)
360 {
361 // ... average energy and kappa
362 K[i] /= C[i];
363 // ... factor from fft
364 E[i] /= scaling * scaling;
365 // ... and make to energy 0.5*u^2
366 E[i] *= 0.5;
367 }
368 }
369
377 int
378 get_results(double *& K, double *& E, double *& C, double & e_d, double & e_s)
379 {
380 K = this->K;
381 E = this->E;
382 C = this->C;
383 e_d = this->e_d;
384 e_s = this->e_s;
385 return N / 2 + 1;
386 }
387
393 void
394 serialize(char const * filename)
395 {
396 int start = local_start;
397 int end = local_end;
398 int delta = 2 * (N / 2 + 1) * dealii::Utilities::pow(N, dim - 2);
399 int delta_all = 2 * (N / 2 + 1) * dealii::Utilities::pow(N, dim - 1) * sizeof(double);
400
401 //
402 int dofs = (end - start) * delta;
403 MPI_Offset disp = 8 * sizeof(int) + start * delta * sizeof(double); // bytes
404
405 // create view
406 MPI_Datatype stype;
407 MPI_Type_contiguous(dofs, MPI_DOUBLE, &stype);
408 MPI_Type_commit(&stype);
409
410 // read file
411 MPI_File fh;
412 MPI_File_open(comm, filename, MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);
413
414 MPI_File_set_view(fh, disp + delta_all * 0, MPI_DOUBLE, MPI_DOUBLE, "native", MPI_INFO_NULL);
415 MPI_File_write_all(fh, u_real, dofs, MPI_DOUBLE, MPI_STATUSES_IGNORE);
416
417 MPI_File_set_view(fh, disp + delta_all * 1, MPI_DOUBLE, MPI_DOUBLE, "native", MPI_INFO_NULL);
418 MPI_File_write_all(fh, v_real, dofs, MPI_DOUBLE, MPI_STATUSES_IGNORE);
419
420 if(dim == 3)
421 {
422 MPI_File_set_view(fh, disp + delta_all * 2, MPI_DOUBLE, MPI_DOUBLE, "native", MPI_INFO_NULL);
423 MPI_File_write_all(fh, w_real, dofs, MPI_DOUBLE, MPI_STATUSES_IGNORE);
424 }
425
426 MPI_File_close(&fh);
427 MPI_Type_free(&stype);
428 }
429
435 void
436 deserialize(char *& filename)
437 {
438 int start = local_start;
439 int end = local_end;
440 int delta = 2 * (N / 2 + 1) * dealii::Utilities::pow(N, dim - 2);
441 int delta_all = 2 * (N / 2 + 1) * dealii::Utilities::pow(N, dim - 1) * sizeof(double);
442
443 //
444 int dofs = (end - start) * delta;
445 MPI_Offset disp = 8 * sizeof(int) + start * delta * sizeof(double); // bytes
446
447 // create view
448 MPI_Datatype stype;
449 MPI_Type_contiguous(dofs, MPI_DOUBLE, &stype);
450 MPI_Type_commit(&stype);
451
452 // read file
453 MPI_File fh;
454 MPI_File_open(comm, filename, MPI_MODE_RDONLY, MPI_INFO_NULL, &fh);
455
456 MPI_File_set_view(fh, disp + delta_all * 0, MPI_DOUBLE, MPI_DOUBLE, "native", MPI_INFO_NULL);
457 MPI_File_read_all(fh, u_real, dofs, MPI_DOUBLE, MPI_STATUSES_IGNORE);
458
459 MPI_File_set_view(fh, disp + delta_all * 1, MPI_DOUBLE, MPI_DOUBLE, "native", MPI_INFO_NULL);
460 MPI_File_read_all(fh, v_real, dofs, MPI_DOUBLE, MPI_STATUSES_IGNORE);
461
462 if(dim == 3)
463 {
464 MPI_File_set_view(fh, disp + delta_all * 2, MPI_DOUBLE, MPI_DOUBLE, "native", MPI_INFO_NULL);
465 MPI_File_read_all(fh, w_real, dofs, MPI_DOUBLE, MPI_STATUSES_IGNORE);
466 }
467
468 MPI_File_close(&fh);
469 MPI_Type_free(&stype);
470 }
471
472private:
473 // number of dofs in each direction
474 int N;
475 // dimensions
476 int dim;
477 // rank of this process
478 int rank;
479 // number of processes
480 int size;
481 // bin count
482 int bins;
483 // number of dofs in each direction (for FFTW)
484 ptrdiff_t * n;
485 // local row/plane range: start
486 ptrdiff_t local_start;
487 // ... end
488 ptrdiff_t local_end;
489 ptrdiff_t alloc_local;
490
491public:
492 // size of each real field
493 int bsize;
494 // real field for u
495 double * u_real;
496
497private:
498 // ... for v
499 double * v_real;
500 // ... for w
501 double * w_real;
502 // complex field for u
503 fftw_complex * u_comp;
504 // ... for v
505 fftw_complex * v_comp;
506 // ... for w
507 fftw_complex * w_comp;
508
509private:
510 // array for locally collecting energy
511 double * e;
512 // ... kappa
513 double * k;
514 // ... kappa count
515 double * c;
516 // array for globally collecting energy (on rank 0)
517 double * E;
518 // ... kappa
519 double * K;
520 // ... kappa count
521 double * C;
522 double e_d;
523 double e_s;
524};
525
526} // namespace dealspectrum
527
528#endif /* EXADG_POSTPROCESSOR_SPECTRAL_ANALYSIS_SPECTRUM_H_ */
Definition setup.h:56
~SpectralAnalysis()
Definition spectrum.h:172
void calculate_energy_spectrum()
Definition spectrum.h:289
SpectralAnalysis(MPI_Comm const &comm, Setup &s)
Definition spectrum.h:61
void init()
Definition spectrum.h:94
void deserialize(char *&filename)
Definition spectrum.h:436
void serialize(char const *filename)
Definition spectrum.h:394
int get_results(double *&K, double *&E, double *&C, double &e_d, double &e_s)
Definition spectrum.h:378
int indices_proc_rows(int i)
Definition spectrum.h:72
void execute()
Definition spectrum.h:203
void getLocalRange(int &start, int &end)
Definition spectrum.h:84