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,28 +127,15 @@ private:
127 127
128 // Kludge 128 // Kludge
129 void clean_table(const int& threshold); 129 void clean_table(const int& threshold);
130 - GFft fft_smooth_kernel(const int& nbins,  
131 - const double& sigma) const;  
132 void clip_table(const double& threshold); 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 GNdarray gaussian_array(const double& mean, const double& rms) const; 132 GNdarray gaussian_array(const double& mean, const double& rms) const;
137 GNdarray get_gaussian_pars(const double& sigma) const; 133 GNdarray get_gaussian_pars(const double& sigma) const;
  134 + GFft fft_smooth_kernel(const int& nbins,
  135 + const double& sigma) const;
138 void get_mean_rms(const GNdarray& array, double* mean, double* rms) const; 136 void get_mean_rms(const GNdarray& array, double* mean, double* rms) const;
139 double get_single_event_value(const GNdarray& array) const; 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 // Protected classes 139 // Protected classes
153 class edisp_kern : public GFunction { 140 class edisp_kern : public GFunction {
154 public: 141 public:
inst/cta/src/GCTAEdisp2D.cpp
@@ -53,7 +53,7 @@ @@ -53,7 +53,7 @@
53 53
54 /* __ Coding definitions _________________________________________________ */ 54 /* __ Coding definitions _________________________________________________ */
55 #define G_SMOOTH_EDISP_KLUDGE //!< Get rid of noise in energy dispersion 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 /* __ Debug definitions __________________________________________________ */ 58 /* __ Debug definitions __________________________________________________ */
59 59
@@ -1200,15 +1200,9 @@ void GCTAEdisp2D::set_table(void) @@ -1200,15 +1200,9 @@ void GCTAEdisp2D::set_table(void)
1200 1200
1201 // Smooth energy dispersion table 1201 // Smooth energy dispersion table
1202 #if defined(G_SMOOTH_EDISP_KLUDGE) 1202 #if defined(G_SMOOTH_EDISP_KLUDGE)
1203 - denoise_table(); 1203 + denoise_table(10.0);
1204 clean_table(15); 1204 clean_table(15);
1205 clip_table(0.001); 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 #endif 1206 #endif
1213 1207
1214 // Normalize energy dispersion table 1208 // Normalize energy dispersion table
@@ -1369,11 +1363,11 @@ void GCTAEdisp2D::normalize_table(void) @@ -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 * @param[in] threshold Minimum number of consecutive non-zeros 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 * For each offset angle and true energy, the method determines the maximum 1372 * For each offset angle and true energy, the method determines the maximum
1379 * value of the energy dispersion table and then walks towards smaller and 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,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 * @brief Clip energy dispersion table 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 * Clips all migration vectors at a threshold that is a fraction of the 1486 * Clips all migration vectors at a threshold that is a fraction of the
1766 * maximum value in the migration vector. 1487 * maximum value in the migration vector.
@@ -1808,58 +1529,19 @@ void GCTAEdisp2D::clip_table(const double&amp; threshold) @@ -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 * @brief Denoise energy dispersion table 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 * True energy values with no energy dispersion information are interpolated 1541 * True energy values with no energy dispersion information are interpolated
1860 * from neighbouring energy dispersion values. 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 // Log entrance 1546 // Log entrance
1865 //std::cout << "GCTAEdisp2D::denoise_table in" << std::endl; 1547 //std::cout << "GCTAEdisp2D::denoise_table in" << std::endl;
@@ -1871,7 +1553,7 @@ void GCTAEdisp2D::denoise_table(void) @@ -1871,7 +1553,7 @@ void GCTAEdisp2D::denoise_table(void)
1871 int npix = netrue * nmigra; 1553 int npix = netrue * nmigra;
1872 1554
1873 // Get Gaussian parameters for all offset angles and true energies 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 // Loop over all offset angles 1558 // Loop over all offset angles
1877 for (int itheta = 0; itheta < ntheta; ++itheta) { 1559 for (int itheta = 0; itheta < ntheta; ++itheta) {
@@ -1974,119 +1656,6 @@ void GCTAEdisp2D::denoise_table(void) @@ -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 * @brief Return Gaussian approximation of energy dispersion array 1659 * @brief Return Gaussian approximation of energy dispersion array
2091 * 1660 *
2092 * @param[in] mean Gaussian mean. 1661 * @param[in] mean Gaussian mean.
@@ -2105,13 +1674,6 @@ GNdarray GCTAEdisp2D::gaussian_array(const double&amp; mean, const double&amp; rms) cons @@ -2105,13 +1674,6 @@ GNdarray GCTAEdisp2D::gaussian_array(const double&amp; mean, const double&amp; rms) cons
2105 int nmigra = m_edisp.axis_bins(m_inx_migra); 1674 int nmigra = m_edisp.axis_bins(m_inx_migra);
2106 GNdarray gaussian(nmigra); 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 // If the array contains information then compute a Gaussian 1677 // If the array contains information then compute a Gaussian
2116 // approximation 1678 // approximation
2117 if (rms > 0.0) { 1679 if (rms > 0.0) {
@@ -2257,6 +1819,48 @@ GNdarray GCTAEdisp2D::get_gaussian_pars(const double&amp; sigma) const @@ -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 * @brief Compute mean and root mean square of migration array 1864 * @brief Compute mean and root mean square of migration array
2261 * 1865 *
2262 * @param[in] array Energy dispersion array. 1866 * @param[in] array Energy dispersion array.