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 | * Bug fix version 1.5.1 released | 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 | Prevent unintentional deallocation of arguments (#2340). | 7 | Prevent unintentional deallocation of arguments (#2340). |
| 8 | Correct sky map smoothing. | 8 | Correct sky map smoothing. |
| 9 | 9 |
NEWS
| 1 | New Features and Important Changes in GammaLib 1.5.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 | 1. Introduction | 6 | 1. Introduction |
| @@ -149,9 +149,7 @@ None | @@ -149,9 +149,7 @@ None | ||
| 149 | 20. CTA interface | 149 | 20. CTA interface |
| 150 | ----------------- | 150 | ----------------- |
| 151 | Add a kludge to handle the noisy energy dispersion that is shipped with the 1DC | 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 | The GCTAObservation::response() and GCTAOnOffObservation::response() methods | 154 | The GCTAObservation::response() and GCTAOnOffObservation::response() methods |
| 157 | now only deallocate the previous content if the reference to the argument | 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,6 +10,8 @@ GammaLib 1.5.1 is a bug fix release for GammaLib release 1.5. | ||
| 10 | 10 | ||
| 11 | The following bugs were fixed: | 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 | * [`2342 <https://cta-redmine.irap.omp.eu/issues/2342>`_] - | 15 | * [`2342 <https://cta-redmine.irap.omp.eu/issues/2342>`_] - |
| 14 | Add kludge to handle noisy CTA 2D energy dispersion | 16 | Add kludge to handle noisy CTA 2D energy dispersion |
| 15 | * [`2340 <https://cta-redmine.irap.omp.eu/issues/2340>`_] - | 17 | * [`2340 <https://cta-redmine.irap.omp.eu/issues/2340>`_] - |
inst/cta/include/GCTAEdisp2D.hpp
| @@ -126,15 +126,23 @@ private: | @@ -126,15 +126,23 @@ private: | ||
| 126 | void normalize_table(void); | 126 | void normalize_table(void); |
| 127 | 127 | ||
| 128 | // Kludge | 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 | void get_mean_rms(const GNdarray& array, double* mean, double* rms) const; | 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 | // Protected classes | 147 | // Protected classes |
| 140 | class edisp_kern : public GFunction { | 148 | class edisp_kern : public GFunction { |
inst/cta/src/GCTAEdisp2D.cpp
| @@ -1200,9 +1200,7 @@ void GCTAEdisp2D::set_table(void) | @@ -1200,9 +1200,7 @@ void GCTAEdisp2D::set_table(void) | ||
| 1200 | 1200 | ||
| 1201 | // Smooth energy dispersion table | 1201 | // Smooth energy dispersion table |
| 1202 | #if defined(G_SMOOTH_EDISP_KLUDGE) | 1202 | #if defined(G_SMOOTH_EDISP_KLUDGE) |
| 1203 | - denoise_table(10.0); | ||
| 1204 | - clean_table(15); | ||
| 1205 | - clip_table(0.001); | 1203 | + smooth_table(); |
| 1206 | #endif | 1204 | #endif |
| 1207 | 1205 | ||
| 1208 | // Normalize energy dispersion table | 1206 | // Normalize energy dispersion table |
| @@ -1362,166 +1360,80 @@ void GCTAEdisp2D::normalize_table(void) | @@ -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 | // Get axes dimensions | 1372 | // Get axes dimensions |
| 1386 | int netrue = m_edisp.axis_bins(m_inx_etrue); | 1373 | int netrue = m_edisp.axis_bins(m_inx_etrue); |
| 1387 | int nmigra = m_edisp.axis_bins(m_inx_migra); | 1374 | int nmigra = m_edisp.axis_bins(m_inx_migra); |
| 1388 | int ntheta = m_edisp.axis_bins(m_inx_theta); | 1375 | int ntheta = m_edisp.axis_bins(m_inx_theta); |
| 1376 | + int npix = netrue * nmigra; | ||
| 1389 | 1377 | ||
| 1390 | // Loop over all offset angles | 1378 | // Loop over all offset angles |
| 1391 | for (int itheta = 0; itheta < ntheta; ++itheta) { | 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 | else { | 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 | m_edisp(m_inx_matrix, i) = 0.0; | 1427 | m_edisp(m_inx_matrix, i) = 0.0; |
| 1519 | } | 1428 | } |
| 1520 | } | 1429 | } |
| 1521 | 1430 | ||
| 1522 | } // endfor: looped over true energies | 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 | // Return | 1438 | // Return |
| 1527 | return; | 1439 | return; |
| @@ -1529,334 +1441,198 @@ void GCTAEdisp2D::clip_table(const double& threshold) | @@ -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 | // Get axes dimensions | 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 | double mean_value = 0.0; | 1624 | double mean_value = 0.0; |
| 1740 | double rms_value = 0.0; | 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,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 |