Commit 147c3f169b4e09e55c9c50341df023eb991fe6fa

Authored by Jürgen Knödlseder
1 parent 2b15414b

Settle energy dispersion kludge (#2403)

ChangeLog
1   -2018-02-23
  1 +2018-03-19
2 2  
3 3 * Bug fix version 1.5.1 released
4 4 ================================
5 5  
6   - Add kludge to handle noisy CTA 2D energy dispersion (#2342).
  6 + Add kludge to handle noisy CTA 2D energy dispersion (#2342, #2403).
7 7 Prevent unintentional deallocation of arguments (#2340).
8 8 Correct sky map smoothing.
9 9  
... ...
1 1 New Features and Important Changes in GammaLib 1.5.1
2 2  
3   -23 February 2018
  3 +19 Mars 2018
4 4  
5 5  
6 6 1. Introduction
... ... @@ -149,9 +149,7 @@ None
149 149 20. CTA interface
150 150 -----------------
151 151 Add a kludge to handle the noisy energy dispersion that is shipped with the 1DC
152   -data and that is included in the Prod3 release. Noisy pixel values are skipped
153   -and a slight smoothing is applied in true energy to reduce the noise level
154   -(#2342).
  152 +data and that is included in the Prod3 release (#2342, #2403).
155 153  
156 154 The GCTAObservation::response() and GCTAOnOffObservation::response() methods
157 155 now only deallocate the previous content if the reference to the argument
... ...
doc/source/admin/release_history/1.5.1.rst
... ... @@ -10,6 +10,8 @@ GammaLib 1.5.1 is a bug fix release for GammaLib release 1.5.
10 10  
11 11 The following bugs were fixed:
12 12  
  13 +* [`2403 <https://cta-redmine.irap.omp.eu/issues/2403>`_] -
  14 + Refine kludge to handle noisy CTA 2D energy dispersion
13 15 * [`2342 <https://cta-redmine.irap.omp.eu/issues/2342>`_] -
14 16 Add kludge to handle noisy CTA 2D energy dispersion
15 17 * [`2340 <https://cta-redmine.irap.omp.eu/issues/2340>`_] -
... ...
inst/cta/include/GCTAEdisp2D.hpp
... ... @@ -126,15 +126,23 @@ private:
126 126 void normalize_table(void);
127 127  
128 128 // Kludge
129   - void clean_table(const int& threshold);
130   - void clip_table(const double& threshold);
131   - void denoise_table(const double& sigma);
132   - GNdarray gaussian_array(const double& mean, const double& rms) const;
133   - GNdarray get_gaussian_pars(const double& sigma) const;
134   - GFft fft_smooth_kernel(const int& nbins,
135   - const double& sigma) const;
  129 + void smooth_table(void);
  130 + GNdarray smooth_array(const GNdarray& array,
  131 + const int& nstart,
  132 + const int& nstop,
  133 + const double& limit) const;
  134 + double smoothed_array_value(const int& inx,
  135 + const GNdarray& array,
  136 + const int& nodes,
  137 + const double& limit) const;
  138 + void get_moments(const int& itheta,
  139 + GNdarray* mean,
  140 + GNdarray* rms,
  141 + GNdarray* total) const;
136 142 void get_mean_rms(const GNdarray& array, double* mean, double* rms) const;
137   - double get_single_event_value(const GNdarray& array) const;
  143 + GNdarray gaussian_array(const double& mean,
  144 + const double& rms,
  145 + const double& total) const;
138 146  
139 147 // Protected classes
140 148 class edisp_kern : public GFunction {
... ...
inst/cta/src/GCTAEdisp2D.cpp
... ... @@ -1200,9 +1200,7 @@ void GCTAEdisp2D::set_table(void)
1200 1200  
1201 1201 // Smooth energy dispersion table
1202 1202 #if defined(G_SMOOTH_EDISP_KLUDGE)
1203   - denoise_table(10.0);
1204   - clean_table(15);
1205   - clip_table(0.001);
  1203 + smooth_table();
1206 1204 #endif
1207 1205  
1208 1206 // Normalize energy dispersion table
... ... @@ -1362,166 +1360,80 @@ void GCTAEdisp2D::normalize_table(void)
1362 1360 }
1363 1361  
1364 1362  
  1363 +
1365 1364 /***********************************************************************//**
1366   - * @brief Clean energy dispersion table
1367   - *
1368   - * @param[in] threshold Minimum number of consecutive non-zeros
1369   - *
1370   - * Removes noise from the energy dispersion table.
1371   - *
1372   - * For each offset angle and true energy, the method determines the maximum
1373   - * value of the energy dispersion table and then walks towards smaller and
1374   - * larger migration value to determine the first pixel for which the energy
1375   - * dispersion becomes zero. All pixels beyond these pixels are then clipped
1376   - * to zero. This removes noise that is at small or large migration values.
1377   - *
1378   - * The method then computes the number of remaining contiguos non-zero pixels
1379   - * for each offset angle and true energy, and if this number is smaller than
1380   - * a threshold, all pixels are set to zero. This removes true energy bins
1381   - * that are sparesely filled.
  1365 + * @brief Smoothed energy dispersion table
1382 1366 ***************************************************************************/
1383   -void GCTAEdisp2D::clean_table(const int& threshold)
  1367 +void GCTAEdisp2D::smooth_table(void)
1384 1368 {
  1369 + // Log entrance
  1370 + //std::cout << "GCTAEdisp2D::smooth_table in" << std::endl;
  1371 +
1385 1372 // Get axes dimensions
1386 1373 int netrue = m_edisp.axis_bins(m_inx_etrue);
1387 1374 int nmigra = m_edisp.axis_bins(m_inx_migra);
1388 1375 int ntheta = m_edisp.axis_bins(m_inx_theta);
  1376 + int npix = netrue * nmigra;
1389 1377  
1390 1378 // Loop over all offset angles
1391 1379 for (int itheta = 0; itheta < ntheta; ++itheta) {
1392 1380  
1393   - // Loop ober all true energies
1394   - for (int ietrue = 0; ietrue < netrue; ++ietrue) {
  1381 + // Compute means, rms and total as function of etrue
  1382 + GNdarray mean(netrue);
  1383 + GNdarray rms(netrue);
  1384 + GNdarray total(netrue);
  1385 + get_moments(itheta, &mean, &rms, &total);
1395 1386  
1396   - // Compute base index
1397   - int ibase = ietrue + itheta * netrue * nmigra;
1398   -
1399   - // Determine index of maximum value
1400   - int imax = 0;
1401   - double max = m_edisp(m_inx_matrix,ibase+ietrue);
1402   - for (int imigra = 1; imigra < nmigra; ++imigra) {
1403   - int inx = ibase + imigra*netrue;
1404   - double value = m_edisp(m_inx_matrix, inx);
1405   - if (value > max) {
1406   - imax = imigra;
1407   - max = value;
1408   - }
1409   - }
  1387 + // Smooth all three
  1388 + mean = smooth_array(mean, 30, 30, 0.5);
  1389 + rms = smooth_array(rms, 30, 30, 0.03);
  1390 + total = smooth_array(total, 30, 30, 0.0);
1410 1391  
1411   - // Do nothing if all pixels are zero
1412   - if (max == 0.0) {
1413   - continue;
  1392 + // Make sure that total is not negative
  1393 + for (int ietrue = 0; ietrue < netrue; ++ietrue) {
  1394 + if (total(ietrue) < 0.0) {
  1395 + total(ietrue) = 0.0;
1414 1396 }
  1397 + }
  1398 +
  1399 + // Replace matrix by Gaussians
  1400 + for (int ietrue = 0; ietrue < netrue; ++ietrue) {
1415 1401  
1416   - // Walk from maximum to the first pixel
1417   - bool clip = false;
1418   - for (int imigra = imax; imigra >= 0; imigra--) {
1419   - int inx = ibase + imigra*netrue;
1420   - if (clip) {
1421   - m_edisp(m_inx_matrix, inx) = 0.0;
1422   - }
1423   - else {
1424   - double value = m_edisp(m_inx_matrix, inx);
1425   - if (value == 0.0) {
1426   - clip = true;
1427   - }
1428   - }
1429   - }
  1402 + // Continue only if there is information
  1403 + if (total(ietrue) > 0.0) {
1430 1404  
1431   - // Walk from maximum to the last pixel
1432   - clip = false;
1433   - for (int imigra = imax; imigra < nmigra; ++imigra) {
1434   - int inx = ibase + imigra*netrue;
1435   - if (clip) {
1436   - m_edisp(m_inx_matrix, inx) = 0.0;
1437   - }
1438   - else {
1439   - double value = m_edisp(m_inx_matrix, inx);
1440   - if (value == 0.0) {
1441   - clip = true;
1442   - }
1443   - }
1444   - }
  1405 + // Get Gaussian
  1406 + GNdarray gauss = gaussian_array(mean(ietrue), rms(ietrue), total(ietrue));
1445 1407  
1446   - // Determine number of contiguous non-zero pixels
1447   - int nonzero = 0;
1448   - int contiguous_nonzero = 0;
1449   - for (int imigra = 0; imigra < nmigra; ++imigra) {
1450   - int inx = ibase + imigra*netrue;
1451   - double value = m_edisp(m_inx_matrix, inx);
1452   - if (value != 0.0) {
1453   - nonzero++;
1454   - if (nonzero > contiguous_nonzero) {
1455   - contiguous_nonzero = nonzero;
  1408 + // Compute base index
  1409 + int ibase = itheta * npix + ietrue;
  1410 + if ((mean(ietrue) > 0.0) && (rms(ietrue))) {
  1411 + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) {
  1412 + m_edisp(m_inx_matrix, i) = gauss(imigra);
1456 1413 }
1457 1414 }
1458 1415 else {
1459   - nonzero = 0;
1460   - }
1461   - }
1462   -
1463   - // If there are less than threshold contiguous non-zero pixels we
1464   - // only have noise, hence all pixels are set to zero
1465   - if (contiguous_nonzero < threshold) {
1466   - for (int imigra = 0; imigra < nmigra; ++imigra) {
1467   - int inx = ibase + imigra*netrue;
1468   - m_edisp(m_inx_matrix, inx) = 0.0;
1469   - }
1470   - }
1471   -
1472   - } // endfor: looped over all true energies
1473   -
1474   - } // endfor: looped over all offset angles
1475   -
1476   - // Return
1477   - return;
1478   -}
1479   -
1480   -
1481   -/***********************************************************************//**
1482   - * @brief Clip energy dispersion table
1483   - *
1484   - * @param[in] threshold Clipping threshold as fraction of maximum value.
1485   - *
1486   - * Clips all migration vectors at a threshold that is a fraction of the
1487   - * maximum value in the migration vector.
1488   - ***************************************************************************/
1489   -void GCTAEdisp2D::clip_table(const double& threshold)
1490   -{
1491   - // Get axes dimensions
1492   - int netrue = m_edisp.axis_bins(m_inx_etrue);
1493   - int nmigra = m_edisp.axis_bins(m_inx_migra);
1494   - int ntheta = m_edisp.axis_bins(m_inx_theta);
1495   - int npix = netrue * nmigra;
1496   -
1497   - // Loop over all offset angles
1498   - for (int itheta = 0; itheta < ntheta; ++itheta) {
1499   -
1500   - // Loop over all true energies
1501   - for (int ietrue = 0; ietrue < netrue; ++ietrue) {
1502   -
1503   - // Compute base index
1504   - int ibase = itheta * npix + ietrue;
1505   -
1506   - // Compute maximum value
1507   - double max = 0.0;
1508   - for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) {
1509   - if (m_edisp(m_inx_matrix, i) > max) {
1510   - max = m_edisp(m_inx_matrix, i);
  1416 + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) {
  1417 + m_edisp(m_inx_matrix, i) = 0.0;
  1418 + }
1511 1419 }
1512   - }
  1420 +
  1421 + } // endif: there was information
1513 1422  
1514   - // Clip values below threshold
1515   - double clip_value = max * threshold;
1516   - for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) {
1517   - if (m_edisp(m_inx_matrix, i) < clip_value) {
  1423 + // ... otherwise reset the matrix to zero
  1424 + else {
  1425 + int ibase = itheta * npix + ietrue;
  1426 + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) {
1518 1427 m_edisp(m_inx_matrix, i) = 0.0;
1519 1428 }
1520 1429 }
1521 1430  
1522 1431 } // endfor: looped over true energies
1523 1432  
1524   - } // endfor: looped over all offset angles
  1433 + } // endfor: looped over offset angles
  1434 +
  1435 + // Log exit
  1436 + //std::cout << "GCTAEdisp2D::smooth_table out" << std::endl;
1525 1437  
1526 1438 // Return
1527 1439 return;
... ... @@ -1529,334 +1441,198 @@ void GCTAEdisp2D::clip_table(const double&amp; threshold)
1529 1441  
1530 1442  
1531 1443 /***********************************************************************//**
1532   - * @brief Denoise energy dispersion table
1533   - *
1534   - * @param[in] sigma Gaussian smoothing single in true energy.
  1444 + * @brief Smoothed array
1535 1445 *
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.
  1446 + * @param[in] array Array.
  1447 + * @param[in] nstart Number of nodes used for regression at first array value.
  1448 + * @param[in] nstop Number of nodes used for regression at last array value.
  1449 + * @param[in] limit Limit for array element exclusion.
  1450 + * @return Smoothed array.
1540 1451 *
1541   - * True energy values with no energy dispersion information are interpolated
1542   - * from neighbouring energy dispersion values.
  1452 + * Returns a smoothed array that is computed by locally adjusting a linear
  1453 + * regression law to the array elements.
1543 1454 ***************************************************************************/
1544   -void GCTAEdisp2D::denoise_table(const double& sigma)
  1455 +GNdarray GCTAEdisp2D::smooth_array(const GNdarray& array,
  1456 + const int& nstart,
  1457 + const int& nstop,
  1458 + const double& limit) const
1545 1459 {
1546   - // Log entrance
1547   - //std::cout << "GCTAEdisp2D::denoise_table in" << std::endl;
1548   -
1549   - // Get axes dimensions
1550   - int netrue = m_edisp.axis_bins(m_inx_etrue);
1551   - int nmigra = m_edisp.axis_bins(m_inx_migra);
1552   - int ntheta = m_edisp.axis_bins(m_inx_theta);
1553   - int npix = netrue * nmigra;
1554   -
1555   - // Get Gaussian parameters for all offset angles and true energies
1556   - GNdarray gpars = get_gaussian_pars(sigma);
1557   -
1558   - // Loop over all offset angles
1559   - for (int itheta = 0; itheta < ntheta; ++itheta) {
1560   -
1561   - // Initialise sums
1562   - std::vector<double> sum_array;
1563   -
1564   - // Loop over all true energies
1565   - for (int ietrue = 0; ietrue < netrue; ++ietrue) {
1566   -
1567   - // Compute base index
1568   - int ibase = itheta * npix + ietrue;
1569   -
1570   - // Extract array
1571   - GNdarray array(nmigra);
1572   - for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) {
1573   - array(imigra) = m_edisp(m_inx_matrix, i);
1574   - }
1575   -
1576   - // Compute single event value
1577   - double event_value = get_single_event_value(array);
1578   -
1579   - // Smooth array
1580   - GNdarray smoothed_array(nmigra);
1581   - if (max(array) >= 2.0*event_value) {
1582   - smoothed_array = gaussian_array(gpars(itheta, ietrue, 0),
1583   - gpars(itheta, ietrue, 1));
1584   - }
1585   -
1586   - // Compute total of smoothed array
1587   - double total = sum(smoothed_array);
1588   -
1589   - // Store sum
1590   - sum_array.push_back(total);
1591   -
1592   - // Put back smoothed array values in matrix
1593   - if (total > 0.0) {
1594   - for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) {
1595   - m_edisp(m_inx_matrix, i) = smoothed_array(imigra) / total;
1596   - }
1597   - }
1598   -
1599   - } // endfor: looped over all true energies
  1460 + // Initialise smoothed array
  1461 + GNdarray smoothed_array(array.size());
1600 1462  
1601   - // Interpolate gaps
1602   - for (int ietrue = 0; ietrue < netrue; ++ietrue) {
1603   -
1604   - // Fall through if array is not empty
1605   - if (sum_array[ietrue] > 0.0) {
1606   - continue;
1607   - }
  1463 + // Compute node step size
  1464 + double nstep = double(nstop - nstart)/double(array.size());
1608 1465  
1609   - // Find enclosing indices of non-empty arrays
1610   - int ileft = ietrue;
1611   - int iright = ietrue;
1612   - for (; ileft >= 0; ileft--) {
1613   - if (sum_array[ileft] > 0.0) {
1614   - break;
1615   - }
1616   - }
1617   - for (; iright < netrue; ++iright) {
1618   - if (sum_array[iright] > 0.0) {
1619   - break;
1620   - }
1621   - }
1622   -
1623   - // If encosing indices are valid then interpolate array
1624   - if ((ileft >= 0) && (iright < netrue)) {
1625   -
1626   - // Compute weight factors
1627   - double norm = 1.0 / double(iright-ileft);
1628   - double wleft = double(iright - ietrue) * norm;
1629   - double wright = double(ietrue - ileft) * norm;
1630   -
1631   - // Fill array by linear interpolation
1632   - int idst = itheta * npix + ietrue;
1633   - int isrc_left = itheta * npix + ileft;
1634   - int isrc_right = itheta * npix + iright;
1635   - for (int imigra = 0; imigra < nmigra; ++imigra) {
1636   - m_edisp(m_inx_matrix, idst) =
1637   - wleft * m_edisp(m_inx_matrix, isrc_left) +
1638   - wright * m_edisp(m_inx_matrix, isrc_right);
1639   - idst += netrue;
1640   - isrc_left += netrue;
1641   - isrc_right += netrue;
1642   - }
1643   -
1644   - } // endif: there were valid enclosing indices
1645   -
1646   - } // endfor: looped over all true energies
1647   -
1648   - } // endfor: looped over all offset angles
1649   -
1650   - // Log exit
1651   - //std::cout << "GCTAEdisp2D::denoise_table out" << std::endl;
  1466 + // Loop over all array elements and computed the smoothed value
  1467 + for (int i = 0; i < array.size(); ++i) {
  1468 + int nodes = nstart + int(i*nstep);
  1469 + smoothed_array(i) = smoothed_array_value(i, array, nodes, limit);
  1470 + }
1652 1471  
1653   - // Return
1654   - return;
  1472 + // Return smoothed array
  1473 + return smoothed_array;
1655 1474 }
1656 1475  
1657 1476  
1658 1477 /***********************************************************************//**
1659   - * @brief Return Gaussian approximation of energy dispersion array
  1478 + * @brief Get smoothed array value
1660 1479 *
1661   - * @param[in] mean Gaussian mean.
1662   - * @param[in] rms Gaussian rms.
1663   - * @return Gaussian approximation of energy dispersion array.
  1480 + * @param[in] inx Index of array element.
  1481 + * @param[in] array Array.
  1482 + * @param[in] nodes Number of nodes used for regression.
  1483 + * @param[in] limit Limit for array element exclusion.
  1484 + * @return Smoothed array value.
1664 1485 *
1665   - * Returns a Gaussian approximation of the energy dispersion array by
1666   - * computing the mean migration value and its root mean square and by using
1667   - * these values as the centre and the width of a Gaussian function. The
1668   - * Gaussian function is normalized so that the sum of the output array is
1669   - * unity.
  1486 + * Returns a smoothed array value that is derived from adjusting a linear
  1487 + * law using regression to @p nodes adjacent array elements. Array elements
  1488 + * with values below @p limit are excluded from the regression.
1670 1489 ***************************************************************************/
1671   -GNdarray GCTAEdisp2D::gaussian_array(const double& mean, const double& rms) const
  1490 +double GCTAEdisp2D::smoothed_array_value(const int& inx,
  1491 + const GNdarray& array,
  1492 + const int& nodes,
  1493 + const double& limit) const
1672 1494 {
1673   - // Initialise empty Gaussian array
1674   - int nmigra = m_edisp.axis_bins(m_inx_migra);
1675   - GNdarray gaussian(nmigra);
1676   -
1677   - // If the array contains information then compute a Gaussian
1678   - // approximation
1679   - if (rms > 0.0) {
1680   -
1681   - // Get reference to migration values
1682   - const GNodeArray& migras = m_edisp.axis_nodes(m_inx_migra);
1683   - int nmigra = migras.size();
  1495 + // Initialise variables
  1496 + double mean_x = 0.0;
  1497 + double mean_y = 0.0;
  1498 + int ileft = inx - 1;
  1499 + int iright = inx + 1;
  1500 + int nleft = 0;
  1501 + int nright = 0;
  1502 +
  1503 + // Allocate vector of elements used for regression
  1504 + std::vector<double> x_vals;
  1505 + std::vector<double> y_vals;
  1506 +
  1507 + // Add nodes on the left of the element of interest
  1508 + while (nleft < nodes) {
  1509 + if (ileft < 0) {
  1510 + break;
  1511 + }
  1512 + if (array(ileft) > limit) {
  1513 + double x = double(ileft);
  1514 + double y = array(ileft);
  1515 + x_vals.push_back(x);
  1516 + y_vals.push_back(y);
  1517 + mean_x += x;
  1518 + mean_y += y;
  1519 + nleft++;
  1520 + }
  1521 + ileft--;
  1522 + }
1684 1523  
1685   - // Compute Gaussian
1686   - double total_gauss = 0.0;
1687   - for (int imigra = 0; imigra < nmigra; ++imigra) {
1688   - double arg = (migras[imigra] - mean) / rms;
1689   - double value = std::exp(-0.5*arg*arg);
1690   - gaussian(imigra) = value;
1691   - total_gauss += value;
  1524 + // Add remaining nodes on the right of the element of interest
  1525 + while (nright < 2*nodes-nleft) {
  1526 + if (iright >= array.size()) {
  1527 + break;
  1528 + }
  1529 + if (array(iright) > limit) {
  1530 + double x = double(iright);
  1531 + double y = array(iright);
  1532 + x_vals.push_back(x);
  1533 + y_vals.push_back(y);
  1534 + mean_x += x;
  1535 + mean_y += y;
  1536 + nright++;
1692 1537 }
  1538 + iright++;
  1539 + }
1693 1540  
1694   - // Normalise Gaussian
1695   - if (total_gauss > 0.0) {
1696   - gaussian *= 1.0 / total_gauss;
  1541 + // Add remaining nodes on the left if all right nodes were not exhausted
  1542 + while (nleft < 2*nodes-nright) {
  1543 + if (ileft < 0) {
  1544 + break;
  1545 + }
  1546 + if (array(ileft) > limit) {
  1547 + double x = double(ileft);
  1548 + double y = array(ileft);
  1549 + x_vals.push_back(x);
  1550 + y_vals.push_back(y);
  1551 + mean_x += x;
  1552 + mean_y += y;
  1553 + nleft++;
1697 1554 }
  1555 + ileft--;
  1556 + }
1698 1557  
1699   - } // endif: computed Gaussian approximation
  1558 + // Compute mean x and y values
  1559 + double total = double(nleft+nright);
  1560 + if (total > 0.0) {
  1561 + mean_x /= total;
  1562 + mean_y /= total;
  1563 + }
1700 1564  
1701   - // Return Gaussian array
1702   - return gaussian;
  1565 + // Compute regression line slope
  1566 + double beta_nom = 0.0;
  1567 + double beta_denom = 0.0;
  1568 + for (int i = 0; i < x_vals.size(); ++i) {
  1569 + double x = x_vals[i];
  1570 + double y = y_vals[i];
  1571 + beta_nom += (x - mean_x) * (y - mean_y);
  1572 + beta_denom += (x - mean_x) * (x - mean_x);
  1573 + }
  1574 + double beta = beta_nom / beta_denom;
  1575 +
  1576 + // Compute regression line offset
  1577 + double alpha = mean_y - beta * mean_x;
  1578 +
  1579 + // Compute value
  1580 + double value = alpha + beta * double(inx);
  1581 +
  1582 + // Return value
  1583 + return value;
1703 1584 }
1704 1585  
1705 1586  
1706 1587 /***********************************************************************//**
1707   - * @brief Compute mean and root mean square of migration array
  1588 + * @brief Compute moments
1708 1589 *
1709   - * @param[in] sigma Smoothing parameter in true energy.
1710   - * @return Array of mean and rms values for each offset angle and true energy.
  1590 + * @param[in] itheta Offset angle index.
  1591 + * @param[out] mean Pointer to mean array.
  1592 + * @param[out] rms Pointer to rms array.
  1593 + * @param[out] total Pointer to total array.
1711 1594 *
1712   - * Computes the mean and the root mean square of the migration array for each
1713   - * offset angle and true energy and applies a Gaussian smoothing in true
1714   - * energy.
  1595 + * Computes the mean and root mean square values as function of true energy
  1596 + * for a given offset angle.
1715 1597 ***************************************************************************/
1716   -GNdarray GCTAEdisp2D::get_gaussian_pars(const double& sigma) const
  1598 +void GCTAEdisp2D::get_moments(const int& itheta,
  1599 + GNdarray* mean,
  1600 + GNdarray* rms,
  1601 + GNdarray* total) const
1717 1602 {
1718 1603 // Get axes dimensions
1719   - int netrue = m_edisp.axis_bins(m_inx_etrue);
1720   - int nmigra = m_edisp.axis_bins(m_inx_migra);
1721   - int ntheta = m_edisp.axis_bins(m_inx_theta);
1722   - int npix = netrue * nmigra;
  1604 + int netrue = m_edisp.axis_bins(m_inx_etrue);
  1605 + int nmigra = m_edisp.axis_bins(m_inx_migra);
  1606 + int npix = netrue * nmigra;
1723 1607  
1724   - // Initialise result
1725   - GNdarray result(ntheta, netrue, 2);
  1608 + // Loop over all true energies
  1609 + for (int ietrue = 0; ietrue < netrue; ++ietrue) {
1726 1610  
1727   - // Setup smoothing kernel
1728   - GFft fft_kernel = fft_smooth_kernel(netrue, sigma);
  1611 + // Compute base index
  1612 + int ibase = itheta * npix + ietrue;
1729 1613  
1730   - // Loop over all offset angles
1731   - for (int itheta = 0; itheta < ntheta; ++itheta) {
  1614 + // Extract array
  1615 + GNdarray array(nmigra);
  1616 + for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) {
  1617 + array(imigra) = m_edisp(m_inx_matrix, i);
  1618 + }
1732 1619  
1733   - // Allocate working arrays
1734   - GNdarray work_mean(netrue);
1735   - GNdarray work_rms(netrue);
1736   - GNdarray work_sum(netrue);
  1620 + // Store sum
  1621 + (*total)(ietrue) = sum(array);
1737 1622  
1738   - // Initialise mean and rms values
  1623 + // Get Gaussian mean and rms
1739 1624 double mean_value = 0.0;
1740 1625 double rms_value = 0.0;
  1626 + get_mean_rms(array, &mean_value, &rms_value);
1741 1627  
1742   - // Loop over all true energies
1743   - for (int ietrue = 0; ietrue < netrue; ++ietrue) {
1744   -
1745   - // Compute base index
1746   - int ibase = itheta * npix + ietrue;
1747   -
1748   - // Extract array
1749   - GNdarray array(nmigra);
1750   - for (int imigra = 0, i = ibase; imigra < nmigra; ++imigra, i += netrue) {
1751   - array(imigra) = m_edisp(m_inx_matrix, i);
1752   - }
1753   -
1754   - // Store sum (only results with positive sum will be kept)
1755   - work_sum(ietrue) = sum(array);
1756   -
1757   - // Compute single event value
1758   - double event_value = get_single_event_value(array);
1759   -
1760   - // Get Gaussian mean and rms. Use last mean and rms value in case
1761   - // that the array is empty
1762   - if (work_sum(ietrue) > 0) {
1763   - mean_value = 0.0;
1764   - rms_value = 0.0;
1765   - get_mean_rms(array, &mean_value, &rms_value);
1766   - }
1767   -
1768   - // Store mean and rms
1769   - work_mean(ietrue) = mean_value;
1770   - work_rms(ietrue) = rms_value;
1771   -
1772   - } // endfor: looped over all true energies
1773   -
1774   - // Pad front
1775   - for (int ietrue = 0; ietrue < netrue; ++ietrue) {
1776   - if (work_mean(ietrue) > 0.0) {
1777   - for (int i = 0; i < ietrue; ++i) {
1778   - work_mean(i) = work_mean(ietrue);
1779   - work_rms(i) = work_rms(ietrue);
1780   - }
1781   - break;
1782   - }
1783   - }
1784   -
1785   - // Pad back
1786   - for (int ietrue = netrue-1; ietrue >= 0; ietrue--) {
1787   - if (work_mean(ietrue) > 0.0) {
1788   - for (int i = ietrue; i < netrue; ++i) {
1789   - work_mean(i) = work_mean(ietrue);
1790   - work_rms(i) = work_rms(ietrue);
1791   - }
1792   - break;
1793   - }
1794   - }
1795   -
1796   - // Smooth mean
1797   - GFft fft_mean(work_mean);
1798   - GFft fft_mean_smooth = fft_mean * fft_kernel;
1799   - work_mean = fft_mean_smooth.backward();
  1628 + // Store mean and rms
  1629 + (*mean)(ietrue) = mean_value;
  1630 + (*rms)(ietrue) = rms_value;
1800 1631  
1801   - // Smooth rms
1802   - GFft fft_rms(work_rms);
1803   - GFft fft_rms_smooth = fft_rms * fft_kernel;
1804   - work_rms = fft_rms_smooth.backward();
  1632 + } // endfor: looped over all true energies
1805 1633  
1806   - // Store result
1807   - for (int ietrue = 0; ietrue < netrue; ++ietrue) {
1808   - if (work_sum(ietrue) > 0.0) {
1809   - result(itheta, ietrue, 0) = work_mean(ietrue);
1810   - result(itheta, ietrue, 1) = work_rms(ietrue);
1811   - }
1812   - }
1813   -
1814   - } // endif: looped over all offset angles
1815   -
1816   - // Return result
1817   - return result;
1818   -}
1819   -
1820   -
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));
  1634 + // Return
  1635 + return;
1860 1636 }
1861 1637  
1862 1638  
... ... @@ -1910,35 +1686,52 @@ void GCTAEdisp2D::get_mean_rms(const GNdarray&amp; array,
1910 1686  
1911 1687  
1912 1688 /***********************************************************************//**
1913   - * @brief Estimate the value of a single event
  1689 + * @brief Return Gaussian approximation of energy dispersion array
1914 1690 *
1915   - * @param[in] array Energy dispersion array.
1916   - * @return Value of a single event.
  1691 + * @param[in] mean Gaussian mean.
  1692 + * @param[in] rms Gaussian rms.
  1693 + * @param[in] total Gaussian total.
  1694 + * @return Gaussian approximation of energy dispersion array.
1917 1695 *
1918   - * Returns the minimum non-zero value in an array as an estimate of the value
1919   - * of a single event. If the array is empty the method returns zero.
  1696 + * Returns a Gaussian approximation of the energy dispersion array by
  1697 + * computing the mean migration value and its root mean square and by using
  1698 + * these values as the centre and the width of a Gaussian function. The
  1699 + * Gaussian function is normalized so that the sum of the output array is
  1700 + * unity.
1920 1701 ***************************************************************************/
1921   -double GCTAEdisp2D::get_single_event_value(const GNdarray& array) const
  1702 +GNdarray GCTAEdisp2D::gaussian_array(const double& mean,
  1703 + const double& rms,
  1704 + const double& total) const
1922 1705 {
1923   - // Initialise minimum value
1924   - double event_value = 1.0e30;
  1706 + // Initialise empty Gaussian array
  1707 + int nmigra = m_edisp.axis_bins(m_inx_migra);
  1708 + GNdarray gaussian(nmigra);
1925 1709  
1926   - // Get minimum non-zero value
1927   - for (int i = 0; i < array.size(); ++i) {
1928   - double value = array(i);
1929   - if ((value > 0.0) && (value < event_value)) {
1930   - event_value = value;
  1710 + // If the rms is valid then compute Gaussian
  1711 + if (rms > 0.0) {
  1712 +
  1713 + // Get reference to migration values
  1714 + const GNodeArray& migras = m_edisp.axis_nodes(m_inx_migra);
  1715 + int nmigra = migras.size();
  1716 +
  1717 + // Compute Gaussian
  1718 + double total_gauss = 0.0;
  1719 + for (int imigra = 0; imigra < nmigra; ++imigra) {
  1720 + double arg = (migras[imigra] - mean) / rms;
  1721 + double value = std::exp(-0.5*arg*arg);
  1722 + gaussian(imigra) = value;
  1723 + total_gauss += value;
1931 1724 }
1932   - }
1933 1725  
1934   - // If no minimum value was encountered then set single event value to
1935   - // zero
1936   - if (event_value == 1.0e30) {
1937   - event_value = 0.0;
1938   - }
  1726 + // Normalise Gaussian
  1727 + if (total_gauss > 0.0) {
  1728 + gaussian *= total / total_gauss;
  1729 + }
1939 1730  
1940   - // Return
1941   - return event_value;
  1731 + } // endif: computed Gaussian
  1732 +
  1733 + // Return Gaussian array
  1734 + return gaussian;
1942 1735 }
1943 1736  
1944 1737  
... ...