Commit 147c3f169b4e09e55c9c50341df023eb991fe6fa
1 parent
2b15414b
Settle energy dispersion kludge (#2403)
Showing
5 changed files
with
259 additions
and
458 deletions
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 | ... | ... |
NEWS
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& 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& 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 | ... | ... |