Commit 3b8c660717a587f0ba1fee613053bdc382a41399
1 parent
19f117ae
Add more methods to test energy dispersion smoothing
Showing
2 changed files
with
758 additions
and
24 deletions
inst/cta/include/GCTAEdisp2D.hpp
| ... | ... | @@ -124,12 +124,30 @@ private: |
| 124 | 124 | void set_boundaries(void); |
| 125 | 125 | void set_max_edisp(void) const; |
| 126 | 126 | void normalize_table(void); |
| 127 | - void clip_table(const int& threshold); | |
| 128 | - void smooth_table(const double& sigma, const double& threshold); | |
| 129 | - GFft fft_smooth_kernel(const int& netrue, | |
| 130 | - const int& nmigra, | |
| 131 | - const double& sigma) const; | |
| 132 | - GNdarray clip_array(const GNdarray& array, const double& threshold) const; | |
| 127 | + | |
| 128 | + // Kludge | |
| 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); | |
| 133 | + void denoise_table(void); | |
| 134 | + GNdarray smooth_array(const GNdarray& array, const double& mean, | |
| 135 | + const double& rms) const; | |
| 136 | + GNdarray gaussian_array(const double& mean, const double& rms) const; | |
| 137 | + GNdarray get_gaussian_pars(const double& sigma) const; | |
| 138 | + void get_mean_rms(const GNdarray& array, double* mean, double* rms) const; | |
| 139 | + double get_single_event_value(const GNdarray& array) const; | |
| 140 | + | |
| 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; | |
| 133 | 151 | |
| 134 | 152 | // Protected classes |
| 135 | 153 | class edisp_kern : public GFunction { | ... | ... |
inst/cta/src/GCTAEdisp2D.cpp
| ... | ... | @@ -52,7 +52,8 @@ |
| 52 | 52 | /* __ Macros _____________________________________________________________ */ |
| 53 | 53 | |
| 54 | 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 | 57 | |
| 57 | 58 | /* __ Debug definitions __________________________________________________ */ |
| 58 | 59 | |
| ... | ... | @@ -1199,8 +1200,15 @@ void GCTAEdisp2D::set_table(void) |
| 1199 | 1200 | |
| 1200 | 1201 | // Smooth energy dispersion table |
| 1201 | 1202 | #if defined(G_SMOOTH_EDISP_KLUDGE) |
| 1202 | - clip_table(15); | |
| 1203 | - smooth_table(3.0, 0.0001); | |
| 1203 | + denoise_table(); | |
| 1204 | + clean_table(15); | |
| 1205 | + clip_table(0.001); | |
| 1206 | + /* | |
| 1207 | + denoise_table(); | |
| 1208 | + clean_table(20); | |
| 1209 | + smooth_table(5.0); | |
| 1210 | + clip_table(0.01); | |
| 1211 | + */ | |
| 1204 | 1212 | #endif |
| 1205 | 1213 | |
| 1206 | 1214 | // Normalize energy dispersion table |
| ... | ... | @@ -1209,6 +1217,11 @@ void GCTAEdisp2D::set_table(void) |
| 1209 | 1217 | // Set maximum energy dispersion value |
| 1210 | 1218 | set_max_edisp(); |
| 1211 | 1219 | |
| 1220 | + // Save test | |
| 1221 | + #if defined(G_SMOOTH_EDISP_SAVE_TEST) | |
| 1222 | + this->save("test_edisp.fits", true); | |
| 1223 | + #endif | |
| 1224 | + | |
| 1212 | 1225 | // Return |
| 1213 | 1226 | return; |
| 1214 | 1227 | } |
| ... | ... | @@ -1370,10 +1383,10 @@ void GCTAEdisp2D::normalize_table(void) |
| 1370 | 1383 | * |
| 1371 | 1384 | * The method then computes the number of remaining contiguos non-zero pixels |
| 1372 | 1385 | * for each offset angle and true energy, and if this number is smaller than |
| 1373 | - * 10, all pixels are set to zero. This removes true energy bins that are | |
| 1374 | - * sparesely filled. | |
| 1386 | + * a threshold, all pixels are set to zero. This removes true energy bins | |
| 1387 | + * that are sparesely filled. | |
| 1375 | 1388 | ***************************************************************************/ |
| 1376 | -void GCTAEdisp2D::clip_table(const int& threshold) | |
| 1389 | +void GCTAEdisp2D::clean_table(const int& threshold) | |
| 1377 | 1390 | { |
| 1378 | 1391 | // Get axes dimensions |
| 1379 | 1392 | int netrue = m_edisp.axis_bins(m_inx_etrue); |
| ... | ... | @@ -1475,16 +1488,13 @@ void GCTAEdisp2D::clip_table(const int& threshold) |
| 1475 | 1488 | * @brief Smooth energy dispersion table |
| 1476 | 1489 | * |
| 1477 | 1490 | * @param[in] sigma Smoothing width (number of bins in true energy) |
| 1478 | - * @param[in] threshold Clipping threshold | |
| 1479 | 1491 | * |
| 1480 | 1492 | * Smooth the energy dispersion table in true energy. The smoothing is done |
| 1481 | 1493 | * using a fast-fourrier transform. For this purpose the energy disperison |
| 1482 | 1494 | * table for each offset angle is copied into a 2D array, padded with some |
| 1483 | - * zero pixels on the left and the right to avoid wrap around. After | |
| 1484 | - * smoothing the array is clipped using the clip_array() method to avoid | |
| 1485 | - * marginal non-zero energy dispersion values. | |
| 1495 | + * zero pixels on the left and the right to avoid wrap around. | |
| 1486 | 1496 | ***************************************************************************/ |
| 1487 | -void GCTAEdisp2D::smooth_table(const double& sigma, const double& threshold) | |
| 1497 | +void GCTAEdisp2D::smooth_table(const double& sigma) | |
| 1488 | 1498 | { |
| 1489 | 1499 | // Get axes dimensions |
| 1490 | 1500 | int netrue = m_edisp.axis_bins(m_inx_etrue); |
| ... | ... | @@ -1505,10 +1515,10 @@ void GCTAEdisp2D::smooth_table(const double& sigma, const double& threshold) |
| 1505 | 1515 | |
| 1506 | 1516 | // Extract matrix in GNdarray |
| 1507 | 1517 | GNdarray array(netrue_padded, nmigra); |
| 1508 | - for (int imigra = 0, i = 0; imigra < nmigra; ++imigra) { | |
| 1518 | + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra) { | |
| 1509 | 1519 | double* ptr = array.data() + imigra * netrue_padded + netrue_pad; |
| 1510 | 1520 | for (int ietrue = 0; ietrue < netrue; ++ietrue, ++i) { |
| 1511 | - *ptr++ = m_edisp(m_inx_matrix, ibase+i); | |
| 1521 | + *ptr++ = m_edisp(m_inx_matrix, i); | |
| 1512 | 1522 | } |
| 1513 | 1523 | } |
| 1514 | 1524 | |
| ... | ... | @@ -1521,14 +1531,74 @@ void GCTAEdisp2D::smooth_table(const double& sigma, const double& threshold) |
| 1521 | 1531 | // Backward transform array |
| 1522 | 1532 | GNdarray smooth = fft_smooth.backward(); |
| 1523 | 1533 | |
| 1524 | - // Clip array | |
| 1525 | - smooth = clip_array(smooth, threshold); | |
| 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(); | |
| 1526 | 1596 | |
| 1527 | 1597 | // Put back array values in matrix |
| 1528 | - for (int imigra = 0, i = 0; imigra < nmigra; ++imigra) { | |
| 1598 | + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra) { | |
| 1529 | 1599 | double* ptr = smooth.data() + imigra * netrue_padded + netrue_pad; |
| 1530 | 1600 | for (int ietrue = 0; ietrue < netrue; ++ietrue, ++i) { |
| 1531 | - m_edisp(m_inx_matrix, ibase+i) = *ptr++; | |
| 1601 | + m_edisp(m_inx_matrix, i) = *ptr++; | |
| 1532 | 1602 | } |
| 1533 | 1603 | } |
| 1534 | 1604 | |
| ... | ... | @@ -1542,6 +1612,51 @@ void GCTAEdisp2D::smooth_table(const double& sigma, const double& threshold) |
| 1542 | 1612 | /***********************************************************************//** |
| 1543 | 1613 | * @brief Get FFT of smoothing kernel |
| 1544 | 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. | |
| 1545 | 1660 | * @return FFT of smoothing kernel |
| 1546 | 1661 | * |
| 1547 | 1662 | * Returns the fast-fourrier transform of a Gaussian smoothing kernel in |
| ... | ... | @@ -1582,14 +1697,126 @@ GFft GCTAEdisp2D::fft_smooth_kernel(const int& netrue, |
| 1582 | 1697 | |
| 1583 | 1698 | |
| 1584 | 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 | |
| 1762 | + * | |
| 1763 | + * @param[in] threshold Clipping threshold in fraction of maximum value. | |
| 1764 | + * | |
| 1765 | + * Clips all migration vectors at a threshold that is a fraction of the | |
| 1766 | + * maximum value in the migration vector. | |
| 1767 | + ***************************************************************************/ | |
| 1768 | +void GCTAEdisp2D::clip_table(const double& threshold) | |
| 1769 | +{ | |
| 1770 | + // Get axes dimensions | |
| 1771 | + int netrue = m_edisp.axis_bins(m_inx_etrue); | |
| 1772 | + int nmigra = m_edisp.axis_bins(m_inx_migra); | |
| 1773 | + int ntheta = m_edisp.axis_bins(m_inx_theta); | |
| 1774 | + int npix = netrue * nmigra; | |
| 1775 | + | |
| 1776 | + // Loop over all offset angles | |
| 1777 | + for (int itheta = 0; itheta < ntheta; ++itheta) { | |
| 1778 | + | |
| 1779 | + // Loop over all true energies | |
| 1780 | + for (int ietrue = 0; ietrue < netrue; ++ietrue) { | |
| 1781 | + | |
| 1782 | + // Compute base index | |
| 1783 | + int ibase = itheta * npix + ietrue; | |
| 1784 | + | |
| 1785 | + // Compute maximum value | |
| 1786 | + double max = 0.0; | |
| 1787 | + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) { | |
| 1788 | + if (m_edisp(m_inx_matrix, i) > max) { | |
| 1789 | + max = m_edisp(m_inx_matrix, i); | |
| 1790 | + } | |
| 1791 | + } | |
| 1792 | + | |
| 1793 | + // Clip values below threshold | |
| 1794 | + double clip_value = max * threshold; | |
| 1795 | + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) { | |
| 1796 | + if (m_edisp(m_inx_matrix, i) < clip_value) { | |
| 1797 | + m_edisp(m_inx_matrix, i) = 0.0; | |
| 1798 | + } | |
| 1799 | + } | |
| 1800 | + | |
| 1801 | + } // endfor: looped over true energies | |
| 1802 | + | |
| 1803 | + } // endfor: looped over all offset angles | |
| 1804 | + | |
| 1805 | + // Return | |
| 1806 | + return; | |
| 1807 | +} | |
| 1808 | + | |
| 1809 | + | |
| 1810 | +/***********************************************************************//** | |
| 1585 | 1811 | * @brief Clip array |
| 1586 | 1812 | * |
| 1587 | 1813 | * @param[in] array Ndarray that should be clipped. |
| 1588 | 1814 | * @param[in] threshold Clipping threshold in fraction of maximum value. |
| 1589 | 1815 | * @return Clipped Ndarray |
| 1590 | 1816 | * |
| 1591 | - * Clips all values below a threshold of 10 to zero. | |
| 1817 | + * Clips all values below a threshold to zero. | |
| 1592 | 1818 | ***************************************************************************/ |
| 1819 | +/* | |
| 1593 | 1820 | GNdarray GCTAEdisp2D::clip_array(const GNdarray& array, |
| 1594 | 1821 | const double& threshold) const |
| 1595 | 1822 | { |
| ... | ... | @@ -1620,6 +1847,495 @@ GNdarray GCTAEdisp2D::clip_array(const GNdarray& array, |
| 1620 | 1847 | // Return clipped array |
| 1621 | 1848 | return clipped; |
| 1622 | 1849 | } |
| 1850 | +*/ | |
| 1851 | + | |
| 1852 | + | |
| 1853 | +/***********************************************************************//** | |
| 1854 | + * @brief Denoise energy dispersion table | |
| 1855 | + * | |
| 1856 | + * Denoise the energy dispersion table by smoothing the energy dispersion | |
| 1857 | + * for each offset angle and true energy using the smooth_array() method. | |
| 1858 | + * | |
| 1859 | + * True energy values with no energy dispersion information are interpolated | |
| 1860 | + * from neighbouring energy dispersion values. | |
| 1861 | + ***************************************************************************/ | |
| 1862 | +void GCTAEdisp2D::denoise_table(void) | |
| 1863 | +{ | |
| 1864 | + // Log entrance | |
| 1865 | + //std::cout << "GCTAEdisp2D::denoise_table in" << std::endl; | |
| 1866 | + | |
| 1867 | + // Get axes dimensions | |
| 1868 | + int netrue = m_edisp.axis_bins(m_inx_etrue); | |
| 1869 | + int nmigra = m_edisp.axis_bins(m_inx_migra); | |
| 1870 | + int ntheta = m_edisp.axis_bins(m_inx_theta); | |
| 1871 | + int npix = netrue * nmigra; | |
| 1872 | + | |
| 1873 | + // Get Gaussian parameters for all offset angles and true energies | |
| 1874 | + GNdarray gpars = get_gaussian_pars(5.0); | |
| 1875 | + | |
| 1876 | + // Loop over all offset angles | |
| 1877 | + for (int itheta = 0; itheta < ntheta; ++itheta) { | |
| 1878 | + | |
| 1879 | + // Initialise sums | |
| 1880 | + std::vector<double> sum_array; | |
| 1881 | + | |
| 1882 | + // Loop over all true energies | |
| 1883 | + for (int ietrue = 0; ietrue < netrue; ++ietrue) { | |
| 1884 | + | |
| 1885 | + // Compute base index | |
| 1886 | + int ibase = itheta * npix + ietrue; | |
| 1887 | + | |
| 1888 | + // Extract array | |
| 1889 | + GNdarray array(nmigra); | |
| 1890 | + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) { | |
| 1891 | + array(imigra) = m_edisp(m_inx_matrix, i); | |
| 1892 | + } | |
| 1893 | + | |
| 1894 | + // Compute single event value | |
| 1895 | + double event_value = get_single_event_value(array); | |
| 1896 | + | |
| 1897 | + // Smooth array | |
| 1898 | + GNdarray smoothed_array(nmigra); | |
| 1899 | + if (max(array) >= 2.0*event_value) { | |
| 1900 | + smoothed_array = gaussian_array(gpars(itheta, ietrue, 0), | |
| 1901 | + gpars(itheta, ietrue, 1)); | |
| 1902 | + } | |
| 1903 | + | |
| 1904 | + // Compute total of smoothed array | |
| 1905 | + double total = sum(smoothed_array); | |
| 1906 | + | |
| 1907 | + // Store sum | |
| 1908 | + sum_array.push_back(total); | |
| 1909 | + | |
| 1910 | + // Put back smoothed array values in matrix | |
| 1911 | + if (total > 0.0) { | |
| 1912 | + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) { | |
| 1913 | + m_edisp(m_inx_matrix, i) = smoothed_array(imigra) / total; | |
| 1914 | + } | |
| 1915 | + } | |
| 1916 | + | |
| 1917 | + } // endfor: looped over all true energies | |
| 1918 | + | |
| 1919 | + // Interpolate gaps | |
| 1920 | + for (int ietrue = 0; ietrue < netrue; ++ietrue) { | |
| 1921 | + | |
| 1922 | + // Fall through if array is not empty | |
| 1923 | + if (sum_array[ietrue] > 0.0) { | |
| 1924 | + continue; | |
| 1925 | + } | |
| 1926 | + | |
| 1927 | + // Find enclosing indices of non-empty arrays | |
| 1928 | + int ileft = ietrue; | |
| 1929 | + int iright = ietrue; | |
| 1930 | + for (; ileft >= 0; ileft--) { | |
| 1931 | + if (sum_array[ileft] > 0.0) { | |
| 1932 | + break; | |
| 1933 | + } | |
| 1934 | + } | |
| 1935 | + for (; iright < netrue; ++iright) { | |
| 1936 | + if (sum_array[iright] > 0.0) { | |
| 1937 | + break; | |
| 1938 | + } | |
| 1939 | + } | |
| 1940 | + | |
| 1941 | + // If encosing indices are valid then interpolate array | |
| 1942 | + if ((ileft >= 0) && (iright < netrue)) { | |
| 1943 | + | |
| 1944 | + // Compute weight factors | |
| 1945 | + double norm = 1.0 / double(iright-ileft); | |
| 1946 | + double wleft = double(iright - ietrue) * norm; | |
| 1947 | + double wright = double(ietrue - ileft) * norm; | |
| 1948 | + | |
| 1949 | + // Fill array by linear interpolation | |
| 1950 | + int idst = itheta * npix + ietrue; | |
| 1951 | + int isrc_left = itheta * npix + ileft; | |
| 1952 | + int isrc_right = itheta * npix + iright; | |
| 1953 | + for (int imigra = 0; imigra < nmigra; ++imigra) { | |
| 1954 | + m_edisp(m_inx_matrix, idst) = | |
| 1955 | + wleft * m_edisp(m_inx_matrix, isrc_left) + | |
| 1956 | + wright * m_edisp(m_inx_matrix, isrc_right); | |
| 1957 | + idst += netrue; | |
| 1958 | + isrc_left += netrue; | |
| 1959 | + isrc_right += netrue; | |
| 1960 | + } | |
| 1961 | + | |
| 1962 | + } // endif: there were valid enclosing indices | |
| 1963 | + | |
| 1964 | + } // endfor: looped over all true energies | |
| 1965 | + | |
| 1966 | + } // endfor: looped over all offset angles | |
| 1967 | + | |
| 1968 | + // Log exit | |
| 1969 | + //std::cout << "GCTAEdisp2D::denoise_table out" << std::endl; | |
| 1970 | + | |
| 1971 | + // Return | |
| 1972 | + return; | |
| 1973 | +} | |
| 1974 | + | |
| 1975 | + | |
| 1976 | +/***********************************************************************//** | |
| 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 | |
| 2091 | + * | |
| 2092 | + * @param[in] mean Gaussian mean. | |
| 2093 | + * @param[in] rms Gaussian rms. | |
| 2094 | + * @return Gaussian approximation of energy dispersion array. | |
| 2095 | + * | |
| 2096 | + * Returns a Gaussian approximation of the energy dispersion array by | |
| 2097 | + * computing the mean migration value and its root mean square and by using | |
| 2098 | + * these values as the centre and the width of a Gaussian function. The | |
| 2099 | + * Gaussian function is normalized so that the sum of the output array is | |
| 2100 | + * unity. | |
| 2101 | + ***************************************************************************/ | |
| 2102 | +GNdarray GCTAEdisp2D::gaussian_array(const double& mean, const double& rms) const | |
| 2103 | +{ | |
| 2104 | + // Initialise empty Gaussian array | |
| 2105 | + int nmigra = m_edisp.axis_bins(m_inx_migra); | |
| 2106 | + GNdarray gaussian(nmigra); | |
| 2107 | + | |
| 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 | |
| 2116 | + // approximation | |
| 2117 | + if (rms > 0.0) { | |
| 2118 | + | |
| 2119 | + // Get reference to migration values | |
| 2120 | + const GNodeArray& migras = m_edisp.axis_nodes(m_inx_migra); | |
| 2121 | + int nmigra = migras.size(); | |
| 2122 | + | |
| 2123 | + // Compute Gaussian | |
| 2124 | + double total_gauss = 0.0; | |
| 2125 | + for (int imigra = 0; imigra < nmigra; ++imigra) { | |
| 2126 | + double arg = (migras[imigra] - mean) / rms; | |
| 2127 | + double value = std::exp(-0.5*arg*arg); | |
| 2128 | + gaussian(imigra) = value; | |
| 2129 | + total_gauss += value; | |
| 2130 | + } | |
| 2131 | + | |
| 2132 | + // Normalise Gaussian | |
| 2133 | + if (total_gauss > 0.0) { | |
| 2134 | + gaussian *= 1.0 / total_gauss; | |
| 2135 | + } | |
| 2136 | + | |
| 2137 | + } // endif: computed Gaussian approximation | |
| 2138 | + | |
| 2139 | + // Return Gaussian array | |
| 2140 | + return gaussian; | |
| 2141 | +} | |
| 2142 | + | |
| 2143 | + | |
| 2144 | +/***********************************************************************//** | |
| 2145 | + * @brief Compute mean and root mean square of migration array | |
| 2146 | + * | |
| 2147 | + * @param[in] sigma Smoothing parameter in true energy. | |
| 2148 | + * @return Array of mean and rms values for each offset angle and true energy. | |
| 2149 | + * | |
| 2150 | + * Computes the mean and the root mean square of the migration array for each | |
| 2151 | + * offset angle and true energy and applies a Gaussian smoothing in true | |
| 2152 | + * energy. | |
| 2153 | + ***************************************************************************/ | |
| 2154 | +GNdarray GCTAEdisp2D::get_gaussian_pars(const double& sigma) const | |
| 2155 | +{ | |
| 2156 | + // Get axes dimensions | |
| 2157 | + int netrue = m_edisp.axis_bins(m_inx_etrue); | |
| 2158 | + int nmigra = m_edisp.axis_bins(m_inx_migra); | |
| 2159 | + int ntheta = m_edisp.axis_bins(m_inx_theta); | |
| 2160 | + int npix = netrue * nmigra; | |
| 2161 | + | |
| 2162 | + // Initialise result | |
| 2163 | + GNdarray result(ntheta, netrue, 2); | |
| 2164 | + | |
| 2165 | + // Setup smoothing kernel | |
| 2166 | + GFft fft_kernel = fft_smooth_kernel(netrue, sigma); | |
| 2167 | + | |
| 2168 | + // Loop over all offset angles | |
| 2169 | + for (int itheta = 0; itheta < ntheta; ++itheta) { | |
| 2170 | + | |
| 2171 | + // Allocate working arrays | |
| 2172 | + GNdarray work_mean(netrue); | |
| 2173 | + GNdarray work_rms(netrue); | |
| 2174 | + GNdarray work_sum(netrue); | |
| 2175 | + | |
| 2176 | + // Initialise mean and rms values | |
| 2177 | + double mean_value = 0.0; | |
| 2178 | + double rms_value = 0.0; | |
| 2179 | + | |
| 2180 | + // Loop over all true energies | |
| 2181 | + for (int ietrue = 0; ietrue < netrue; ++ietrue) { | |
| 2182 | + | |
| 2183 | + // Compute base index | |
| 2184 | + int ibase = itheta * npix + ietrue; | |
| 2185 | + | |
| 2186 | + // Extract array | |
| 2187 | + GNdarray array(nmigra); | |
| 2188 | + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) { | |
| 2189 | + array(imigra) = m_edisp(m_inx_matrix, i); | |
| 2190 | + } | |
| 2191 | + | |
| 2192 | + // Store sum (only results with positive sum will be kept) | |
| 2193 | + work_sum(ietrue) = sum(array); | |
| 2194 | + | |
| 2195 | + // Compute single event value | |
| 2196 | + double event_value = get_single_event_value(array); | |
| 2197 | + | |
| 2198 | + // Get Gaussian mean and rms. Use last mean and rms value in case | |
| 2199 | + // that the array is empty | |
| 2200 | + if (work_sum(ietrue) > 0) { | |
| 2201 | + mean_value = 0.0; | |
| 2202 | + rms_value = 0.0; | |
| 2203 | + get_mean_rms(array, &mean_value, &rms_value); | |
| 2204 | + } | |
| 2205 | + | |
| 2206 | + // Store mean and rms | |
| 2207 | + work_mean(ietrue) = mean_value; | |
| 2208 | + work_rms(ietrue) = rms_value; | |
| 2209 | + | |
| 2210 | + } // endfor: looped over all true energies | |
| 2211 | + | |
| 2212 | + // Pad front | |
| 2213 | + for (int ietrue = 0; ietrue < netrue; ++ietrue) { | |
| 2214 | + if (work_mean(ietrue) > 0.0) { | |
| 2215 | + for (int i = 0; i < ietrue; ++i) { | |
| 2216 | + work_mean(i) = work_mean(ietrue); | |
| 2217 | + work_rms(i) = work_rms(ietrue); | |
| 2218 | + } | |
| 2219 | + break; | |
| 2220 | + } | |
| 2221 | + } | |
| 2222 | + | |
| 2223 | + // Pad back | |
| 2224 | + for (int ietrue = netrue-1; ietrue >= 0; ietrue--) { | |
| 2225 | + if (work_mean(ietrue) > 0.0) { | |
| 2226 | + for (int i = ietrue; i < netrue; ++i) { | |
| 2227 | + work_mean(i) = work_mean(ietrue); | |
| 2228 | + work_rms(i) = work_rms(ietrue); | |
| 2229 | + } | |
| 2230 | + break; | |
| 2231 | + } | |
| 2232 | + } | |
| 2233 | + | |
| 2234 | + // Smooth mean | |
| 2235 | + GFft fft_mean(work_mean); | |
| 2236 | + GFft fft_mean_smooth = fft_mean * fft_kernel; | |
| 2237 | + work_mean = fft_mean_smooth.backward(); | |
| 2238 | + | |
| 2239 | + // Smooth rms | |
| 2240 | + GFft fft_rms(work_rms); | |
| 2241 | + GFft fft_rms_smooth = fft_rms * fft_kernel; | |
| 2242 | + work_rms = fft_rms_smooth.backward(); | |
| 2243 | + | |
| 2244 | + // Store result | |
| 2245 | + for (int ietrue = 0; ietrue < netrue; ++ietrue) { | |
| 2246 | + if (work_sum(ietrue) > 0.0) { | |
| 2247 | + result(itheta, ietrue, 0) = work_mean(ietrue); | |
| 2248 | + result(itheta, ietrue, 1) = work_rms(ietrue); | |
| 2249 | + } | |
| 2250 | + } | |
| 2251 | + | |
| 2252 | + } // endif: looped over all offset angles | |
| 2253 | + | |
| 2254 | + // Return result | |
| 2255 | + return result; | |
| 2256 | +} | |
| 2257 | + | |
| 2258 | + | |
| 2259 | +/***********************************************************************//** | |
| 2260 | + * @brief Compute mean and root mean square of migration array | |
| 2261 | + * | |
| 2262 | + * @param[in] array Energy dispersion array. | |
| 2263 | + * @param[out] mean Pointer to mean migration value. | |
| 2264 | + * @param[out] rms Pointer to root mean square value. | |
| 2265 | + * | |
| 2266 | + * Computes the mean and the root mean square of the migration array. If the | |
| 2267 | + * migration array is empty the mean and root mean square values are set to | |
| 2268 | + * zero. | |
| 2269 | + * | |
| 2270 | + * The method does not check whether the @p mean and @p rms pointers are | |
| 2271 | + * valid. | |
| 2272 | + ***************************************************************************/ | |
| 2273 | +void GCTAEdisp2D::get_mean_rms(const GNdarray& array, | |
| 2274 | + double* mean, | |
| 2275 | + double* rms) const | |
| 2276 | +{ | |
| 2277 | + // Initialise mean and rms | |
| 2278 | + *mean = 0.0; | |
| 2279 | + *rms = 0.0; | |
| 2280 | + | |
| 2281 | + // Get reference to migration values | |
| 2282 | + const GNodeArray& migras = m_edisp.axis_nodes(m_inx_migra); | |
| 2283 | + int nmigra = migras.size(); | |
| 2284 | + | |
| 2285 | + // Pre-compute values for mean and rms computation | |
| 2286 | + double sum = 0.0; | |
| 2287 | + for (int imigra = 0; imigra < nmigra; ++imigra) { | |
| 2288 | + double migra = migras[imigra]; | |
| 2289 | + double weight = migra * array(imigra); | |
| 2290 | + *mean += migra * array(imigra); | |
| 2291 | + *rms += weight * migra; | |
| 2292 | + sum += array(imigra); | |
| 2293 | + } | |
| 2294 | + | |
| 2295 | + // If the array is not empty then compute now mean and standard deviation | |
| 2296 | + if (sum > 0.0) { | |
| 2297 | + *mean /= sum; | |
| 2298 | + *rms /= sum; | |
| 2299 | + *rms -= (*mean) * (*mean); | |
| 2300 | + *rms = std::sqrt(*rms); | |
| 2301 | + } | |
| 2302 | + | |
| 2303 | + // Return | |
| 2304 | + return; | |
| 2305 | +} | |
| 2306 | + | |
| 2307 | + | |
| 2308 | +/***********************************************************************//** | |
| 2309 | + * @brief Estimate the value of a single event | |
| 2310 | + * | |
| 2311 | + * @param[in] array Energy dispersion array. | |
| 2312 | + * @return Value of a single event. | |
| 2313 | + * | |
| 2314 | + * Returns the minimum non-zero value in an array as an estimate of the value | |
| 2315 | + * of a single event. If the array is empty the method returns zero. | |
| 2316 | + ***************************************************************************/ | |
| 2317 | +double GCTAEdisp2D::get_single_event_value(const GNdarray& array) const | |
| 2318 | +{ | |
| 2319 | + // Initialise minimum value | |
| 2320 | + double event_value = 1.0e30; | |
| 2321 | + | |
| 2322 | + // Get minimum non-zero value | |
| 2323 | + for (int i = 0; i < array.size(); ++i) { | |
| 2324 | + double value = array(i); | |
| 2325 | + if ((value > 0.0) && (value < event_value)) { | |
| 2326 | + event_value = value; | |
| 2327 | + } | |
| 2328 | + } | |
| 2329 | + | |
| 2330 | + // If no minimum value was encountered then set single event value to | |
| 2331 | + // zero | |
| 2332 | + if (event_value == 1.0e30) { | |
| 2333 | + event_value = 0.0; | |
| 2334 | + } | |
| 2335 | + | |
| 2336 | + // Return | |
| 2337 | + return event_value; | |
| 2338 | +} | |
| 1623 | 2339 | |
| 1624 | 2340 | |
| 1625 | 2341 | /***********************************************************************//** | ... | ... |