Commit 5fbb6c3ebc420786b47bafa922080533fc08b6c3
Merge branch 'devel' into 1948-implement-smoothbrokenpowerlaw
Showing
8 changed files
with
273 additions
and
22 deletions
ChangeLog
| 1 | -2017-03-18 | |
| 1 | +2017-03-22 | |
| 2 | 2 | |
| 3 | 3 | * Version 1.3.0 released |
| 4 | 4 | ========================= |
| 5 | 5 | |
| 6 | + Implement lazy loading of energy dispersion in GCTAEdisp2D (#1940) | |
| 6 | 7 | Add GCTARoi::contains method |
| 7 | 8 | Do no longer interpret a CTA calibration database as file path (#1969) |
| 8 | 9 | Add GCTAResponseIrf::rspname() method | ... | ... |
NEWS
| 1 | -New Features and Important Changes in GammaLib 1.3.0 | |
| 1 | +New Features and Important Changes in GammaLib 1.3.0.dev1 | |
| 2 | 2 | |
| 3 | -18 March 2017 | |
| 3 | +22 March 2017 | |
| 4 | 4 | |
| 5 | 5 | |
| 6 | 6 | 1. Introduction |
| ... | ... | @@ -24,6 +24,7 @@ The following classes have been renamed: |
| 24 | 24 | The following methods have been added: |
| 25 | 25 | - GCTAResponseIrf::rspname |
| 26 | 26 | - GCTARoi::contains |
| 27 | +- GCTAEdisp2D::fetch | |
| 27 | 28 | |
| 28 | 29 | The following methods have been removed: |
| 29 | 30 | - ... |
| ... | ... | @@ -141,6 +142,10 @@ GCTAResponseIrf::read() method (#1969). |
| 141 | 142 | Add GCTARoi::contains() method that checks whether an instrument direction |
| 142 | 143 | is contained within the region of interest. |
| 143 | 144 | |
| 145 | +Lazy loading of the energy dispersion in GCTAEdisp2D has been implemented by | |
| 146 | +adding a fetch() method to the class. Now the energy dispersion is only loaded | |
| 147 | +when needed, for example for function evaluation or Monte Carlo sampling (#1940). | |
| 148 | + | |
| 144 | 149 | |
| 145 | 150 | 21. COMPTEL interface |
| 146 | 151 | --------------------- | ... | ... |
inst/cta/include/GCTAEdisp2D.hpp
| 1 | 1 | /*************************************************************************** |
| 2 | 2 | * GCTAEdisp2D.hpp - CTA 2D energy dispersion class * |
| 3 | 3 | * ----------------------------------------------------------------------- * |
| 4 | - * copyright (C) 2015-2016 by Florent Forest * | |
| 4 | + * copyright (C) 2015-2017 by Florent Forest * | |
| 5 | 5 | * ----------------------------------------------------------------------- * |
| 6 | 6 | * * |
| 7 | 7 | * This program is free software: you can redistribute it and/or modify * |
| ... | ... | @@ -90,12 +90,14 @@ public: |
| 90 | 90 | std::string print(const GChatter& chatter = NORMAL) const; |
| 91 | 91 | |
| 92 | 92 | // Methods |
| 93 | + void fetch(void) const; | |
| 93 | 94 | const GCTAResponseTable& table(void) const; |
| 94 | 95 | void table(const GCTAResponseTable& table); |
| 95 | 96 | void read(const GFitsTable& table); |
| 96 | 97 | void write(GFitsBinTable& table) const; |
| 97 | 98 | void save(const GFilename& filename, |
| 98 | 99 | const bool& clobber = false) const; |
| 100 | + | |
| 99 | 101 | private: |
| 100 | 102 | // Methods |
| 101 | 103 | void init_members(void); |
| ... | ... | @@ -133,6 +135,7 @@ private: |
| 133 | 135 | // Members |
| 134 | 136 | mutable GFilename m_filename; //!< Name of Edisp response file |
| 135 | 137 | GCTAResponseTable m_edisp; //!< Edisp response table |
| 138 | + mutable bool m_fetched; //!< Signals that Edisp has been fetched | |
| 136 | 139 | int m_inx_etrue; //!< True energy index |
| 137 | 140 | int m_inx_migra; //!< Migration index |
| 138 | 141 | int m_inx_theta; //!< Theta index | ... | ... |
inst/cta/src/GCTAEdisp2D.cpp
| ... | ... | @@ -45,6 +45,7 @@ |
| 45 | 45 | #define G_READ "GCTAEdisp2D::read(GFitsTable&)" |
| 46 | 46 | #define G_MC "GCTAEdisp2D::mc(GRan&, double&, double&, double&, double&, "\ |
| 47 | 47 | "double&)" |
| 48 | +#define G_FETCH "GCTAEdisp2D::fetch()" | |
| 48 | 49 | |
| 49 | 50 | /* __ Macros _____________________________________________________________ */ |
| 50 | 51 | |
| ... | ... | @@ -191,6 +192,9 @@ double GCTAEdisp2D::operator()(const double& logEobs, |
| 191 | 192 | const double& zenith, |
| 192 | 193 | const double& azimuth) const |
| 193 | 194 | { |
| 195 | + // Make sure that energy dispersion is online | |
| 196 | + fetch(); | |
| 197 | + | |
| 194 | 198 | // Initalize edisp |
| 195 | 199 | double edisp = 0.0; |
| 196 | 200 | |
| ... | ... | @@ -407,6 +411,9 @@ void GCTAEdisp2D::read(const GFitsTable& table) |
| 407 | 411 | ***************************************************************************/ |
| 408 | 412 | void GCTAEdisp2D::write(GFitsBinTable& table) const |
| 409 | 413 | { |
| 414 | + // Make sure that energy dispersion is online | |
| 415 | + fetch(); | |
| 416 | + | |
| 410 | 417 | // Write background table |
| 411 | 418 | m_edisp.write(table); |
| 412 | 419 | |
| ... | ... | @@ -427,17 +434,8 @@ void GCTAEdisp2D::write(GFitsBinTable& table) const |
| 427 | 434 | ***************************************************************************/ |
| 428 | 435 | void GCTAEdisp2D::load(const GFilename& filename) |
| 429 | 436 | { |
| 430 | - // Open FITS file | |
| 431 | - GFits fits(filename); | |
| 432 | - | |
| 433 | - // Get energy dispersion table | |
| 434 | - const GFitsTable& table = *fits.table(filename.extname("ENERGY DISPERSION")); | |
| 435 | - | |
| 436 | - // Read energy dispersion from table | |
| 437 | - read(table); | |
| 438 | - | |
| 439 | - // Close FITS file | |
| 440 | - fits.close(); | |
| 437 | + // Clear object | |
| 438 | + clear(); | |
| 441 | 439 | |
| 442 | 440 | // Store filename |
| 443 | 441 | m_filename = filename; |
| ... | ... | @@ -521,6 +519,9 @@ GEnergy GCTAEdisp2D::mc(GRan& ran, |
| 521 | 519 | const double& zenith, |
| 522 | 520 | const double& azimuth) const |
| 523 | 521 | { |
| 522 | + // Make sure that energy dispersion is online | |
| 523 | + fetch(); | |
| 524 | + | |
| 524 | 525 | // Get boundaries for observed energy |
| 525 | 526 | GEbounds ebounds = ebounds_obs(logEsrc, theta, phi, zenith, azimuth); |
| 526 | 527 | double emin = ebounds.emin().log10TeV(); |
| ... | ... | @@ -582,6 +583,9 @@ GEbounds GCTAEdisp2D::ebounds_obs(const double& logEsrc, |
| 582 | 583 | const double& zenith, |
| 583 | 584 | const double& azimuth) const |
| 584 | 585 | { |
| 586 | + // Make sure that energy dispersion is online | |
| 587 | + fetch(); | |
| 588 | + | |
| 585 | 589 | // Compute only if parameters changed |
| 586 | 590 | if (!m_ebounds_obs_computed || theta != m_last_theta_obs) { |
| 587 | 591 | |
| ... | ... | @@ -644,6 +648,9 @@ GEbounds GCTAEdisp2D::ebounds_src(const double& logEobs, |
| 644 | 648 | const double& zenith, |
| 645 | 649 | const double& azimuth) const |
| 646 | 650 | { |
| 651 | + // Make sure that energy dispersion is online | |
| 652 | + fetch(); | |
| 653 | + | |
| 647 | 654 | // Compute only if parameters changed |
| 648 | 655 | if (!m_ebounds_src_computed || theta != m_last_theta_src) { |
| 649 | 656 | |
| ... | ... | @@ -687,9 +694,101 @@ GEbounds GCTAEdisp2D::ebounds_src(const double& logEobs, |
| 687 | 694 | |
| 688 | 695 | |
| 689 | 696 | /***********************************************************************//** |
| 697 | + * @brief Fetch energy dispersion | |
| 698 | + * | |
| 699 | + * @exception GException::file_error | |
| 700 | + * File not found. | |
| 701 | + * Unable to load energy dispersion. | |
| 702 | + * @exception GException::invalid_value | |
| 703 | + * No file name has been specified. | |
| 704 | + * | |
| 705 | + * Fetches the energy dispersion by reading it from a FITS file. This method | |
| 706 | + * does nothing if the energy dispersion is already loaded, if there is | |
| 707 | + * nothing to fetch, or if the m_filename member is empty. | |
| 708 | + * | |
| 709 | + * The method is thread save. The method checks whether the file from which | |
| 710 | + * the energy dispersion should be loaded actually exists. | |
| 711 | + ***************************************************************************/ | |
| 712 | +void GCTAEdisp2D::fetch(void) const | |
| 713 | +{ | |
| 714 | + // Continue only if energy dispersion has not yet been fetched | |
| 715 | + if (!m_fetched) { | |
| 716 | + | |
| 717 | + // Continue only if the file name is not empty | |
| 718 | + if (!m_filename.is_empty()) { | |
| 719 | + | |
| 720 | + // Throw an exception if the file does not exist | |
| 721 | + if (!m_filename.exists()) { | |
| 722 | + std::string msg = "File \""+m_filename+"\" not found. Cannot " | |
| 723 | + "fetch energy dispersion. Maybe the file has " | |
| 724 | + "been deleted in the meantime."; | |
| 725 | + GException::file_error(G_FETCH, msg); | |
| 726 | + } | |
| 727 | + | |
| 728 | + // Signal that energy dispersion will be fetched (has to come | |
| 729 | + // before reading since the ebounds_obs() and ebounds_src() methods | |
| 730 | + // will otherwise call the fetch() method recursively. | |
| 731 | + m_fetched = true; | |
| 732 | + | |
| 733 | + // Initialise exception flag | |
| 734 | + bool has_exception = false; | |
| 735 | + | |
| 736 | + // Load energy dispersion. Catch any exception. Put the code into | |
| 737 | + // a critical zone as it might be called from within a parallelized | |
| 738 | + // thread. | |
| 739 | + #pragma omp critical | |
| 740 | + { | |
| 741 | + try { | |
| 742 | + | |
| 743 | + | |
| 744 | + // Open FITS file | |
| 745 | + GFits fits(m_filename); | |
| 746 | + | |
| 747 | + // Initialise energy dispersion extension name | |
| 748 | + std::string extname = m_filename.extname("ENERGY DISPERSION"); | |
| 749 | + | |
| 750 | + // Get energy dispersion table | |
| 751 | + const GFitsTable& table = *fits.table(extname); | |
| 752 | + | |
| 753 | + // Read energy dispersion from table | |
| 754 | + const_cast<GCTAEdisp2D*>(this)->read(table); | |
| 755 | + | |
| 756 | + // Close FITS file | |
| 757 | + fits.close(); | |
| 758 | + | |
| 759 | + } | |
| 760 | + catch (...) { | |
| 761 | + has_exception = true; | |
| 762 | + } | |
| 763 | + } | |
| 764 | + | |
| 765 | + // Throw an exception if an exception has occured | |
| 766 | + if (has_exception) { | |
| 767 | + std::string msg = "Unable to load energy dispersion from " | |
| 768 | + "file \""+m_filename+"\"."; | |
| 769 | + throw GException::file_error(G_FETCH, msg); | |
| 770 | + } | |
| 771 | + | |
| 772 | + } // endif: filename was not empty | |
| 773 | + | |
| 774 | + // Throw an exception if the FITS file name is not known | |
| 775 | + else { | |
| 776 | + std::string msg = "Unable to fetch energy dispersion since no " | |
| 777 | + "filename is specified."; | |
| 778 | + throw GException::invalid_value(G_FETCH, msg); | |
| 779 | + } | |
| 780 | + | |
| 781 | + } // endif: energy dispersion had not yet been fetched | |
| 782 | + | |
| 783 | + // Return | |
| 784 | + return; | |
| 785 | +} | |
| 786 | + | |
| 787 | + | |
| 788 | +/***********************************************************************//** | |
| 690 | 789 | * @brief Print energy dispersion information |
| 691 | 790 | * |
| 692 | - * @param[in] chatter Chattiness (defaults to NORMAL). | |
| 791 | + * @param[in] chatter Chattiness. | |
| 693 | 792 | * @return String containing energy dispersion information. |
| 694 | 793 | ***************************************************************************/ |
| 695 | 794 | std::string GCTAEdisp2D::print(const GChatter& chatter) const |
| ... | ... | @@ -700,6 +799,9 @@ std::string GCTAEdisp2D::print(const GChatter& chatter) const |
| 700 | 799 | // Continue only if chatter is not silent |
| 701 | 800 | if (chatter != SILENT) { |
| 702 | 801 | |
| 802 | + // Make sure that energy dispersion is online | |
| 803 | + fetch(); | |
| 804 | + | |
| 703 | 805 | // Compute energy boundaries in TeV |
| 704 | 806 | double emin = m_edisp.axis_lo(m_inx_etrue,0); |
| 705 | 807 | double emax = m_edisp.axis_hi(m_inx_etrue, |
| ... | ... | @@ -754,6 +856,7 @@ void GCTAEdisp2D::init_members(void) |
| 754 | 856 | // Initialise members |
| 755 | 857 | m_filename.clear(); |
| 756 | 858 | m_edisp.clear(); |
| 859 | + m_fetched = false; | |
| 757 | 860 | m_inx_etrue = 0; |
| 758 | 861 | m_inx_migra = 1; |
| 759 | 862 | m_inx_theta = 2; |
| ... | ... | @@ -787,6 +890,7 @@ void GCTAEdisp2D::copy_members(const GCTAEdisp2D& edisp) |
| 787 | 890 | // Copy members |
| 788 | 891 | m_filename = edisp.m_filename; |
| 789 | 892 | m_edisp = edisp.m_edisp; |
| 893 | + m_fetched = edisp.m_fetched; | |
| 790 | 894 | m_inx_etrue = edisp.m_inx_etrue; |
| 791 | 895 | m_inx_migra = edisp.m_inx_migra; |
| 792 | 896 | m_inx_theta = edisp.m_inx_theta; | ... | ... |
inst/cta/src/GCTAObservation.cpp
| ... | ... | @@ -1441,7 +1441,7 @@ std::string GCTAObservation::print(const GChatter& chatter) const |
| 1441 | 1441 | if (m_events != NULL) { |
| 1442 | 1442 | result.append("\n"+m_events->print(reduced_chatter)); |
| 1443 | 1443 | } |
| 1444 | - | |
| 1444 | + | |
| 1445 | 1445 | } // endif: appended detailed information |
| 1446 | 1446 | |
| 1447 | 1447 | } // endif: chatter was not silent | ... | ... |
inst/cta/test/dev/test_background_integration.py
0 โ 100755
| 1 | +#! /usr/bin/env python | |
| 2 | +# ========================================================================== | |
| 3 | +# This energy integration of GCTABackground3D | |
| 4 | +# | |
| 5 | +# Copyright (C) 2017 Juergen Knoedlseder | |
| 6 | +# | |
| 7 | +# This program is free software: you can redistribute it and/or modify | |
| 8 | +# it under the terms of the GNU General Public License as published by | |
| 9 | +# the Free Software Foundation, either version 3 of the License, or | |
| 10 | +# (at your option) any later version. | |
| 11 | +# | |
| 12 | +# This program is distributed in the hope that it will be useful, | |
| 13 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 15 | +# GNU General Public License for more details. | |
| 16 | +# | |
| 17 | +# You should have received a copy of the GNU General Public License | |
| 18 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| 19 | +# | |
| 20 | +# ========================================================================== | |
| 21 | +import os | |
| 22 | +import math | |
| 23 | +import gammalib | |
| 24 | +import matplotlib.pyplot as plt | |
| 25 | + | |
| 26 | + | |
| 27 | +# ========================================= # | |
| 28 | +# Integrate using Trapezoid rule in log-log # | |
| 29 | +# ========================================= # | |
| 30 | +def integrate_trapezoid_loglog(bkg, logE1, logE2): | |
| 31 | + """ | |
| 32 | + Integrate using Trapezoid rule in log-log | |
| 33 | + """ | |
| 34 | + # Get function values | |
| 35 | + logf1 = math.log(bkg(logE1, 0.0, 0.0)) | |
| 36 | + logf2 = math.log(bkg(logE2, 0.0, 0.0)) | |
| 37 | + | |
| 38 | + # Compute ln of energy boundaries | |
| 39 | + lnE1 = logE1 * gammalib.ln10 | |
| 40 | + lnE2 = logE2 * gammalib.ln10 | |
| 41 | + | |
| 42 | + # Compute integral using Trapezoid rule in log-log | |
| 43 | + integral = (lnE2 - lnE1) * (math.exp(lnE1 + logf1) + | |
| 44 | + math.exp(lnE2 + logf2)) * 0.5 | |
| 45 | + | |
| 46 | + # Return integral | |
| 47 | + return integral | |
| 48 | + | |
| 49 | + | |
| 50 | +# ========================================= # | |
| 51 | +# Integrate using Trapezoid rule in lin-lin # | |
| 52 | +# ========================================= # | |
| 53 | +def integrate_trapezoid_linlin(bkg, logE1, logE2): | |
| 54 | + """ | |
| 55 | + Integrate using Trapezoid rule in lin-lin | |
| 56 | + """ | |
| 57 | + # Set number of energy bins | |
| 58 | + nbins = 1000 | |
| 59 | + | |
| 60 | + # Compute log energy bin size | |
| 61 | + dlogE = (logE2-logE1)/float(nbins-1) | |
| 62 | + | |
| 63 | + # Perform integration | |
| 64 | + integral = 0.0 | |
| 65 | + for i in range(nbins-1): | |
| 66 | + logE_low = logE1 + i*dlogE | |
| 67 | + logE_high = logE_low + dlogE | |
| 68 | + dE = math.pow(10.0, logE_high) - math.pow(10.0, logE_low) | |
| 69 | + integral += dE * (bkg(logE_low, 0.0, 0.0) + bkg(logE_high, 0.0, 0.0)) * 0.5 | |
| 70 | + | |
| 71 | + # Return integral | |
| 72 | + return integral | |
| 73 | + | |
| 74 | + | |
| 75 | +# =============== # | |
| 76 | +# Plot background # | |
| 77 | +# =============== # | |
| 78 | +def plot_background(bkg, emin=0.020, emax=120.0): | |
| 79 | + """ | |
| 80 | + Plot background | |
| 81 | + """ | |
| 82 | + # Generate energy vector | |
| 83 | + energies = gammalib.GEnergies(100, gammalib.GEnergy(emin,'TeV'), | |
| 84 | + gammalib.GEnergy(emax,'TeV')) | |
| 85 | + | |
| 86 | + # Set energy values | |
| 87 | + x = [energy.TeV() for energy in energies] | |
| 88 | + y = [bkg(energy.log10TeV(), 0.0, 0.0) for energy in energies] | |
| 89 | + | |
| 90 | + # Get node energies | |
| 91 | + i_eng = bkg.table().axis('ENERG') | |
| 92 | + e_nodes = bkg.table().axis_nodes(i_eng) | |
| 93 | + xo = [math.pow(10.0,logE) for logE in e_nodes] | |
| 94 | + yo = [bkg(logE, 0.0, 0.0) for logE in e_nodes] | |
| 95 | + | |
| 96 | + # Compute integrals | |
| 97 | + for i in range(len(xo)-1): | |
| 98 | + if yo[i] > 0 and yo[i+1] > 0: | |
| 99 | + integral_loglog = integrate_trapezoid_loglog(bkg, e_nodes[i], e_nodes[i+1]) | |
| 100 | + integral_linlin = integrate_trapezoid_linlin(bkg, e_nodes[i], e_nodes[i+1]) | |
| 101 | + print('%f-%f: %e %e (%f)' % (xo[i], xo[i+1], integral_loglog, | |
| 102 | + integral_linlin, | |
| 103 | + integral_loglog/integral_linlin)) | |
| 104 | + | |
| 105 | + # Create figure | |
| 106 | + plt.figure(1) | |
| 107 | + plt.title('CTA Background') | |
| 108 | + | |
| 109 | + # Plot background | |
| 110 | + plt.loglog(x, y, 'r-') | |
| 111 | + plt.loglog(xo, yo, 'ro') | |
| 112 | + | |
| 113 | + # Set axes | |
| 114 | + plt.xlabel("Energy (TeV)") | |
| 115 | + plt.ylabel("Background (events/s/MeV/sr)") | |
| 116 | + | |
| 117 | + # Show plot | |
| 118 | + plt.show() | |
| 119 | + | |
| 120 | + | |
| 121 | +# ======================== # | |
| 122 | +# Main routine entry point # | |
| 123 | +# ======================== # | |
| 124 | +if __name__ == '__main__': | |
| 125 | + | |
| 126 | + # Load background | |
| 127 | + bkg = gammalib.GCTABackground3D('../../caldb/data/cta/prod2/bcf/South_50h/irf_file.fits') | |
| 128 | + print(bkg) | |
| 129 | + | |
| 130 | + # Plot background | |
| 131 | + plot_background(bkg) | ... | ... |
src/numerics/GIntegral.cpp
| ... | ... | @@ -385,11 +385,18 @@ double GIntegral::romberg(std::vector<double> bounds, const int& order) |
| 385 | 385 | // Initialise integral |
| 386 | 386 | double value = 0.0; |
| 387 | 387 | |
| 388 | + // Initialise integration status information | |
| 389 | + int calls = 0; | |
| 390 | + | |
| 388 | 391 | // Add integral of all intervals |
| 389 | 392 | for (int i = 0; i < bounds.size()-1; ++i) { |
| 390 | 393 | value += romberg(bounds[i], bounds[i+1], order); |
| 394 | + calls += m_calls; | |
| 391 | 395 | } |
| 392 | 396 | |
| 397 | + // Set integration status information | |
| 398 | + m_calls = calls; | |
| 399 | + | |
| 393 | 400 | // Return value |
| 394 | 401 | return value; |
| 395 | 402 | } | ... | ... |
src/obs/GObservation.cpp
| ... | ... | @@ -352,17 +352,17 @@ double GObservation::model(const GModels& models, |
| 352 | 352 | * |
| 353 | 353 | * @returns Total number of observed events. |
| 354 | 354 | * |
| 355 | - * Returns the total number of observed events. | |
| 355 | + * Returns the total number of observed events. If the observation does not | |
| 356 | + * contain any events the method returns zero. | |
| 356 | 357 | ***************************************************************************/ |
| 357 | 358 | int GObservation::nobserved(void) const |
| 358 | 359 | { |
| 359 | 360 | // Initialise number of observed events |
| 360 | 361 | int nobserved = 0; |
| 361 | 362 | |
| 362 | - // Extract number of observed events from event contained | |
| 363 | - const GEvents* events = this->events(); | |
| 364 | - if (events != NULL) { | |
| 365 | - nobserved = events->number(); | |
| 363 | + // Extract number of observed events | |
| 364 | + if (m_events != NULL) { | |
| 365 | + nobserved = m_events->number(); | |
| 366 | 366 | } |
| 367 | 367 | |
| 368 | 368 | // Return number of observed events | ... | ... |