LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
LArFFTW.h
Go to the documentation of this file.
1 #ifndef LARFFTW_H
2 #define LARFFTW_H
3 
4 // C/C++ standard libraries
5 #include <algorithm>
6 #include <complex>
7 #include <string>
8 #include <vector>
9 
10 #include "fftw3.h"
11 
12 #include "cetlib_except/coded_exception.h"
15 
16 namespace util {
17 
18  class LArFFTW {
19 
20  public:
21  using FloatVector = std::vector<float>;
22  using DoubleVector = std::vector<double>;
23  using ComplexVector = std::vector<std::complex<double>>;
24 
25  LArFFTW(int transformSize, const void* fplan, const void* rplan, int fitbins);
26  ~LArFFTW();
27 
28  template <class T>
29  void DoFFT(std::vector<T>& input);
30  template <class T>
31  void DoFFT(std::vector<T>& input, ComplexVector& output);
32  template <class T>
33  void DoInvFFT(std::vector<T>& output);
34  template <class T>
35  void DoInvFFT(ComplexVector& input, std::vector<T>& output);
36 
37  // ... Do convolution calculation (for simulation).
38  template <class T>
39  void Convolute(std::vector<T>& func, const ComplexVector& kern);
40  template <class T>
41  void Convolute(std::vector<T>& func, std::vector<T>& resp);
42 
43  // ... Do deconvolution calculation (for reconstruction).
44  template <class T>
45  void Deconvolute(std::vector<T>& func, const ComplexVector& kern);
46  template <class T>
47  void Deconvolute(std::vector<T>& func, std::vector<T>& resp);
48 
49  // ... Do correlation
50  template <class T>
51  void Correlate(std::vector<T>& func, const ComplexVector& kern);
52  template <class T>
53  void Correlate(std::vector<T>& func, std::vector<T>& resp);
54 
55  void ShiftData(ComplexVector& input, double shift);
56  template <class T>
57  void ShiftData(std::vector<T>& input, double shift);
58 
59  template <class T>
60  void AlignedSum(std::vector<T>& input, std::vector<T>& output, bool add = true);
61  template <class T>
62  T PeakCorrelation(std::vector<T>& shape1, std::vector<T>& shape2);
63 
64  private:
65  ComplexVector fKern; // transformed response function
66  ComplexVector fCompTemp; // temporary complex data
67  std::vector<float> fConvHist; // Fit data histogram
68  int fSize; // size of transform
69  int fFreqSize; // size of frequency space
70  void* fIn;
71  void* fOut;
72  const void* fPlan;
73  void* rIn;
74  void* rOut;
75  const void* rPlan;
76  int fFitBins; // Bins used for peak fit
77 
79  };
80 
81 } // end namespace util
82 
83 // -----------------------------------------------------------------------------
84 // ~~~~ Do Forward Fourier Transform - DoFFT( REAL In )
85 // -----------------------------------------------------------------------------
86 template <class T>
87 inline void util::LArFFTW::DoFFT(std::vector<T>& input)
88 {
89  // ..set point
90  for (size_t p = 0; p < input.size(); ++p) {
91  ((double*)fIn)[p] = input[p];
92  }
93 
94  // ..transform (using the New-array Execute Functions)
95  fftw_execute_dft_r2c((fftw_plan)fPlan, (double*)fIn, (fftw_complex*)fOut);
96 
97  return;
98 }
99 
100 // -----------------------------------------------------------------------------
101 // ~~~~ Do Forward Fourier Transform - DoFFT( REAL In, COMPLEX Out )
102 // -----------------------------------------------------------------------------
103 template <class T>
104 inline void util::LArFFTW::DoFFT(std::vector<T>& input, ComplexVector& output)
105 {
106  // ..set point
107  for (size_t p = 0; p < input.size(); ++p) {
108  ((double*)fIn)[p] = input[p];
109  }
110 
111  // ..transform (using the New-array Execute Functions)
112  fftw_execute_dft_r2c((fftw_plan)fPlan, (double*)fIn, (fftw_complex*)fOut);
113 
114  for (int i = 0; i < fFreqSize; ++i) {
115  output[i].real(((fftw_complex*)fOut)[i][0]);
116  output[i].imag(((fftw_complex*)fOut)[i][1]);
117  }
118 
119  return;
120 }
121 
122 // -----------------------------------------------------------------------------
123 // ~~~~ Do Inverse Fourier Transform - DoInvFFT( REAL Out )
124 // -----------------------------------------------------------------------------
125 template <class T>
126 inline void util::LArFFTW::DoInvFFT(std::vector<T>& output)
127 {
128  // ..transform (using the New-array Execute Functions)
129  fftw_execute_dft_c2r((fftw_plan)rPlan, (fftw_complex*)rIn, (double*)rOut);
130 
131  // ..get point real
132  double factor = 1.0 / (double)fSize;
133  const double* array = (const double*)(rOut);
134  for (int i = 0; i < fSize; ++i) {
135  output[i] = factor * array[i];
136  }
137 
138  return;
139 }
140 
141 // -----------------------------------------------------------------------------
142 // ~~~~ Do Inverse Fourier Transform - DoInvFFT( COMPLEX In, REAL Out )
143 // -----------------------------------------------------------------------------
144 template <class T>
145 inline void util::LArFFTW::DoInvFFT(ComplexVector& input, std::vector<T>& output)
146 {
147  // ..set point complex
148  for (int i = 0; i < fFreqSize; ++i) {
149  ((fftw_complex*)rIn)[i][0] = input[i].real();
150  ((fftw_complex*)rIn)[i][1] = input[i].imag();
151  }
152 
153  // ..transform (using the New-array Execute Functions)
154  fftw_execute_dft_c2r((fftw_plan)rPlan, (fftw_complex*)rIn, (double*)rOut);
155 
156  // ..get point real
157  double factor = 1.0 / (double)fSize;
158  const double* array = (const double*)(rOut);
159  for (int i = 0; i < fSize; ++i) {
160  output[i] = factor * array[i];
161  }
162 
163  return;
164 }
165 
166 // -----------------------------------------------------------------------------
167 // ~~~~ Do Convolution: using transformed response function
168 // -----------------------------------------------------------------------------
169 template <class T>
170 inline void util::LArFFTW::Convolute(std::vector<T>& func, const ComplexVector& kern)
171 {
172 
173  // ... Make sure that time series and kernel have the correct size.
174  int n = func.size();
175  if (n != fSize) { throw cet::exception("LArFFTW") << "Bad time series size = " << n << "\n"; }
176  n = kern.size();
177  if (n != fFreqSize) { throw cet::exception("LArFFTW") << "Bad kernel size = " << n << "\n"; }
178 
179  DoFFT(func);
180 
181  // ..perform the convolution
182  for (int i = 0; i < fFreqSize; ++i) {
183  double re = ((fftw_complex*)fOut)[i][0];
184  double im = ((fftw_complex*)fOut)[i][1];
185  ((fftw_complex*)rIn)[i][0] = re * kern[i].real() - im * kern[i].imag();
186  ((fftw_complex*)rIn)[i][1] = re * kern[i].imag() + im * kern[i].real();
187  }
188 
189  DoInvFFT(func);
190 }
191 
192 // -----------------------------------------------------------------------------
193 // ~~~~ Do Convolution: using all time-domain information
194 // -----------------------------------------------------------------------------
195 template <class T>
196 inline void util::LArFFTW::Convolute(std::vector<T>& func1, std::vector<T>& func2)
197 {
198 
199  // ... Make sure that time series has the correct size.
200  int n = func1.size();
201  if (n != fSize) { throw cet::exception("LArFFTW") << "Bad 1st time series size = " << n << "\n"; }
202  n = func2.size();
203  if (n != fSize) { throw cet::exception("LArFFTW") << "Bad 2nd time series size = " << n << "\n"; }
204 
205  DoFFT(func2);
206  for (int i = 0; i < fFreqSize; ++i) {
207  fKern[i].real(((fftw_complex*)fOut)[i][0]);
208  fKern[i].imag(((fftw_complex*)fOut)[i][1]);
209  }
210  DoFFT(func1);
211 
212  // ..perform the convolution
213  for (int i = 0; i < fFreqSize; ++i) {
214  double re = ((fftw_complex*)fOut)[i][0];
215  double im = ((fftw_complex*)fOut)[i][1];
216  ((fftw_complex*)rIn)[i][0] = re * fKern[i].real() - im * fKern[i].imag();
217  ((fftw_complex*)rIn)[i][1] = re * fKern[i].imag() + im * fKern[i].real();
218  }
219 
220  DoInvFFT(func1);
221 }
222 
223 // -----------------------------------------------------------------------------
224 // ~~~~ Do Deconvolution: using transformed response function
225 // -----------------------------------------------------------------------------
226 template <class T>
227 inline void util::LArFFTW::Deconvolute(std::vector<T>& func, const ComplexVector& kern)
228 {
229 
230  // ... Make sure that time series and kernel have the correct size.
231  int n = func.size();
232  if (n != fSize) { throw cet::exception("LArFFTW") << "Bad time series size = " << n << "\n"; }
233  n = kern.size();
234  if (n != fFreqSize) { throw cet::exception("LArFFTW") << "Bad kernel size = " << n << "\n"; }
235 
236  DoFFT(func);
237 
238  // ..perform the deconvolution
239  double a, b, c, d, e;
240  for (int i = 0; i < fFreqSize; ++i) {
241  a = ((fftw_complex*)fOut)[i][0];
242  b = ((fftw_complex*)fOut)[i][1];
243  c = kern[i].real();
244  d = kern[i].imag();
245  e = 1. / (c * c + d * d);
246  ((fftw_complex*)rIn)[i][0] = (a * c + b * d) * e;
247  ((fftw_complex*)rIn)[i][1] = (b * c - a * d) * e;
248  }
249 
250  DoInvFFT(func);
251 }
252 
253 // -----------------------------------------------------------------------------
254 // ~~~~ Do Deconvolution: using all time domain information
255 // -----------------------------------------------------------------------------
256 template <class T>
257 inline void util::LArFFTW::Deconvolute(std::vector<T>& func, std::vector<T>& resp)
258 {
259 
260  // ... Make sure that time series has the correct size.
261  int n = func.size();
262  if (n != fSize) { throw cet::exception("LArFFTW") << "Bad 1st time series size = " << n << "\n"; }
263  n = resp.size();
264  if (n != fSize) { throw cet::exception("LArFFTW") << "Bad 2nd time series size = " << n << "\n"; }
265 
266  DoFFT(resp);
267  for (int i = 0; i < fFreqSize; ++i) {
268  fKern[i].real(((fftw_complex*)fOut)[i][0]);
269  fKern[i].imag(((fftw_complex*)fOut)[i][1]);
270  }
271  DoFFT(func);
272 
273  // ..perform the deconvolution
274  double a, b, c, d, e;
275  for (int i = 0; i < fFreqSize; ++i) {
276  a = ((fftw_complex*)fOut)[i][0];
277  b = ((fftw_complex*)fOut)[i][1];
278  c = fKern[i].real();
279  d = fKern[i].imag();
280  e = 1. / (c * c + d * d);
281  ((fftw_complex*)rIn)[i][0] = (a * c + b * d) * e;
282  ((fftw_complex*)rIn)[i][1] = (b * c - a * d) * e;
283  }
284 
285  DoInvFFT(func);
286 }
287 
288 // -----------------------------------------------------------------------------
289 // ~~~~ Do Deconvolution: using transformed response function
290 // -----------------------------------------------------------------------------
291 template <class T>
292 inline void util::LArFFTW::Correlate(std::vector<T>& func, const ComplexVector& kern)
293 {
294 
295  // ... Make sure that time series and kernel have the correct size.
296  int n = func.size();
297  if (n != fSize) { throw cet::exception("LArFFTW") << "Bad time series size = " << n << "\n"; }
298  n = kern.size();
299  if (n != fFreqSize) { throw cet::exception("LArFFTW") << "Bad kernel size = " << n << "\n"; }
300 
301  DoFFT(func);
302 
303  // ..perform the correlation
304  for (int i = 0; i < fFreqSize; ++i) {
305  double re = ((fftw_complex*)fOut)[i][0];
306  double im = ((fftw_complex*)fOut)[i][1];
307  ((fftw_complex*)rIn)[i][0] = re * kern[i].real() + im * kern[i].imag();
308  ((fftw_complex*)rIn)[i][1] = -re * kern[i].imag() + im * kern[i].real();
309  }
310 
311  DoInvFFT(func);
312 }
313 
314 // -----------------------------------------------------------------------------
315 // ~~~~ Do Correlation: using all time domain information
316 // -----------------------------------------------------------------------------
317 template <class T>
318 inline void util::LArFFTW::Correlate(std::vector<T>& func1, std::vector<T>& func2)
319 {
320 
321  // ... Make sure that time series has the correct size.
322  int n = func1.size();
323  if (n != fSize) { throw cet::exception("LArFFTW") << "Bad 1st time series size = " << n << "\n"; }
324  n = func2.size();
325  if (n != fSize) { throw cet::exception("LArFFTW") << "Bad 2nd time series size = " << n << "\n"; }
326 
327  DoFFT(func2);
328  for (int i = 0; i < fFreqSize; ++i) {
329  fKern[i].real(((fftw_complex*)fOut)[i][0]);
330  fKern[i].imag(((fftw_complex*)fOut)[i][1]);
331  }
332  DoFFT(func1);
333 
334  // ..perform the correlation
335  for (int i = 0; i < fFreqSize; ++i) {
336  double re = ((fftw_complex*)fOut)[i][0];
337  double im = ((fftw_complex*)fOut)[i][1];
338  ((fftw_complex*)rIn)[i][0] = re * fKern[i].real() + im * fKern[i].imag();
339  ((fftw_complex*)rIn)[i][1] = -re * fKern[i].imag() + im * fKern[i].real();
340  }
341 
342  DoInvFFT(func1);
343 }
344 
345 // -----------------------------------------------------------------------------
346 // ~~~~ Shifts real vectors using above ShiftData function
347 // -----------------------------------------------------------------------------
348 template <class T>
349 inline void util::LArFFTW::ShiftData(std::vector<T>& input, double shift)
350 {
351  DoFFT(input, fCompTemp);
352  ShiftData(fCompTemp, shift);
353  DoInvFFT(fCompTemp, input);
354 
355  return;
356 }
357 
358 // -----------------------------------------------------------------------------
359 // ~~~~ Scheme for adding two signals which have an arbitrary relative
360 // translation. Shape1 is translated over shape2 and is replaced with the
361 // sum, or the translated result if add = false
362 // -----------------------------------------------------------------------------
363 template <class T>
364 inline void util::LArFFTW::AlignedSum(std::vector<T>& shape1, std::vector<T>& shape2, bool add)
365 {
366  double shift = PeakCorrelation(shape1, shape2);
367 
368  ShiftData(shape1, shift);
369 
370  if (add)
371  for (int i = 0; i < fSize; i++)
372  shape1[i] += shape2[i];
373 
374  return;
375 }
376 
377 // -----------------------------------------------------------------------------
378 // ~~~~ Returns the length of the translation at which the correlation
379 // of 2 signals is maximal.
380 // -----------------------------------------------------------------------------
381 template <class T>
382 inline T util::LArFFTW::PeakCorrelation(std::vector<T>& shape1, std::vector<T>& shape2)
383 {
384  float chiSqr = std::numeric_limits<float>::max();
385  float dchiSqr = std::numeric_limits<float>::max();
386  const float chiCut = 1e-3;
387  float lambda = 0.001; // Marquardt damping parameter
388  std::vector<float> p;
389 
390  std::vector<T> holder = shape1;
391  Correlate(holder, shape2);
392 
393  int maxT = max_element(holder.begin(), holder.end()) - holder.begin();
394  float startT = maxT - fFitBins / 2;
395  int offset = 0;
396 
397  for (int i = 0; i < fFitBins; i++) {
398  if (startT + i < 0)
399  offset = fSize;
400  else if (startT + i > fSize)
401  offset = -fSize;
402  else
403  offset = 0;
404  if (holder[i + startT + offset] <= 0.) { fConvHist[i] = 0.; }
405  else {
406  fConvHist[i] = holder[i + startT + offset];
407  }
408  }
409 
410  p[0] = *max_element(fConvHist.begin(), fConvHist.end());
411  p[1] = fFitBins / 2;
412  p[2] = fFitBins / 2;
413  float p1 = p[1]; // save initial p[1] guess
414 
415  int fitResult{-1};
416  int trial = 0;
417  lambda = -1.; // initialize lambda on first call
418  do {
419  fitResult = fMarqFitAlg->mrqdtfit(lambda, &p[0], &fConvHist[0], 3, fFitBins, chiSqr, dchiSqr);
420  trial++;
421  if (fitResult) {
422  mf::LogWarning("LArFFTW") << "Peak Correlation Fitting failed";
423  break;
424  }
425  else if (trial > 100) {
426  break;
427  }
428  } while (fabs(dchiSqr) >= chiCut);
429  if (!fitResult) p1 = p[1]; // if fit succeeded, use fit result
430 
431  return p1 + 0.5 + startT;
432 }
433 #endif
ComplexVector fKern
Definition: LArFFTW.h:65
std::vector< double > DoubleVector
Definition: LArFFTW.h:22
Namespace for general, non-LArSoft-specific utilities.
Definition: PIDAAlg.h:26
void * fIn
Definition: LArFFTW.h:70
int fFreqSize
Definition: LArFFTW.h:69
const void * fPlan
Definition: LArFFTW.h:72
std::vector< std::complex< double >> ComplexVector
Definition: LArFFTW.h:23
LArFFTW(int transformSize, const void *fplan, const void *rplan, int fitbins)
Definition: LArFFTW.cxx:5
void * rOut
Definition: LArFFTW.h:74
int func2(int i)
Definition: XFunc.cc:50
T PeakCorrelation(std::vector< T > &shape1, std::vector< T > &shape2)
Definition: LArFFTW.h:382
int mrqdtfit(float &lambda, float p[], float y[], const int nParam, const int nData, float &chiSqr, float &dchiSqr)
Definition: MarqFitAlg.cxx:294
void AlignedSum(std::vector< T > &input, std::vector< T > &output, bool add=true)
Definition: LArFFTW.h:364
std::vector< float > fConvHist
Definition: LArFFTW.h:67
void Deconvolute(std::vector< T > &func, const ComplexVector &kern)
Definition: LArFFTW.h:227
int fFitBins
Definition: LArFFTW.h:76
auto array(Array const &a)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:250
void * rIn
Definition: LArFFTW.h:73
void ShiftData(ComplexVector &input, double shift)
Definition: LArFFTW.cxx:43
Float_t d
Definition: plot.C:235
void Convolute(std::vector< T > &func, const ComplexVector &kern)
Definition: LArFFTW.h:170
int func1(int i)
Definition: XFunc.cc:39
const void * rPlan
Definition: LArFFTW.h:75
void * fOut
Definition: LArFFTW.h:71
void Correlate(std::vector< T > &func, const ComplexVector &kern)
Definition: LArFFTW.h:292
ComplexVector fCompTemp
Definition: LArFFTW.h:66
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
gshf::MarqFitAlg * fMarqFitAlg
Definition: LArFFTW.h:78
std::vector< float > FloatVector
Definition: LArFFTW.h:21
Char_t n[5]
void DoInvFFT(std::vector< T > &output)
Definition: LArFFTW.h:126
Float_t e
Definition: plot.C:35
void DoFFT(std::vector< T > &input)
Definition: LArFFTW.h:87
cet::coded_exception< error, detail::translate > exception
Definition: exception.h:33