S2kit  1.1
Toolkit for working with functions defined on the sphere
FST_semi_memo.c
Go to the documentation of this file.
1 
10 #include "s2kit/FST_semi_memo.h"
11 
12 #include <math.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include <fftw3.h>
17 
18 #include "s2kit/cospml.h"
19 #include "s2kit/naive.h"
20 #include "s2kit/seminaive.h"
21 #include "s2kit/weights.h"
22 #include "s2kit/chebyshev_nodes.h"
23 #include "s2kit/util.h"
24 
68 void FSTSemiMemo(double* rdata, double* idata, double* rcoeffs, double* icoeffs, const int bw,
69  double** seminaive_naive_table, double* workspace, DataFormat data_format,
70  const int cutoff, fftw_plan* DCT_plan, fftw_plan* FFT_plan, double* weights) {
71  int size = 2 * bw;
72 
73  // total workspace is (8 * bw^2) + (7 * bw)
74  double* rres = workspace; // needs (size * size) = (4 * bw^2)
75  double* ires = rres + (size * size); // needs (size * size) = (4 * bw^2)
76  double* fltres = ires + (size * size); // needs bw
77  double* eval_pts = fltres + bw; // needs (2 * bw)
78  double* scratchpad = eval_pts + (size); // needs (4 * bw)
79 
80  // do the FFTs along phi
81  fftw_execute_split_dft(*FFT_plan, rdata, idata, rres, ires);
82 
83  // Normalize
84  // The associated Legendres are of norm 1, so because the spherical
85  // harmonics are of norm 1 we are using coeff of sqrt(2*pi) here.
86  double normed_coeff = sqrt(2. * M_PI) / size;
87  for (int i = 0; i < size * size; ++i) {
88  rres[i] *= normed_coeff;
89  ires[i] *= normed_coeff;
90  }
91 
92  // point to start of output data buffers
93  double* rdataptr = rcoeffs;
94  double* idataptr = icoeffs;
95 
96  for (int m = 0; m < cutoff; ++m) { // semi-naive part
97  // real part
98  DLTSemi(rres + (m * size), bw, m, fltres, scratchpad, seminaive_naive_table[m], weights, DCT_plan);
99  // load real part of coefficients into output space
100  memcpy(rdataptr, fltres, sizeof(double) * (bw - m));
101  rdataptr += bw - m;
102 
103  // imaginary part
104  DLTSemi(ires + (m * size), bw, m, fltres, scratchpad, seminaive_naive_table[m], weights, DCT_plan);
105  // load imaginary part of coefficients into output space
106  memcpy(idataptr, fltres, sizeof(double) * (bw - m));
107  idataptr += bw - m;
108  }
109 
110  for (int m = cutoff; m < bw; ++m) { // naive part
111  // real part
112  DLTNaive(rres + (m * size), bw, m, weights, fltres, seminaive_naive_table[m], scratchpad);
113  memcpy(rdataptr, fltres, sizeof(double) * (bw - m));
114  rdataptr += bw - m;
115 
116  // imaginary part
117  DLTNaive(ires + (m * size), bw, m, weights, fltres, seminaive_naive_table[m], scratchpad);
118  memcpy(idataptr, fltres, sizeof(double) * (bw - m));
119  idataptr += bw - m;
120  }
121 
122  // upper coefficients
123  /*
124  If the data is real, we don't have to compute the coeffs whose
125  order is less than 0, since the data is real, we know that
126 
127  f-hat(l,-m) = (-1)^m * conjugate(f-hat(l,m)),
128 
129  so use that to get the rest of the coefficients
130  */
131  if (data_format == REAL) {
132  double coeff = 1.;
133  for (int i = 1; i < bw; ++i) {
134  coeff *= -1.;
135  for (int j = i; j < bw; ++j) {
136  int index0 = IndexOfHarmonicCoeff(i, j, bw);
137  int index1 = IndexOfHarmonicCoeff(-i, j, bw);
138 
139  rcoeffs[index1] = coeff * rcoeffs[index0];
140  icoeffs[index1] = -coeff * icoeffs[index0];
141  }
142  }
143 
144  return;
145  }
146 
147  // complex data
148  /*
149  Note that `m` is greater than `bw` here, but this is for
150  purposes of indexing the input data arrays. The "true"
151  value of `m` as a parameter for Pml is `size-m`
152  */
153  for (int m = bw + 1; m <= size - cutoff; ++m) { // naive part
154  // real part
155  DLTNaive(rres + (m * size), bw, size - m, weights, fltres, seminaive_naive_table[size - m], scratchpad);
156  // load real part of coefficients into output space
157  if (m % 2) {
158  for (int i = 0; i < m - bw; ++i)
159  rdataptr[i] = -fltres[i];
160  } else {
161  memcpy(rdataptr, fltres, sizeof(double) * (m - bw));
162  }
163  rdataptr += m - bw;
164 
165  // imaginary part
166  DLTNaive(ires + (m * size), bw, size - m, weights, fltres, seminaive_naive_table[size - m], scratchpad);
167  // load imaginary part of coefficients into output space
168  if (m % 2) {
169  for (int i = 0; i < m - bw; ++i)
170  idataptr[i] = -fltres[i];
171  } else {
172  memcpy(idataptr, fltres, sizeof(double) * (m - bw));
173  }
174  idataptr += m - bw;
175  }
176 
177  for (int m = size - cutoff + 1; m < size; ++m) { // semi-naive part
178  // real part
179  DLTSemi(rres + (m * size), bw, size - m, fltres, scratchpad, seminaive_naive_table[size - m],
180  weights, DCT_plan);
181  // load real part of coefficients into output space
182  if (m % 2) {
183  for (int i = 0; i < m - bw; ++i)
184  rdataptr[i] = -fltres[i];
185  } else {
186  memcpy(rdataptr, fltres, sizeof(double) * (m - bw));
187  }
188  rdataptr += m - bw;
189 
190  // imaginary part
191  DLTSemi(ires + (m * size), bw, size - m, fltres, scratchpad, seminaive_naive_table[size - m],
192  weights, DCT_plan);
193  // load imaginary part of coefficients into output space
194  if (m % 2) {
195  for (int i = 0; i < m - bw; ++i)
196  idataptr[i] = -fltres[i];
197  } else {
198  memcpy(idataptr, fltres, sizeof(double) * (m - bw));
199  }
200  idataptr += m - bw;
201  }
202 }
203 
228 void InvFSTSemiMemo(double* rcoeffs, double* icoeffs, double* rdata, double* idata, const int bw,
229  double** transpose_seminaive_naive_table, double* workspace, DataFormat data_format,
230  const int cutoff, fftw_plan* inv_DCT_plan, fftw_plan* inv_FFT_plan) {
231  // TODO try to use double** -> double*
232  int size = 2 * bw;
233 
234  // total workspace = (8 * bw^2) + (10 * bw)
235  double* rfourdata = workspace; // needs (size * size)
236  double* ifourdata = rfourdata + (size * size); // needs (size * size)
237  double* rinvfltres = ifourdata + (size * size); // needs (2 * bw)
238  double* iminvfltres = rinvfltres + (size); // needs (2 * bw)
239  double* sin_values = iminvfltres + (size); // needs (2 * bw)
240  double* eval_pts = sin_values + (size); // needs (2 * bw)
241  double* scratchpad = eval_pts + (size); // needs (2 * bw)
242 
243  // load up the sin_values array
244  AcosOfChebyshevNodes(size, eval_pts);
245  for (int i = 0; i < size; ++i)
246  sin_values[i] = sin(eval_pts[i]);
247 
248  // do all of the inverse Legendre transforms
249  double* rdataptr = rcoeffs;
250  double* idataptr = icoeffs;
251 
252  for (int m = 0; m < cutoff; ++m) { // semi-naive part
253  // real part
254  InvDLTSemi(rdataptr, bw, m, rinvfltres, transpose_seminaive_naive_table[m], sin_values, scratchpad,
255  inv_DCT_plan);
256  // imaginary part
257  InvDLTSemi(idataptr, bw, m, iminvfltres, transpose_seminaive_naive_table[m], sin_values, scratchpad,
258  inv_DCT_plan);
259 
260  // store normal, then tranpose before doing inverse fft
261  memcpy(rfourdata + (m * size), rinvfltres, sizeof(double) * size);
262  memcpy(ifourdata + (m * size), iminvfltres, sizeof(double) * size);
263 
264  rdataptr += bw - m;
265  idataptr += bw - m;
266  }
267 
268  for (int m = cutoff; m < bw; ++m) { // naive part
269  // real part
270  InvDLTNaive(rdataptr, bw, m, rinvfltres, transpose_seminaive_naive_table[m]);
271  // imaginary part
272  InvDLTNaive(idataptr, bw, m, iminvfltres, transpose_seminaive_naive_table[m]);
273 
274  // store normal, then tranpose before doing inverse fft
275  memcpy(rfourdata + (m * size), rinvfltres, sizeof(double) * size);
276  memcpy(ifourdata + (m * size), iminvfltres, sizeof(double) * size);
277 
278  rdataptr += bw - m;
279  idataptr += bw - m;
280  }
281 
282  // fill in zero values where m = bw (from problem definition)
283  memset(rfourdata + (bw * size), 0, sizeof(double) * size);
284  memset(ifourdata + (bw * size), 0, sizeof(double) * size);
285 
286  /*
287  If the data is real, we don't have to compute the coeffs whose order is less than 0,
288  i.e. since the data is real, we know that
289 
290  invf-hat(l,-m) = conjugate(invf-hat(l,m)),
291 
292  so use that to get the rest of the coefficients
293  */
294  if (data_format == COMPLEX) {
295  // do negative m values
296  for (int m = bw + 1; m <= size - cutoff; ++m) { // naive part
297  InvDLTNaive(rdataptr, bw, size - m, rinvfltres, transpose_seminaive_naive_table[size - m]);
298  InvDLTNaive(idataptr, bw, size - m, iminvfltres, transpose_seminaive_naive_table[size - m]);
299 
300  // store normal, then tranpose before doing inverse fft
301  if (m % 2)
302  for (int i = 0; i < size; ++i) {
303  rinvfltres[i] *= -1.0;
304  iminvfltres[i] *= -1.0;
305  }
306 
307  memcpy(rfourdata + (m * size), rinvfltres, sizeof(double) * size);
308  memcpy(ifourdata + (m * size), iminvfltres, sizeof(double) * size);
309 
310  rdataptr += bw - (size - m);
311  idataptr += bw - (size - m);
312  }
313 
314  for (int m = size - cutoff + 1; m < size; ++m) { // semi-naive part
315  InvDLTSemi(rdataptr, bw, size - m, rinvfltres, transpose_seminaive_naive_table[size - m],
316  sin_values, scratchpad, inv_DCT_plan);
317  InvDLTSemi(idataptr, bw, size - m, iminvfltres, transpose_seminaive_naive_table[size - m],
318  sin_values, scratchpad, inv_DCT_plan);
319 
320  // store normal, then tranpose before doing inverse fft
321  if (m % 2)
322  for (int i = 0; i < size; ++i) {
323  rinvfltres[i] *= -1.0;
324  iminvfltres[i] *= -1.0;
325  }
326 
327  memcpy(rfourdata + (m * size), rinvfltres, sizeof(double) * size);
328  memcpy(ifourdata + (m * size), iminvfltres, sizeof(double) * size);
329 
330  rdataptr += bw - (size - m);
331  idataptr += bw - (size - m);
332  }
333  } else { // real data
334  for (int m = bw + 1; m < size; ++m) {
335  memcpy(rfourdata + (m * size), rfourdata + ((size - m) * size), sizeof(double) * size);
336  memcpy(ifourdata + (m * size), ifourdata + ((size - m) * size), sizeof(double) * size);
337 
338  for (int i = 0; i < size; ++i)
339  ifourdata[(m * size) + i] *= -1.0;
340  }
341  }
342 
343  // normalize
344  double normed_coeff = 1. / (sqrt(2. * M_PI));
345  for (int i = 0; i < 4 * bw * bw; i++) {
346  rfourdata[i] *= normed_coeff;
347  ifourdata[i] *= normed_coeff;
348  }
349 
350  fftw_execute_split_dft(*inv_FFT_plan, ifourdata, rfourdata, idata, rdata);
351 }
352 
374 void FZTSemiMemo(double* rdata, double* idata, double* rres, double* ires, const int bw, double* cos_pml_table,
375  double* workspace, const DataFormat data_format, fftw_plan* DCT_plan, double* weights) {
376  int size = 2 * bw;
377 
378  double* r0 = workspace; // needs (2 * bw)
379  double* i0 = r0 + size; // needs (2 * bw)
380  double* scratchpad = i0 + size; // needs (4 * bw)
381 
382  // total workspace = 13*bw
383 
384  double dsize = sqrt(2. * M_PI) / size;
385  // compute the m = 0 components
386  for (int i = 0; i < size; ++i) {
387  double tmpreal = 0.0;
388  double tmpimag = 0.0;
389 
390  for (int j = 0; j < size; ++j) {
391  tmpreal += rdata[(i * size) + j];
392  tmpimag += idata[(i * size) + j];
393  }
394 
395  // normalize
396  r0[i] = tmpreal * dsize;
397  i0[i] = tmpimag * dsize;
398  }
399 
400  // real part
401  DLTSemi(r0, bw, 0, rres, scratchpad, cos_pml_table, weights, DCT_plan);
402 
403  if (data_format == COMPLEX) // if data is complex, do imaginary part
404  DLTSemi(i0, bw, 0, ires, scratchpad, cos_pml_table, weights, DCT_plan);
405  else // otherwise set coefficients to zero
406  memset(ires, 0, sizeof(double) * size);
407 }
408 
439 void ConvOn2SphereSemiMemo(double* rdata, double* idata, double* rfilter, double* ifilter, double* rres, double* ires,
440  const int bw, double* workspace) {
441  // TODO cutoff to args? (doc days it's legal but needs hardcode for changing param value now)
442  int size = 2 * bw;
443  int cutoff = bw;
444  int legendre_size = Reduced_Naive_TableSize(bw, cutoff) + Reduced_SpharmonicTableSize(bw, cutoff);
445 
446  double* spharmonic_result_space = workspace; // needs legendre_size
447  double* transpose_spharmonic_result_space = spharmonic_result_space + legendre_size; // needs legendre_size
448 
449  double* frres = transpose_spharmonic_result_space + legendre_size; // needs (bw * bw)
450  double* fires = frres + (bw * bw); // needs (bw * bw)
451  double* trres = fires + (bw * bw); // needs (bw * bw)
452  double* tires = trres + (bw * bw); // needs (bw * bw)
453  double* filtrres = tires + (bw * bw); // needs bw
454  double* filtires = filtrres + bw; // needs bw
455  double* scratchpad = filtires + bw; // needs (8 * bw^2) + (10 * bw)
456 
457  double* weights = (double*)malloc(sizeof(double) * 4 * bw);
458  GenerateWeightsForDLT(bw, weights);
459 
460  // Make DCT plans. Note that I will be using the GURU interface to execute these plans within the routines
461  fftw_plan DCT_plan = fftw_plan_r2r_1d(size, weights, rdata, FFTW_REDFT10, FFTW_ESTIMATE);
462  fftw_plan inv_DCT_plan = fftw_plan_r2r_1d(size, weights, rdata, FFTW_REDFT01, FFTW_ESTIMATE);
463 
464  // fftw "preamble"
465  // Note that FFT plan places the output in a transposed array
466  int rank = 1;
467  fftw_iodim dims[rank];
468  dims[0].n = size;
469  dims[0].is = 1;
470  dims[0].os = size;
471 
472  int howmany_rank = 1;
473  fftw_iodim howmany_dims[howmany_rank];
474  howmany_dims[0].n = size;
475  howmany_dims[0].is = size;
476  howmany_dims[0].os = 1;
477 
478  fftw_plan FFT_plan = fftw_plan_guru_split_dft(rank, dims, howmany_rank, howmany_dims, rdata, idata, workspace,
479  workspace + (4 * bw * bw), FFTW_ESTIMATE);
480 
481  // Note that FFT plans assumes that I'm working with a transposed array, e.g. the inputs for a length 2*bw transform
482  // are placed every 2*bw apart, the output will be consecutive entries in the array
483 
484  rank = 1;
485  dims[0].n = size;
486  dims[0].is = size;
487  dims[0].os = 1;
488 
489  howmany_rank = 1;
490  howmany_dims[0].n = size;
491  howmany_dims[0].is = 1;
492  howmany_dims[0].os = size;
493 
494  fftw_plan inv_FFT_plan = fftw_plan_guru_split_dft(rank, dims, howmany_rank, howmany_dims, rdata, idata, workspace,
495  workspace + (4 * bw * bw), FFTW_ESTIMATE);
496 
497  // precompute the associated Legendre fcts
498  double** spharmonic_pml_table = Spharmonic_Pml_Table(bw, spharmonic_result_space, scratchpad);
499  double** transpose_spharmonic_pml_table =
500  Transpose_Spharmonic_Pml_Table(spharmonic_pml_table, bw, transpose_spharmonic_result_space);
501 
502  FSTSemiMemo(rdata, idata, frres, fires, bw, spharmonic_pml_table, scratchpad, 1, bw, &DCT_plan, &FFT_plan, weights);
503  FZTSemiMemo(rfilter, ifilter, filtrres, filtires, bw, spharmonic_pml_table[0], scratchpad, 1, &DCT_plan, weights);
504 
505  TransMult(frres, fires, filtrres, filtires, trres, tires, bw);
506 
507  InvFSTSemiMemo(trres, tires, rres, ires, bw, transpose_spharmonic_pml_table, scratchpad, 1, bw, &inv_DCT_plan,
508  &inv_FFT_plan);
509 
510  free(weights);
511  free(spharmonic_pml_table);
512  free(transpose_spharmonic_pml_table);
513 
514  fftw_destroy_plan(inv_FFT_plan);
515  fftw_destroy_plan(FFT_plan);
516  fftw_destroy_plan(inv_DCT_plan);
517  fftw_destroy_plan(DCT_plan);
518 }
InvFSTSemiMemo
void InvFSTSemiMemo(double *rcoeffs, double *icoeffs, double *rdata, double *idata, const int bw, double **transpose_seminaive_naive_table, double *workspace, DataFormat data_format, const int cutoff, fftw_plan *inv_DCT_plan, fftw_plan *inv_FFT_plan)
Computes inverse spherical harmonic transform.
Definition: FST_semi_memo.c:228
Reduced_Naive_TableSize
int Reduced_Naive_TableSize(const int, const int)
Definition: cospml.c:434
TransMult
void TransMult(double *, double *, double *, double *, double *, double *, const int)
Multiplies harmonic coefficients of a function and a filter.
Definition: util.c:68
Transpose_Spharmonic_Pml_Table
double ** Transpose_Spharmonic_Pml_Table(double **, const int, double *)
Definition: cospml.c:415
weights.h
FZTSemiMemo
void FZTSemiMemo(double *rdata, double *idata, double *rres, double *ires, const int bw, double *cos_pml_table, double *workspace, const DataFormat data_format, fftw_plan *DCT_plan, double *weights)
Computes zonal harmonic transform using seminaive algorithm.
Definition: FST_semi_memo.c:374
chebyshev_nodes.h
IndexOfHarmonicCoeff
int IndexOfHarmonicCoeff(const int, const int, const int)
Gives the position of the coefficient f-hat(m,l) in the one-row array.
Definition: util.c:42
COMPLEX
Definition: util.h:11
naive.h
InvDLTSemi
void InvDLTSemi(double *, const int, const int, double *, double *, double *, double *, fftw_plan *)
Computes the inverse Legendre transform using the transposed seminaive algorithm.
Definition: seminaive.c:56
InvDLTNaive
void InvDLTNaive(double *, const int, const int, double *, double *)
The inverse discrete Legendre transform.
Definition: naive.c:78
GenerateWeightsForDLT
void GenerateWeightsForDLT(const int, double *)
Generates weights for both even and odd order Legendre transforms for a given bandwidth.
Definition: weights.c:32
Spharmonic_Pml_Table
double ** Spharmonic_Pml_Table(const int, double *, double *)
Definition: cospml.c:387
FSTSemiMemo
void FSTSemiMemo(double *rdata, double *idata, double *rcoeffs, double *icoeffs, const int bw, double **seminaive_naive_table, double *workspace, DataFormat data_format, const int cutoff, fftw_plan *DCT_plan, fftw_plan *FFT_plan, double *weights)
Computes spherical harmonic transform using the seminaive and naive algorithms.
Definition: FST_semi_memo.c:68
REAL
Definition: util.h:12
AcosOfChebyshevNodes
void AcosOfChebyshevNodes(const int, double *)
Generates an array of the angular arguments of n Chebyshev nodes.
Definition: chebyshev_nodes.c:16
ConvOn2SphereSemiMemo
void ConvOn2SphereSemiMemo(double *rdata, double *idata, double *rfilter, double *ifilter, double *rres, double *ires, const int bw, double *workspace)
Convolves two functions defined on the 2-sphere.
Definition: FST_semi_memo.c:439
DLTNaive
void DLTNaive(double *, const int, const int, double *, double *, double *, double *)
The naive forward discrete Legendre transform.
Definition: naive.c:34
Reduced_SpharmonicTableSize
int Reduced_SpharmonicTableSize(const int, const int)
Definition: cospml.c:107
DataFormat
DataFormat
Result data format for FST functions.
Definition: util.h:10
cospml.h
DLTSemi
void DLTSemi(double *, const int, const int, double *, double *, double *, double *, fftw_plan *)
Computes the Legendre transform of data using the seminaive algorithm.
Definition: seminaive.c:153
util.h
FST_semi_memo.h
seminaive.h