|
| 1 | +/** |
| 2 | + This provides std::vector and Eigen::Array typed interface to an |
| 3 | + IDFT. |
| 4 | + */ |
| 5 | + |
| 6 | +#ifndef WIRECELL_AUX_DFTTOOLS |
| 7 | +#define WIRECELL_AUX_DFTTOOLS |
| 8 | + |
| 9 | +#include "WireCellIface/IDFT.h" |
| 10 | +#include <vector> |
| 11 | +#include <Eigen/Core> |
| 12 | + |
| 13 | +namespace WireCell::Aux { |
| 14 | + |
| 15 | + using complex_t = IDFT::complex_t; |
| 16 | + |
| 17 | + // std::vector based functions |
| 18 | + |
| 19 | + using real_vector_t = std::vector<float>; |
| 20 | + using complex_vector_t = std::vector<complex_t>; |
| 21 | + |
| 22 | + // 1D with vectors |
| 23 | + |
| 24 | + // Perform forward c2c transform on vector. |
| 25 | + inline complex_vector_t fwd(const IDFT::pointer& dft, const complex_vector_t& seq) |
| 26 | + { |
| 27 | + complex_vector_t ret(seq.size()); |
| 28 | + dft->fwd1d(seq.data(), ret.data(), ret.size()); |
| 29 | + return ret; |
| 30 | + } |
| 31 | + |
| 32 | + // Perform forward r2c transform on vector. |
| 33 | + inline complex_vector_t fwd_r2c(const IDFT::pointer& dft, const real_vector_t& vec) |
| 34 | + { |
| 35 | + complex_vector_t cvec(vec.size()); |
| 36 | + std::transform(vec.begin(), vec.end(), cvec.begin(), |
| 37 | + [](float re) { return Aux::complex_t(re,0.0); } ); |
| 38 | + return fwd(dft, cvec); |
| 39 | + } |
| 40 | + |
| 41 | + // Perform inverse c2c transform on vector. |
| 42 | + inline complex_vector_t inv(const IDFT::pointer& dft, const complex_vector_t& spec) |
| 43 | + { |
| 44 | + complex_vector_t ret(spec.size()); |
| 45 | + dft->inv1d(spec.data(), ret.data(), ret.size()); |
| 46 | + return ret; |
| 47 | + } |
| 48 | + |
| 49 | + // Perform inverse c2r transform on vector. |
| 50 | + inline real_vector_t inv_c2r(const IDFT::pointer& dft, const complex_vector_t& spec) |
| 51 | + { |
| 52 | + auto cvec = inv(dft, spec); |
| 53 | + real_vector_t rvec(cvec.size()); |
| 54 | + std::transform(cvec.begin(), cvec.end(), rvec.begin(), |
| 55 | + [](const Aux::complex_t& c) { return std::real(c); }); |
| 56 | + return rvec; |
| 57 | + } |
| 58 | + |
| 59 | + // 1D high-level interface |
| 60 | + |
| 61 | + /// Convovle in1 and in2. Returned vecgtor has size sum of sizes |
| 62 | + /// of in1 and in2 less one element in order to assure no periodic |
| 63 | + /// aliasing. Caller need not (should not) pad either input. |
| 64 | + /// Caller is free to truncate result as required. |
| 65 | + real_vector_t convolve(const IDFT::pointer& dft, |
| 66 | + const real_vector_t& in1, |
| 67 | + const real_vector_t& in2); |
| 68 | + |
| 69 | + |
| 70 | + /// Replace response res1 in meas with response res2. |
| 71 | + /// |
| 72 | + /// This will compute the FFT of all three, in frequency space will form: |
| 73 | + /// |
| 74 | + /// meas * resp2 / resp1 |
| 75 | + /// |
| 76 | + /// apply the inverse FFT and return its real part. |
| 77 | + /// |
| 78 | + /// The output vector is long enough to assure no periodic |
| 79 | + /// aliasing. In general, caller should NOT pre-pad any input. |
| 80 | + /// Any subsequent truncation of result is up to caller. |
| 81 | + real_vector_t replace(const IDFT::pointer& dft, |
| 82 | + const real_vector_t& meas, |
| 83 | + const real_vector_t& res1, |
| 84 | + const real_vector_t& res2); |
| 85 | + |
| 86 | + |
| 87 | + // Eigen array based functions |
| 88 | + |
| 89 | + /// 2D array types. Note, use Array::cast<complex_t>() if you |
| 90 | + /// need to convert rom real or arr.real() to convert to real. |
| 91 | + using real_array_t = Eigen::ArrayXXf; |
| 92 | + using complex_array_t = Eigen::ArrayXXcf; |
| 93 | + |
| 94 | + // 2D with Eigen arrays. Use eg arr.cast<complex_>() to provde |
| 95 | + // from real or arr.real()() to convert result to real. |
| 96 | + |
| 97 | + // Transform both dimesions. |
| 98 | + complex_array_t fwd(const IDFT::pointer& dft, const complex_array_t& arr); |
| 99 | + complex_array_t inv(const IDFT::pointer& dft, const complex_array_t& arr); |
| 100 | + |
| 101 | + // As above but internally convert input or output. These are |
| 102 | + // just syntactic sugar hiding a .cast<complex_t>() or a .real() |
| 103 | + // call. |
| 104 | + complex_array_t fwd_r2c(const IDFT::pointer& dft, const real_array_t& arr); |
| 105 | + real_array_t inv_c2r(const IDFT::pointer& dft, const complex_array_t& arr); |
| 106 | + |
| 107 | + // Transform a 2D array along one axis. |
| 108 | + // |
| 109 | + // The axis identifies the logical array "dimension" over which |
| 110 | + // the transform is applied. For example, axis=1 means the |
| 111 | + // transforms are applied along columns (ie, on a per-row basis). |
| 112 | + // Note: this is the same convention as held by numpy.fft. |
| 113 | + // |
| 114 | + // The axis is interpreted in the "logical" sense Eigen arrays |
| 115 | + // indexed as array(irow, icol). Ie, the dimension traversing |
| 116 | + // rows is axis 0 and the dimension traversing columns is axis 1. |
| 117 | + // Note: internal storage order of an Eigen array may differ from |
| 118 | + // the logical order and indeed that of the array template type |
| 119 | + // order. Neither is pertinent in setting the axis. |
| 120 | + complex_array_t fwd(const IDFT::pointer& dft, const complex_array_t& arr, int axis); |
| 121 | + complex_array_t inv(const IDFT::pointer& dft, const complex_array_t& arr, int axis); |
| 122 | + |
| 123 | + |
| 124 | + // Fixme: possible additions |
| 125 | + // - superposition of 2 reals for 2x speedup |
| 126 | + // - r2c / c2r for 1b |
| 127 | + |
| 128 | +} |
| 129 | + |
| 130 | +#endif |
0 commit comments