Commit 2b15414b0e064f68e1e769ebe916db5ea0c506e6

Authored by Jürgen Knödlseder
1 parent 3b8c6607

Implement energy dispersion replacement by Gaussian in GCTAEdisp2D (#2403)

inst/cta/include/GCTAEdisp2D.hpp
... ... @@ -127,28 +127,15 @@ private:
127 127  
128 128 // Kludge
129 129 void clean_table(const int& threshold);
130   - GFft fft_smooth_kernel(const int& nbins,
131   - const double& sigma) const;
132 130 void clip_table(const double& threshold);
133   - void denoise_table(void);
134   - GNdarray smooth_array(const GNdarray& array, const double& mean,
135   - const double& rms) const;
  131 + void denoise_table(const double& sigma);
136 132 GNdarray gaussian_array(const double& mean, const double& rms) const;
137 133 GNdarray get_gaussian_pars(const double& sigma) const;
  134 + GFft fft_smooth_kernel(const int& nbins,
  135 + const double& sigma) const;
138 136 void get_mean_rms(const GNdarray& array, double* mean, double* rms) const;
139 137 double get_single_event_value(const GNdarray& array) const;
140 138  
141   - // Obsolete kludge
142   - void smooth_table(const double& sigma);
143   - void smooth_table2(const double& sigma_etrue, const double& sigma_migra);
144   - GFft fft_smooth_kernel(const int& netrue,
145   - const int& nmigra,
146   - const double& sigma) const;
147   - GFft fft_smooth_kernel2(const int& netrue,
148   - const int& nmigra,
149   - const double& sigma_etrue,
150   - const double& sigma_migra) const;
151   -
152 139 // Protected classes
153 140 class edisp_kern : public GFunction {
154 141 public:
... ...
inst/cta/src/GCTAEdisp2D.cpp
... ... @@ -53,7 +53,7 @@
53 53  
54 54 /* __ Coding definitions _________________________________________________ */
55 55 #define G_SMOOTH_EDISP_KLUDGE //!< Get rid of noise in energy dispersion
56   -#define G_SMOOTH_EDISP_SAVE_TEST //!< Save test matrix
  56 +//#define G_SMOOTH_EDISP_SAVE_TEST //!< Save test matrix
57 57  
58 58 /* __ Debug definitions __________________________________________________ */
59 59  
... ... @@ -1200,15 +1200,9 @@ void GCTAEdisp2D::set_table(void)
1200 1200  
1201 1201 // Smooth energy dispersion table
1202 1202 #if defined(G_SMOOTH_EDISP_KLUDGE)
1203   - denoise_table();
  1203 + denoise_table(10.0);
1204 1204 clean_table(15);
1205 1205 clip_table(0.001);
1206   - /*
1207   - denoise_table();
1208   - clean_table(20);
1209   - smooth_table(5.0);
1210   - clip_table(0.01);
1211   - */
1212 1206 #endif
1213 1207  
1214 1208 // Normalize energy dispersion table
... ... @@ -1369,11 +1363,11 @@ void GCTAEdisp2D::normalize_table(void)
1369 1363  
1370 1364  
1371 1365 /***********************************************************************//**
1372   - * @brief Clip energy dispersion table
  1366 + * @brief Clean energy dispersion table
1373 1367 *
1374 1368 * @param[in] threshold Minimum number of consecutive non-zeros
1375 1369 *
1376   - * Clips noise in the energy dispersion table.
  1370 + * Removes noise from the energy dispersion table.
1377 1371 *
1378 1372 * For each offset angle and true energy, the method determines the maximum
1379 1373 * value of the energy dispersion table and then walks towards smaller and
... ... @@ -1485,282 +1479,9 @@ void GCTAEdisp2D::clean_table(const int&amp; threshold)
1485 1479  
1486 1480  
1487 1481 /***********************************************************************//**
1488   - * @brief Smooth energy dispersion table
1489   - *
1490   - * @param[in] sigma Smoothing width (number of bins in true energy)
1491   - *
1492   - * Smooth the energy dispersion table in true energy. The smoothing is done
1493   - * using a fast-fourrier transform. For this purpose the energy disperison
1494   - * table for each offset angle is copied into a 2D array, padded with some
1495   - * zero pixels on the left and the right to avoid wrap around.
1496   - ***************************************************************************/
1497   -void GCTAEdisp2D::smooth_table(const double& sigma)
1498   -{
1499   - // Get axes dimensions
1500   - int netrue = m_edisp.axis_bins(m_inx_etrue);
1501   - int nmigra = m_edisp.axis_bins(m_inx_migra);
1502   - int ntheta = m_edisp.axis_bins(m_inx_theta);
1503   - int netrue_pad = int(3.0*sigma);
1504   - int netrue_padded = netrue + 2*netrue_pad; // Array padded with zeros
1505   - int npix = netrue * nmigra;
1506   -
1507   - // Get smoothing kernel
1508   - GFft fft_kernel = fft_smooth_kernel(netrue_padded, nmigra, sigma);
1509   -
1510   - // Loop over all offset angles
1511   - for (int itheta = 0; itheta < ntheta; ++itheta) {
1512   -
1513   - // Compute base index
1514   - int ibase = itheta * npix;
1515   -
1516   - // Extract matrix in GNdarray
1517   - GNdarray array(netrue_padded, nmigra);
1518   - for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra) {
1519   - double* ptr = array.data() + imigra * netrue_padded + netrue_pad;
1520   - for (int ietrue = 0; ietrue < netrue; ++ietrue, ++i) {
1521   - *ptr++ = m_edisp(m_inx_matrix, i);
1522   - }
1523   - }
1524   -
1525   - // FFT of array
1526   - GFft fft_array(array);
1527   -
1528   - // Smooth array
1529   - GFft fft_smooth = fft_array * fft_kernel;
1530   -
1531   - // Backward transform array
1532   - GNdarray smooth = fft_smooth.backward();
1533   -
1534   - // Put back array values in matrix
1535   - for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra) {
1536   - double* ptr = smooth.data() + imigra * netrue_padded + netrue_pad;
1537   - for (int ietrue = 0; ietrue < netrue; ++ietrue, ++i) {
1538   - m_edisp(m_inx_matrix, i) = *ptr++;
1539   - }
1540   - }
1541   -
1542   - } // endfor: looped over all offset angles
1543   -
1544   - // Return
1545   - return;
1546   -}
1547   -
1548   -
1549   -/***********************************************************************//**
1550   - * @brief Smooth energy dispersion table
1551   - *
1552   - * @param[in] sigma Smoothing width (number of bins in true energy)
1553   - *
1554   - * Smooth the energy dispersion table in true energy. The smoothing is done
1555   - * using a fast-fourrier transform. For this purpose the energy disperison
1556   - * table for each offset angle is copied into a 2D array, padded with some
1557   - * zero pixels on the left and the right to avoid wrap around.
1558   - ***************************************************************************/
1559   -void GCTAEdisp2D::smooth_table2(const double& sigma_etrue,
1560   - const double& sigma_migra)
1561   -{
1562   - // Get axes dimensions
1563   - int netrue = m_edisp.axis_bins(m_inx_etrue);
1564   - int nmigra = m_edisp.axis_bins(m_inx_migra);
1565   - int ntheta = m_edisp.axis_bins(m_inx_theta);
1566   - int netrue_pad = int(3.0*sigma_etrue);
1567   - int netrue_padded = netrue + 2*netrue_pad; // Array padded with zeros
1568   - int npix = netrue * nmigra;
1569   -
1570   - // Get smoothing kernel
1571   - GFft fft_kernel = fft_smooth_kernel2(netrue_padded, nmigra, sigma_etrue, sigma_migra);
1572   -
1573   - // Loop over all offset angles
1574   - for (int itheta = 0; itheta < ntheta; ++itheta) {
1575   -
1576   - // Compute base index
1577   - int ibase = itheta * npix;
1578   -
1579   - // Extract matrix in GNdarray
1580   - GNdarray array(netrue_padded, nmigra);
1581   - for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra) {
1582   - double* ptr = array.data() + imigra * netrue_padded + netrue_pad;
1583   - for (int ietrue = 0; ietrue < netrue; ++ietrue, ++i) {
1584   - *ptr++ = m_edisp(m_inx_matrix, i);
1585   - }
1586   - }
1587   -
1588   - // FFT of array
1589   - GFft fft_array(array);
1590   -
1591   - // Smooth array
1592   - GFft fft_smooth = fft_array * fft_kernel;
1593   -
1594   - // Backward transform array
1595   - GNdarray smooth = fft_smooth.backward();
1596   -
1597   - // Put back array values in matrix
1598   - for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra) {
1599   - double* ptr = smooth.data() + imigra * netrue_padded + netrue_pad;
1600   - for (int ietrue = 0; ietrue < netrue; ++ietrue, ++i) {
1601   - m_edisp(m_inx_matrix, i) = *ptr++;
1602   - }
1603   - }
1604   -
1605   - } // endfor: looped over all offset angles
1606   -
1607   - // Return
1608   - return;
1609   -}
1610   -
1611   -
1612   -/***********************************************************************//**
1613   - * @brief Get FFT of smoothing kernel
1614   - *
1615   - * @param[in] nbins Number of kernel bins.
1616   - * @param[in] sigma Gaussian kernel sigma in bins.
1617   - * @return FFT of smoothing kernel
1618   - *
1619   - * Returns the fast-fourrier transform of a Gaussian smoothing kernel.
1620   - ***************************************************************************/
1621   -GFft GCTAEdisp2D::fft_smooth_kernel(const int& nbins,
1622   - const double& sigma) const
1623   -{
1624   - // Allocate kernel
1625   - GNdarray kernel(nbins);
1626   -
1627   - // Initialise sum and compute Gaussian normalisation
1628   - double sum = 0.0;
1629   - double norm = -0.5 / (sigma * sigma);
1630   -
1631   - // Set Gaussian kernel
1632   - for (int i = 0; i < nbins; ++i) {
1633   - double value = std::exp(norm*double(i*i));
1634   - kernel(i) += value;
1635   - sum += value;
1636   - if (i > 0) {
1637   - kernel(nbins-i) += value;
1638   - sum += value;
1639   - }
1640   - }
1641   -
1642   - // Normalize kernel
1643   - if (sum > 0.0) {
1644   - for (int i = 0; i < nbins; ++i) {
1645   - kernel(i) /= sum;
1646   - }
1647   - }
1648   -
1649   - // Return FFT of kernel
1650   - return (GFft(kernel));
1651   -}
1652   -
1653   -
1654   -/***********************************************************************//**
1655   - * @brief Get FFT of smoothing kernel for true energy smoothing
1656   - *
1657   - * @param[in] netrue Number of true energy bins.
1658   - * @param[in] nmigra Number of migration bins bins.
1659   - * @param[in] sigma Gaussian kernel sigma in true energy direction.
1660   - * @return FFT of smoothing kernel
1661   - *
1662   - * Returns the fast-fourrier transform of a Gaussian smoothing kernel in
1663   - * true energy.
1664   - ***************************************************************************/
1665   -GFft GCTAEdisp2D::fft_smooth_kernel(const int& netrue,
1666   - const int& nmigra,
1667   - const double& sigma) const
1668   -{
1669   - // Allocate kernel
1670   - GNdarray kernel(netrue, nmigra);
1671   -
1672   - // Initialise sum and compute Gaussian normalisation
1673   - double sum = 0.0;
1674   - double norm = -0.5 / (sigma * sigma);
1675   -
1676   - // Set Gaussian kernel
1677   - for (int i = 0; i < netrue; ++i) {
1678   - double value = std::exp(norm*double(i*i));
1679   - kernel(i,0) += value;
1680   - sum += value;
1681   - if (i > 0) {
1682   - kernel(netrue-i,0) += value;
1683   - sum += value;
1684   - }
1685   - }
1686   -
1687   - // Normalize kernel
1688   - if (sum > 0.0) {
1689   - for (int i = 0; i < netrue; ++i) {
1690   - kernel(i,0) /= sum;
1691   - }
1692   - }
1693   -
1694   - // Return FFT of kernel
1695   - return (GFft(kernel));
1696   -}
1697   -
1698   -
1699   -/***********************************************************************//**
1700   - * @brief Get FFT of smoothing kernel for 2D smoothing
1701   - *
1702   - * @param[in] netrue Number of true energy bins.
1703   - * @param[in] nmigra Number of migration bins bins.
1704   - * @param[in] sigma_etrue Gaussian kernel sigma in true energy direction.
1705   - * @param[in] sigma_migra Gaussian kernel sigma in migration direction.
1706   - * @return FFT of smoothing kernel
1707   - *
1708   - * Returns the fast-fourrier transform of a Gaussian smoothing kernel in
1709   - * true energy and migration.
1710   - ***************************************************************************/
1711   -GFft GCTAEdisp2D::fft_smooth_kernel2(const int& netrue,
1712   - const int& nmigra,
1713   - const double& sigma_etrue,
1714   - const double& sigma_migra) const
1715   -{
1716   - // Allocate kernel
1717   - GNdarray kernel(netrue, nmigra);
1718   -
1719   - // Initialise sum and compute Gaussian normalisation
1720   - double sum = 0.0;
1721   - double norm_etrue = -0.5 / (sigma_etrue * sigma_etrue);
1722   - double norm_migra = -0.5 / (sigma_migra * sigma_migra);
1723   -
1724   - // Set Gaussian kernel
1725   - for (int itrue = 0; itrue < netrue; ++itrue) {
1726   - double value_true = std::exp(norm_etrue*double(itrue*itrue));
1727   - for (int imigra = 0; imigra < nmigra; ++imigra) {
1728   - double value = std::exp(norm_migra*double(imigra*imigra)) * value_true;
1729   - kernel(itrue,imigra) += value;
1730   - sum += value;
1731   - if (itrue > 0) {
1732   - kernel(netrue-itrue,imigra) += value;
1733   - sum += value;
1734   - }
1735   - if (imigra > 0) {
1736   - kernel(itrue,nmigra-imigra) += value;
1737   - sum += value;
1738   - }
1739   - if ((itrue > 0) && (imigra > 0)) {
1740   - kernel(netrue-itrue,nmigra-imigra) += value;
1741   - sum += value;
1742   - }
1743   - }
1744   - }
1745   -
1746   - // Normalize kernel
1747   - if (sum > 0.0) {
1748   - for (int itrue = 0; itrue < netrue; ++itrue) {
1749   - for (int imigra = 0; imigra < nmigra; ++imigra) {
1750   - kernel(itrue,imigra) /= sum;
1751   - }
1752   - }
1753   - }
1754   -
1755   - // Return FFT of kernel
1756   - return (GFft(kernel));
1757   -}
1758   -
1759   -
1760   -/***********************************************************************//**
1761 1482 * @brief Clip energy dispersion table
1762 1483 *
1763   - * @param[in] threshold Clipping threshold in fraction of maximum value.
  1484 + * @param[in] threshold Clipping threshold as fraction of maximum value.
1764 1485 *
1765 1486 * Clips all migration vectors at a threshold that is a fraction of the
1766 1487 * maximum value in the migration vector.
... ... @@ -1808,58 +1529,19 @@ void GCTAEdisp2D::clip_table(const double&amp; threshold)
1808 1529  
1809 1530  
1810 1531 /***********************************************************************//**
1811   - * @brief Clip array
1812   - *
1813   - * @param[in] array Ndarray that should be clipped.
1814   - * @param[in] threshold Clipping threshold in fraction of maximum value.
1815   - * @return Clipped Ndarray
1816   - *
1817   - * Clips all values below a threshold to zero.
1818   - ***************************************************************************/
1819   -/*
1820   -GNdarray GCTAEdisp2D::clip_array(const GNdarray& array,
1821   - const double& threshold) const
1822   -{
1823   - // Set clipped array
1824   - GNdarray clipped = array;
1825   -
1826   - // Initialise variables
1827   - int nsize = clipped.size();
1828   -
1829   - // Determine maximum value in array
1830   - double max = 0.0;
1831   - double* cptr = clipped.data();
1832   - for (int i = 0; i < nsize; ++i, cptr++) {
1833   - if (*cptr > max) {
1834   - max = *cptr;
1835   - }
1836   - }
1837   -
1838   - // Clip values below threshold
1839   - double clip_value = max * threshold;
1840   - cptr = clipped.data();
1841   - for (int i = 0; i < nsize; ++i, cptr++) {
1842   - if (std::abs(*cptr) < clip_value) {
1843   - *cptr = 0.0;
1844   - }
1845   - }
1846   -
1847   - // Return clipped array
1848   - return clipped;
1849   -}
1850   -*/
1851   -
1852   -
1853   -/***********************************************************************//**
1854 1532 * @brief Denoise energy dispersion table
1855 1533 *
1856   - * Denoise the energy dispersion table by smoothing the energy dispersion
1857   - * for each offset angle and true energy using the smooth_array() method.
  1534 + * @param[in] sigma Gaussian smoothing single in true energy.
  1535 + *
  1536 + * Denoise the energy dispersion table by replacing the energy dispersion
  1537 + * for each offset angle and true energy by a Gaussian function. The mean
  1538 + * and rms of the Gaussian is smoothed as function of true energy using a
  1539 + * Gaussian smoothing kernel with a width of @p sigma pixels.
1858 1540 *
1859 1541 * True energy values with no energy dispersion information are interpolated
1860 1542 * from neighbouring energy dispersion values.
1861 1543 ***************************************************************************/
1862   -void GCTAEdisp2D::denoise_table(void)
  1544 +void GCTAEdisp2D::denoise_table(const double& sigma)
1863 1545 {
1864 1546 // Log entrance
1865 1547 //std::cout << "GCTAEdisp2D::denoise_table in" << std::endl;
... ... @@ -1871,7 +1553,7 @@ void GCTAEdisp2D::denoise_table(void)
1871 1553 int npix = netrue * nmigra;
1872 1554  
1873 1555 // Get Gaussian parameters for all offset angles and true energies
1874   - GNdarray gpars = get_gaussian_pars(5.0);
  1556 + GNdarray gpars = get_gaussian_pars(sigma);
1875 1557  
1876 1558 // Loop over all offset angles
1877 1559 for (int itheta = 0; itheta < ntheta; ++itheta) {
... ... @@ -1974,119 +1656,6 @@ void GCTAEdisp2D::denoise_table(void)
1974 1656  
1975 1657  
1976 1658 /***********************************************************************//**
1977   - * @brief Smooth array
1978   - *
1979   - * @param[in] array Energy dispersion array.
1980   - * @param[in] mean Mean of Gaussian.
1981   - * @param[in] rms Root mean square of Gaussian.
1982   - * @return Smoothed energy dispersion array.
1983   - ***************************************************************************/
1984   -GNdarray GCTAEdisp2D::smooth_array(const GNdarray& array,
1985   - const double& mean,
1986   - const double& rms) const
1987   -{
1988   - // Get reference to migration values
1989   - const GNodeArray& migras = m_edisp.axis_nodes(m_inx_migra);
1990   - int nmigra = migras.size();
1991   -
1992   - // Initialise empty smoothed array
1993   - GNdarray smoothed_array(nmigra);
1994   -
1995   - // Set required number of events and minimum and maximum sigma
1996   - double num_events = 200.0; // required number of events under Gaussian
1997   - double min_events = 2.0; // at least 2 events are required
1998   - double sigma_min = 1.0; // minimum sigma
1999   - double sigma_max = 0.1 * nmigra; // maximum sigma (< 20)
2000   - if (sigma_max > 20.0) {
2001   - sigma_max = 20.0;
2002   - }
2003   -
2004   - // Compute single event value
2005   - double event_value = get_single_event_value(array);
2006   -
2007   - // Continue only there are pixels with the requested minimum number of
2008   - // events (otherwise the returned vector will be zero)
2009   - if (max(array) >= min_events*event_value) {
2010   -
2011   - // Get Gaussian approximation
2012   - GNdarray gaussian = gaussian_array(mean, rms);
2013   -
2014   - // Get residual by subtracting Gaussian from array
2015   - GNdarray residual = array - gaussian;
2016   -
2017   - // Initialise smoothed residual
2018   - GNdarray smoothed_residual(nmigra);
2019   -
2020   - // Loop over all migration pixels
2021   - for (int imigra = 0; imigra < nmigra; ++imigra) {
2022   -
2023   - // Get pixel value
2024   - double value = array(imigra);
2025   -
2026   - // Fall through if pixel value is empty
2027   - if (value == 0.0) {
2028   - continue;
2029   - }
2030   -
2031   - // Set Gaussian sigma. Make sure that sigma is within the allowed
2032   - // range
2033   - double sigma = num_events * event_value / value;
2034   - if (sigma < sigma_min) {
2035   - sigma = sigma_min;
2036   - }
2037   - else if (sigma > sigma_max) {
2038   - sigma = sigma_max;
2039   - }
2040   -
2041   - // Convolve residual with a Gaussian function
2042   - double total = 0.0;
2043   - double norm = -0.5 / (sigma * sigma);
2044   - GNdarray work(nmigra);
2045   - for (int k = 0; k < nmigra; ++k) {
2046   - double weight = std::exp(norm*k*k);
2047   - if (weight > 1.0e-30) {
2048   - if (k == 0) {
2049   - work(imigra) = weight;
2050   - total += weight;
2051   - }
2052   - else {
2053   - int ileft = imigra - k;
2054   - if (ileft >= 0) {
2055   - work(ileft) = weight;
2056   - total += weight;
2057   - }
2058   - int iright = imigra + k;
2059   - if (iright < nmigra) {
2060   - work(iright) = weight;
2061   - total += weight;
2062   - }
2063   - }
2064   - }
2065   - }
2066   - if (total > 0.0) {
2067   - smoothed_residual += work * residual(imigra) / total;
2068   - }
2069   -
2070   - } // endfor: looped over all migration pixels
2071   -
2072   - // Set smoothed array
2073   - smoothed_array = gaussian; // + smoothed_residual;
2074   -
2075   - // Make sure that smooth array is not negative
2076   - for (int imigra = 0; imigra < nmigra; ++imigra) {
2077   - if (smoothed_array(imigra) < 0.0) {
2078   - smoothed_array(imigra) = 0.0;
2079   - }
2080   - }
2081   -
2082   - } // endif: single event value was positive
2083   -
2084   - // Return smoothed array
2085   - return smoothed_array;
2086   -}
2087   -
2088   -
2089   -/***********************************************************************//**
2090 1659 * @brief Return Gaussian approximation of energy dispersion array
2091 1660 *
2092 1661 * @param[in] mean Gaussian mean.
... ... @@ -2105,13 +1674,6 @@ GNdarray GCTAEdisp2D::gaussian_array(const double&amp; mean, const double&amp; rms) cons
2105 1674 int nmigra = m_edisp.axis_bins(m_inx_migra);
2106 1675 GNdarray gaussian(nmigra);
2107 1676  
2108   - // Get mean and rms of migration values in array
2109   - /*
2110   - double mean = 0.0;
2111   - double rms = 0.0;
2112   - get_mean_rms(array, &mean, &rms);
2113   - */
2114   -
2115 1677 // If the array contains information then compute a Gaussian
2116 1678 // approximation
2117 1679 if (rms > 0.0) {
... ... @@ -2257,6 +1819,48 @@ GNdarray GCTAEdisp2D::get_gaussian_pars(const double&amp; sigma) const
2257 1819  
2258 1820  
2259 1821 /***********************************************************************//**
  1822 + * @brief Get FFT of smoothing kernel
  1823 + *
  1824 + * @param[in] nbins Number of kernel bins.
  1825 + * @param[in] sigma Gaussian kernel sigma in bins.
  1826 + * @return FFT of smoothing kernel
  1827 + *
  1828 + * Returns the fast-fourrier transform of a Gaussian smoothing kernel.
  1829 + ***************************************************************************/
  1830 +GFft GCTAEdisp2D::fft_smooth_kernel(const int& nbins,
  1831 + const double& sigma) const
  1832 +{
  1833 + // Allocate kernel
  1834 + GNdarray kernel(nbins);
  1835 +
  1836 + // Initialise sum and compute Gaussian normalisation
  1837 + double sum = 0.0;
  1838 + double norm = -0.5 / (sigma * sigma);
  1839 +
  1840 + // Set Gaussian kernel
  1841 + for (int i = 0; i < nbins; ++i) {
  1842 + double value = std::exp(norm*double(i*i));
  1843 + kernel(i) += value;
  1844 + sum += value;
  1845 + if (i > 0) {
  1846 + kernel(nbins-i) += value;
  1847 + sum += value;
  1848 + }
  1849 + }
  1850 +
  1851 + // Normalize kernel
  1852 + if (sum > 0.0) {
  1853 + for (int i = 0; i < nbins; ++i) {
  1854 + kernel(i) /= sum;
  1855 + }
  1856 + }
  1857 +
  1858 + // Return FFT of kernel
  1859 + return (GFft(kernel));
  1860 +}
  1861 +
  1862 +
  1863 +/***********************************************************************//**
2260 1864 * @brief Compute mean and root mean square of migration array
2261 1865 *
2262 1866 * @param[in] array Energy dispersion array.
... ...