inst/cta/src/GCTAOnOffObservation.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *          GCTAOnOffObservation.cpp - CTA On/Off observation class        *
00003  * ----------------------------------------------------------------------- *
00004  *  copyright (C) 2013-2016 by Chia-Chun Lu & Christoph Deil               *
00005  * ----------------------------------------------------------------------- *
00006  *                                                                         *
00007  *  This program is free software: you can redistribute it and/or modify   *
00008  *  it under the terms of the GNU General Public License as published by   *
00009  *  the Free Software Foundation, either version 3 of the License, or      *
00010  *  (at your option) any later version.                                    *
00011  *                                                                         *
00012  *  This program is distributed in the hope that it will be useful,        *
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
00015  *  GNU General Public License for more details.                           *
00016  *                                                                         *
00017  *  You should have received a copy of the GNU General Public License      *
00018  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
00019  *                                                                         *
00020  ***************************************************************************/
00021 /**
00022  * @file GCTAOnOffObservation.cpp
00023  * @brief CTA On/Off observation class implementation
00024  * @author Chia-Chun Lu & Christoph Deil
00025  */
00026 
00027 /* __ Includes ___________________________________________________________ */
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 #include <typeinfo>
00032 #include "GObservationRegistry.hpp"
00033 #include "GTools.hpp"
00034 #include "GModels.hpp"
00035 #include "GModelSky.hpp"
00036 #include "GModelSpatial.hpp"
00037 #include "GModelSpectral.hpp"
00038 #include "GModelTemporal.hpp"
00039 #include "GSkyRegionCircle.hpp"
00040 #include "GOptimizerPars.hpp"
00041 #include "GCTAObservation.hpp"
00042 #include "GCTAEventAtom.hpp"
00043 #include "GCTAEventCube.hpp"
00044 #include "GCTAResponseIrf.hpp"
00045 #include "GCTACubeBackground.hpp"
00046 #include "GCTAModelIrfBackground.hpp"
00047 #include "GCTAOnOffObservation.hpp"
00048 
00049 /* __ OpenMP section _____________________________________________________ */
00050 #ifdef _OPENMP
00051 #include <omp.h>
00052 #endif
00053 
00054 /* __ Globals ____________________________________________________________ */
00055 const GCTAOnOffObservation g_onoff_obs_cta_seed;
00056 const GObservationRegistry g_onoff_obs_cta_registry(&g_onoff_obs_cta_seed);
00057 
00058 /* __ Method name definitions ____________________________________________ */
00059 #define G_RESPONSE_SET           "GCTAOnOffObservation::response(GResponse&)"
00060 #define G_RESPONSE_GET                     "GCTAOnOffObservation::response()"
00061 #define G_WRITE                   "GCTAOnOffObservation::write(GXmlElement&)"
00062 #define G_READ                     "GCTAOnOffObservation::read(GXmlElement&)"
00063 #define G_LIKELIHOOD            "GCTAOnOffObservation::likelihood(GModels&, "\
00064                                           "GOptimizerPars&, GMatrixSparse&, "\
00065                                                 "GVector&, double&, double&)"
00066 #define G_SET                   "GCTAOnOffObservation::set(GCTAObservation&)"
00067 #define G_COMPUTE_ARF   "GCTAOnOffObservation::compute_arf(GCTAObservation&)"
00068 #define G_COMPUTE_BGD   "GCTAOnOffObservation::compute_bgd(GCTAObservation&)"
00069 #define G_COMPUTE_ALPHA                "GCTAOnOffObservation::compute_alpha("\
00070                                                           "GCTAObservation&)"
00071 #define G_COMPUTE_RMF   "GCTAOnOffObservation::compute_rmf(GCTAObservation&)"
00072 
00073 /* __ Constants __________________________________________________________ */
00074 const double minmod = 1.0e-100;                      //!< Minimum model value
00075 const double minerr = 1.0e-100;                //!< Minimum statistical error
00076 
00077 /* __ Macros _____________________________________________________________ */
00078 
00079 /* __ Coding definitions _________________________________________________ */
00080 
00081 /* __ Debug definitions __________________________________________________ */
00082 //#define G_LIKELIHOOD_DEBUG                //!< Debug likelihood computation
00083 
00084 
00085 /*==========================================================================
00086  =                                                                         =
00087  =                         Constructors/destructors                        =
00088  =                                                                         =
00089  ==========================================================================*/
00090 
00091 /***********************************************************************//**
00092  * @brief Void constructor
00093  *
00094  * Constructs empty On/Off observation.
00095  ***************************************************************************/
00096 GCTAOnOffObservation::GCTAOnOffObservation(void) : GObservation()
00097 {
00098     // Initialise private members
00099     init_members();
00100   
00101     // Return
00102     return;
00103 }
00104 
00105 
00106 /***********************************************************************//**
00107  * @brief Copy constructor
00108  *
00109  * @param[in] obs On/Off observation.
00110  ***************************************************************************/
00111 GCTAOnOffObservation::GCTAOnOffObservation(const GCTAOnOffObservation& obs) :
00112                       GObservation(obs)
00113 { 
00114     // Initialise private
00115     init_members();
00116 
00117     // Copy members
00118     copy_members(obs);
00119 
00120     // Return
00121     return;
00122 }
00123 
00124 
00125 /***********************************************************************//**
00126  * @brief CTA observation constructor
00127  *
00128  * @param[in] obs CTA observation.
00129  * @param[in] etrue True energy boundaries.
00130  * @param[in] ereco Reconstructed energy boundaries.
00131  * @param[in] on On regions.
00132  * @param[in] off Off regions.
00133  *
00134  * Constructs On/Off observation a CTA observation by filling the On and Off
00135  * spectra and computing the Auxiliary Response File (ARF) and Redistribution
00136  * Matrix File (RMF). The method requires the specification of the true and
00137  * reconstructed energy boundaries, as well as the definition of On and Off
00138  * regions.
00139  ***************************************************************************/
00140 GCTAOnOffObservation::GCTAOnOffObservation(const GCTAObservation& obs,
00141                                            const GEbounds&        etrue,
00142                                            const GEbounds&        ereco,
00143                                            const GSkyRegions&     on,
00144                                            const GSkyRegions&     off) : 
00145                       GObservation()
00146 {
00147     // Initialise private
00148     init_members();
00149 
00150     // Initialise spectra
00151     m_on_spec  = GPha(ereco);
00152     m_off_spec = GPha(ereco);
00153 
00154     // Initialise response information
00155     m_arf = GArf(ereco);
00156     m_rmf = GRmf(etrue, ereco);
00157 
00158     // Store regions
00159     m_on_regions  = on;
00160     m_off_regions = off;
00161 
00162     // Set On/Off observation from CTA observation
00163     set(obs);
00164 
00165     // Return
00166     return;
00167 }
00168 
00169 
00170 /***********************************************************************//**
00171  * @brief Destructor
00172  ***************************************************************************/
00173 GCTAOnOffObservation::~GCTAOnOffObservation(void)
00174 {
00175     // Free members
00176     free_members();
00177 
00178     // Return
00179     return;
00180 }
00181 
00182 
00183 /*==========================================================================
00184  =                                                                         =
00185  =                                Operators                                =
00186  =                                                                         =
00187  ==========================================================================*/
00188 
00189 /***********************************************************************//**
00190  * @brief Assignment operator
00191  *
00192  * @param[in] obs On/Off observation.
00193  * @return On/Off observation.
00194  *
00195  * Assigns one On/Off observation to another On/Off observation object.
00196  ***************************************************************************/
00197 GCTAOnOffObservation& GCTAOnOffObservation::operator=(const GCTAOnOffObservation& obs)
00198 { 
00199     // Execute only if object is not identical
00200     if (this != &obs) {
00201 
00202         // Copy base class members
00203         this->GObservation::operator=(obs);
00204 
00205         // Free members
00206         free_members();
00207 
00208         // Initialise private members for clean destruction
00209         init_members();
00210 
00211         // Copy members
00212         copy_members(obs);
00213 
00214     } // endif: object was not identical
00215   
00216     // Return
00217     return *this;
00218 }
00219 
00220 
00221 /*==========================================================================
00222  =                                                                         =
00223  =                             Public methods                              =
00224  =                                                                         =
00225  ==========================================================================*/
00226 
00227 /***********************************************************************//**
00228  * @brief Clear instance
00229  *
00230  * Clears the On/Off observation. All class members will be set to the
00231  * initial state. Any information that was present in the object before will
00232  * be lost.
00233  ***************************************************************************/
00234 void GCTAOnOffObservation::clear(void)
00235 {
00236     // Free class members
00237     free_members();
00238 
00239     // Initialise members
00240     init_members();
00241 
00242     // Return
00243     return;
00244 }
00245 
00246 
00247 /***********************************************************************//**
00248  * @brief Clone instance
00249  *
00250  * @return Pointer to deep copy of On/Off observation.
00251  *
00252  * Returns a pointer to a deep copy of an On/Off observation.
00253  **************************************************************************/
00254 GCTAOnOffObservation* GCTAOnOffObservation::clone(void) const
00255 {
00256     return new GCTAOnOffObservation(*this);
00257 }
00258 
00259 
00260 /***********************************************************************//**
00261  * @brief Set response function
00262  *
00263  * @param[in] rsp Response function.
00264  *
00265  * @exception GException::invalid_argument
00266  *            Invalid response class specified.
00267  *
00268  * Sets the response function for the On/Off observation.
00269  ***************************************************************************/
00270 void GCTAOnOffObservation::response(const GResponse& rsp)
00271 {
00272     // Cast response dynamically
00273     const GCTAResponse* ptr = dynamic_cast<const GCTAResponse*>(&rsp);
00274 
00275     // Throw exception if response is not of correct type
00276     if (ptr == NULL) {
00277         std::string cls = std::string(typeid(&rsp).name());
00278         std::string msg = "Invalid response type \""+cls+"\" provided on "
00279                           "input. Please specify a \"GCTAResponse\" "
00280                           "as argument.";
00281         throw GException::invalid_argument(G_RESPONSE_SET, msg);
00282     }
00283 
00284     // Free response
00285     if (m_response != NULL) delete m_response;
00286 
00287     // Clone response function
00288     m_response = ptr->clone();
00289 
00290     // Return
00291     return;
00292 }
00293 
00294 
00295 /***********************************************************************//**
00296  * @brief Return pointer to CTA response function
00297  *
00298  * @return Pointer to CTA response function.
00299  *
00300  * @exception GException::invalid_value
00301  *            No valid response found in CTA observation.
00302  *
00303  * Returns a pointer to the CTA response function. An exception is thrown if
00304  * the pointer is not valid, hence the user does not need to verify the
00305  * validity of the pointer.
00306  ***************************************************************************/
00307 const GCTAResponse* GCTAOnOffObservation::response(void) const
00308 {
00309     // Throw an exception if the response pointer is not valid
00310     if (m_response == NULL) {
00311         std::string msg = "No valid response function found in CTA On/Off "
00312                           "observation.\n";
00313         throw GException::invalid_value(G_RESPONSE_GET, msg);
00314     }
00315 
00316     // Return pointer
00317     return m_response;
00318 }
00319 
00320 
00321 /***********************************************************************//**
00322  * @brief Read On/Off observation from an XML element
00323  *
00324  * @param[in] xml XML element.
00325  *
00326  * Reads information for an On/Off observation from an XML element. The
00327  * expected format of the XML element is
00328  *
00329  *     <observation name="..." id="..." instrument="...">
00330  *       <parameter name="Pha_on"      file="..."/>
00331  *       <parameter name="Pha_off"     file="..."/>
00332  *       <parameter name="Regions_on"  file="..."/>
00333  *       <parameter name="Regions_off" file="..."/>
00334  *       <parameter name="Arf"         file="..."/>
00335  *       <parameter name="Rmf"         file="..."/>
00336  *     </observation>
00337  *
00338  ***************************************************************************/
00339 void GCTAOnOffObservation::read(const GXmlElement& xml)
00340 {
00341     // clean object
00342     clear();
00343 
00344     // Extract instrument name
00345     m_instrument = xml.attribute("instrument");
00346 
00347     // Get file names
00348     std::string pha_on  = gammalib::xml_get_attr(G_READ, xml, "Pha_on",      "file");
00349     std::string pha_off = gammalib::xml_get_attr(G_READ, xml, "Pha_off",     "file");
00350     std::string reg_on  = gammalib::xml_get_attr(G_READ, xml, "Regions_on",  "file");
00351     std::string reg_off = gammalib::xml_get_attr(G_READ, xml, "Regions_off", "file");
00352     std::string arf     = gammalib::xml_get_attr(G_READ, xml, "Arf",         "file");
00353     std::string rmf     = gammalib::xml_get_attr(G_READ, xml, "Rmf",         "file");
00354 
00355     // Expand file names
00356     pha_on  = gammalib::xml_file_expand(xml, pha_on);
00357     pha_off = gammalib::xml_file_expand(xml, pha_off);
00358     reg_on  = gammalib::xml_file_expand(xml, reg_on);
00359     reg_off = gammalib::xml_file_expand(xml, reg_off);
00360     arf     = gammalib::xml_file_expand(xml, arf);
00361     rmf     = gammalib::xml_file_expand(xml, rmf);
00362 
00363     // Load files
00364     m_on_spec.load(pha_on);
00365     m_off_spec.load(pha_off);
00366     m_on_regions.load(reg_on);
00367     m_off_regions.load(reg_off);
00368     m_arf.load(arf);
00369     m_rmf.load(rmf);
00370 
00371     // Return
00372     return;
00373 }
00374 
00375 
00376 /***********************************************************************//**
00377  * @brief write observation to an xml element
00378  *
00379  * @param[in] xml XML element.
00380  *
00381  * Writes information for an On/Off observation into an XML element. The
00382  * expected format of the XML element is
00383  *
00384  *     <observation name="..." id="..." instrument="...">
00385  *       <parameter name="Pha_on"      file="..."/>
00386  *       <parameter name="Pha_off"     file="..."/>
00387  *       <parameter name="Regions_on"  file="..."/>
00388  *       <parameter name="Regions_off" file="..."/>
00389  *       <parameter name="Arf"         file="..."/>
00390  *       <parameter name="Rmf"         file="..."/>
00391  *     </observation>
00392  *
00393  * The actual files described in the XML elements are not written.
00394  ***************************************************************************/
00395 void GCTAOnOffObservation::write(GXmlElement& xml) const
00396 {
00397     // Allocate XML element pointer
00398     GXmlElement* par;
00399 
00400     // Set Pha_on parameter
00401     par = gammalib::xml_need_par(G_WRITE, xml, "Pha_on");
00402     par->attribute("file", gammalib::xml_file_reduce(xml, m_on_spec.filename()));
00403 
00404     // Set Pha_off parameter
00405     par = gammalib::xml_need_par(G_WRITE, xml, "Pha_off");
00406     par->attribute("file", gammalib::xml_file_reduce(xml, m_off_spec.filename()));
00407 
00408     // Set Regions_on parameter
00409     par = gammalib::xml_need_par(G_WRITE, xml, "Regions_on");
00410     par->attribute("file", gammalib::xml_file_reduce(xml, m_on_regions.filename()));
00411 
00412     // Set Regions_off parameter
00413     par = gammalib::xml_need_par(G_WRITE, xml, "Regions_off");
00414     par->attribute("file", gammalib::xml_file_reduce(xml, m_off_regions.filename()));
00415 
00416     // Set Arf parameter
00417     par = gammalib::xml_need_par(G_WRITE, xml, "Arf");
00418     par->attribute("file", gammalib::xml_file_reduce(xml, m_arf.filename()));
00419 
00420     // Set Rmf parameter
00421     par = gammalib::xml_need_par(G_WRITE, xml, "Rmf");
00422     par->attribute("file", gammalib::xml_file_reduce(xml, m_rmf.filename()));
00423 
00424     // Return
00425     return;
00426 }
00427 
00428 
00429 /***********************************************************************//**
00430  * @brief Evaluate log-likelihood function for On/Off analysis
00431  *
00432  * @param[in] models Models.
00433  * @param[in,out] gradient Pointer to gradients.
00434  * @param[in,out] curvature Pointer to curvature matrix.
00435  * @param[in,out] npred Pointer to Npred value.
00436  * @return Log-likelihood value.
00437  *
00438  * @exception GException::invalid_value
00439  *            There are no model parameters.
00440  *
00441  * Computes the log-likelihood value for the On/Off observation. The
00442  * method loops over the energy bins to update the function value, its
00443  * derivatives and the curvature matrix. The number of On counts
00444  * \f$N_{\rm on}\f$ and Off counts \f$N_{\rm off}\f$ are taken from the
00445  * On and Off spectra, the expected number of gamma-ray events
00446  * \f$N_{\gamma}\f$ and background events \f$N_{\rm bgd}\f$ are
00447  * computed from the spectral models of the relevant components in the
00448  * model container (spatial and temporal components are ignored so far).
00449  * See the N_gamma() and N_bgd() methods for details about the model
00450  * computations.
00451  *
00452  * The log-likelihood is given by
00453  *
00454  * \f[
00455  *    \ln L = \sum_i \ln L_i
00456  * \f]
00457  *
00458  * where the sum is taken over all energy bins \f$i\f$ and
00459  *
00460  * \f[
00461  *    \ln L_i = - N_{\rm on}  \ln N_{\rm pred} + N_{\rm pred}
00462  *              - N_{\rm off} \ln N_{\rm bgd}  + N_{\rm bgd}
00463  * \f]
00464  *
00465  * with
00466  *
00467  * \f[
00468  *    N_{\rm pred} = N_{\gamma} + \alpha N_{\rm bgd}
00469  * \f]
00470  *
00471  * being the total number of predicted events for an energy bin in the On
00472  * region,
00473  * \f$N_{\rm on}\f$ is the total number of observed events for an energy
00474  * bin in the On region,
00475  * \f$N_{\rm off}\f$ is the total number of observed events for an energy
00476  * bin in the Off region, and
00477  * \f$N_{\rm bgd}\f$ is the predicted number of background events for an
00478  * energy bin in the Off region.
00479  *
00480  * The log-likelihood gradient with respect to sky model parameters
00481  * \f$p_{\rm sky}\f$ is given by
00482  *
00483  * \f[
00484  *    \left( \frac{\partial \ln L_i}{\partial p_{\rm sky}} \right) =
00485  *    \left( 1 - \frac{N_{\rm on}}{N_{\rm pred}} \right)
00486  *    \left( \frac{\partial N_{\gamma}}{\partial p_{\rm sky}} \right)
00487  * \f]
00488  *
00489  * and with respect to background model parameters \f$p_{\rm bgd}\f$ is
00490  * given by
00491  *
00492  * \f[
00493  *    \left( \frac{\partial \ln L_i}{\partial p_{\rm bgd}} \right) =
00494  *    \left( 1 + \alpha - \frac{N_{\rm off}}{N_{\rm bgd}} -
00495  *           \frac{\alpha N_{\rm on}}{N_{\rm pred}} \right)
00496  *    \left( \frac{\partial N_{\rm bgd}}{\partial p_{\rm bgd}} \right)
00497  * \f]
00498  *
00499  * The curvature matrix elements are given by
00500  *
00501  * \f[
00502  *    \left( \frac{\partial^2 \ln L_i}{\partial^2 p_{\rm sky}} \right) =
00503  *    \left( \frac{N_{\rm on}}{N_{\rm pred}^2} \right)
00504  *    \left( \frac{\partial N_{\gamma}}{\partial p_{\rm sky}} \right)^2
00505  * \f]
00506  *
00507  * \f[
00508  *    \left( \frac{\partial^2 \ln L_i}{\partial p_{\rm sky}
00509  *                                     \partial p_{\rm bgd}} \right) =
00510  *    \left( \frac{\alpha N_{\rm on}}{N_{\rm pred}^2} \right)
00511  *    \left( \frac{\partial N_{\gamma}}{\partial p_{\rm sky}} \right)
00512  *    \left( \frac{\partial N_{\rm bgd}}{\partial p_{\rm bgd}} \right)
00513  * \f]
00514  *
00515  * \f[
00516  *    \left( \frac{\partial^2 \ln L_i}{\partial p_{\rm bgd}
00517  *                                     \partial p_{\rm sky}} \right) =
00518  *    \left( \frac{\alpha N_{\rm on}}{N_{\rm pred}^2} \right)
00519  *    \left( \frac{\partial N_{\rm bgd}}{\partial p_{\rm bgd}} \right)
00520  *    \left( \frac{\partial N_{\gamma}}{\partial p_{\rm sky}} \right)
00521  * \f]
00522  *
00523  * \f[
00524  *    \left( \frac{\partial^2 \ln L_i}{\partial^2 p_{\rm bgd}} \right) =
00525  *    \left( \frac{N_{\rm off}}{N_{\rm bgd}^2} +
00526  *           \frac{\alpha^2 N_{\rm on}}{N_{\rm pred}^2} \right)
00527  *    \left( \frac{\partial N_{\rm bgd}}{\partial p_{\rm bgd}} \right)^2
00528  * \f]
00529  ***********************************************************************/
00530 double GCTAOnOffObservation::likelihood(const GModels& models,
00531                                         GVector*       gradient,
00532                                         GMatrixSparse* curvature,
00533                                         double*        npred) const
00534 {
00535     // Timing measurement
00536     #if defined(G_LIKELIHOOD_DEBUG)
00537     #ifdef _OPENMP
00538     double t_start = omp_get_wtime();
00539     #else
00540     clock_t t_start = clock();
00541     #endif
00542     #endif
00543     
00544     // Initialise statistics
00545     #if defined(G_LIKELIHOOD_DEBUG)
00546     int    n_bins        = m_on_spec.size();
00547     int    n_used        = 0;
00548     int    n_small_model = 0;
00549     int    n_zero_data   = 0;
00550     double sum_data      = 0.0;
00551     double sum_model     = 0.0;
00552     double init_npred    = *npred;
00553     #endif
00554     
00555     // Initialise log-likelihood value
00556     double value = 0.0;
00557     
00558     // Get number of model parameters in model container
00559     int npars = models.npars();
00560     
00561     // Create model gradient vectors for sky and background parameters
00562     GVector sky_grad(npars);
00563     GVector bgd_grad(npars);
00564 
00565     // Allocate working array
00566     GVector colvar(npars);
00567 
00568     // Get reference to alpha parameters
00569     const std::vector<double>& alpha = m_arf["ALPHA"];
00570     
00571     // Check that there is at least one parameter
00572     if (npars > 0) {
00573         
00574         // Loop over all energy bins
00575         for (int i = 0; i < m_on_spec.size(); ++i) {
00576                 
00577             // Reinitialize working arrays
00578             for (int j = 0; j < npars; ++j) {
00579                 sky_grad[j] = 0.0;
00580                 bgd_grad[j] = 0.0;
00581             }
00582           
00583             // Get number of On and Off counts
00584             double non  = m_on_spec[i];
00585             double noff = m_off_spec[i];
00586             
00587             // Get number of gamma and background events (and corresponding
00588             // spectral model gradients)
00589             double ngam = N_gamma(models, i, &sky_grad);
00590             double nbgd = N_bgd(models, i, &bgd_grad);
00591 
00592             // Skip bin if model is too small (avoids -Inf or NaN gradients)
00593             double nonpred = ngam + alpha[i] * nbgd;
00594             if ((nbgd <= minmod) || (nonpred <= minmod)) {
00595                 #if defined(G_LIKELIHOOD_DEBUG)
00596                 n_small_model++;
00597                 #endif
00598                 continue;
00599             }
00600           
00601             // Now we have all predicted gamma and background events for
00602             // current energy bin. Update the log(likelihood) and predicted
00603             // number of events
00604             value  += -non * log(nonpred) + nonpred - noff * log(nbgd) + nbgd;
00605             *npred += nonpred;
00606           
00607             // Update statistics
00608             #if defined(G_LIKELIHOOD_DEBUG)
00609             n_used++;
00610             sum_data  += non;
00611             sum_model += nonpred;
00612             #endif
00613           
00614             // Fill derivatives
00615             double fa         = non/nonpred;
00616             double fb         = fa/nonpred;
00617             double fc         = alpha[i] * fb;
00618             double fd         = fc * alpha[i] + noff/(nbgd*nbgd);
00619             double sky_factor = 1.0 - fa;
00620             double bgd_factor = 1.0 + alpha[i] - alpha[i] * fa - noff/nbgd;
00621 
00622             // Loop over all parameters
00623             for (int j = 0; j < npars; ++j) {
00624             
00625                 // If spectral model for sky component is non-zero and
00626                 // non-infinite then handle sky component gradients and
00627                 // second derivatives including at least a sky component ...
00628                 if (sky_grad[j] != 0.0  && !gammalib::is_infinite(sky_grad[j])) {
00629                 
00630                     // Gradient
00631                     (*gradient)[j] += sky_factor * sky_grad[j];
00632                 
00633                     // Hessian (from first-order derivatives only)
00634                     for (int k = 0; k < npars; ++k) {
00635                 
00636                         // If spectral model for sky component is non-zero and
00637                         // non-infinite then we have the curvature element
00638                         // of a sky component
00639                         if (sky_grad[k] != 0.0  &&
00640                             !gammalib::is_infinite(sky_grad[k])) {
00641                             colvar[k] = sky_grad[j] * sky_grad[k] * fb;
00642                         }
00643                     
00644                         // ... else if spectral model for background component
00645                         // is non-zero and non-infinite then we have the mixed
00646                         // curvature element between a sky and a background
00647                         // component
00648                         else if (bgd_grad[k] != 0.0  &&
00649                                  !gammalib::is_infinite(bgd_grad[k])) {
00650                             colvar[k] = sky_grad[j] * bgd_grad[k] * fc;
00651                         }
00652                         
00653                         // ...else neither sky nor background
00654                         else {
00655                             colvar[k] = 0.0;
00656                         }
00657                     
00658                     } // endfor: Hessian computation
00659                 
00660                     // Update matrix
00661                     curvature->add_to_column(j, colvar);
00662 
00663                 } // endif: spectral model is non-zero and non-infinite
00664             
00665                 // ... otherwise if spectral model for background component is
00666                 // non-zero and non-infinite then handle background component
00667                 // gradients and second derivatives including at least a
00668                 // background component
00669                 else if (bgd_grad[j] != 0.0  &&
00670                          !gammalib::is_infinite(bgd_grad[j])) {
00671                 
00672                     // Gradient
00673                     (*gradient)[j] += bgd_factor * bgd_grad[j];
00674                 
00675                     // Hessian (from first-order derivatives only)
00676                     for (int k = 0; k < npars; ++k) {
00677                     
00678                         // If spectral model for sky component is non-zero and
00679                         // non-infinite then we have the mixed curvature element
00680                         // between a sky and a background component
00681                         if (sky_grad[k] != 0.0  &&
00682                             !gammalib::is_infinite(sky_grad[k])) {
00683                             colvar[k] = bgd_grad[j] * sky_grad[k] * fc;
00684                         }
00685                         
00686                         // ... else if spectral model for background component
00687                         // is non-zero and non-infinite then we have the
00688                         // curvature element of a background component
00689                         else if (bgd_grad[k] != 0.0  &&
00690                                  !gammalib::is_infinite(bgd_grad[k])) {
00691                             colvar[k] = bgd_grad[j] * bgd_grad[k] * fd;
00692                         }
00693                         
00694                         // ... else neither sky nor background
00695                         else {
00696                             colvar[k] = 0.0;
00697                         }
00698 
00699                     } // endfor: Hessian computation
00700                 
00701                     // Update matrix
00702                     curvature->add_to_column(j, colvar);
00703             
00704                 } // endif: spectral model for background component valid
00705 
00706             } // endfor: looped over all parameters for derivatives computation
00707             
00708         } // endfor: looped over energy bins
00709 
00710     } // endif: number of parameters was positive
00711                 
00712     // ... else there are no parameters, so throw an exception
00713     else {
00714         std::string msg ="No model parameter for the computation of the "
00715                          "likelihood in observation \""+this->name()+
00716                          "\" (ID "+this->id()+").\n";
00717         throw GException::invalid_value(G_LIKELIHOOD, msg);
00718     }
00719         
00720     // Dump statistics
00721     #if defined(G_LIKELIHOOD_DEBUG)
00722     std::cout << "Number of parameters: " << npars << std::endl;
00723     std::cout << "Number of bins: " << n_bins << std::endl;
00724     std::cout << "Number of bins used for computation: " << n_used << std::endl;
00725     std::cout << "Number of bins excluded due to small model: ";
00726     std::cout << n_small_model << std::endl;
00727     std::cout << "Number of bins with zero data: " << n_zero_data << std::endl;
00728     std::cout << "Sum of data (On): " << sum_data << std::endl;
00729     std::cout << "Sum of model (On): " << sum_model << std::endl;
00730     std::cout << "Statistics: " << value << std::endl;
00731     #endif
00732     
00733     // Optionally dump gradient and curvature matrix
00734     #if defined(G_LIKELIHOOD_DEBUG)
00735     std::cout << *gradient << std::endl;
00736     std::cout << *curvature << std::endl;
00737     #endif
00738     
00739     // Timing measurement
00740     #if defined(G_LIKELIHOOD_DEBUG)
00741     #ifdef _OPENMP
00742     double t_elapse = omp_get_wtime()-t_start;
00743     #else
00744     double t_elapse = (double)(clock() - t_start) / (double)CLOCKS_PER_SEC;
00745     #endif
00746     std::cout << "GCTAOnOffObservation::optimizer::likelihood: CPU usage = "
00747               << t_elapse << " sec" << std::endl;
00748     #endif
00749     
00750     // Return
00751     return value;
00752 }
00753 
00754 
00755 /***********************************************************************//**
00756  * @brief Print On/Off observation information
00757  *
00758  * @return String containing On/Off observation information.
00759  ***************************************************************************/
00760 std::string GCTAOnOffObservation::print(const GChatter& chatter) const
00761 {
00762     // Initialise result string
00763     std::string result;
00764 
00765     // Continue only if chatter is not silent
00766     if (chatter != SILENT) {
00767     
00768         // Append header
00769         result.append("=== GCTAOnOffObservation ===");
00770 
00771         // Append parameters
00772         result.append("\n"+gammalib::parformat("Name")+m_name);
00773         result.append("\n"+gammalib::parformat("Identifier")+m_id);
00774 
00775         // Append spectra, ARF and RMF
00776         result.append("\n"+m_on_spec.print(gammalib::reduce(chatter)));
00777         result.append("\n"+m_off_spec.print(gammalib::reduce(chatter)));
00778         result.append("\n"+m_arf.print(gammalib::reduce(chatter)));
00779         result.append("\n"+m_rmf.print(gammalib::reduce(chatter)));
00780 
00781         // Append regions
00782         result.append("\n"+m_on_regions.print(gammalib::reduce(chatter)));
00783         result.append("\n"+m_off_regions.print(gammalib::reduce(chatter)));
00784     }
00785 
00786     // Return result
00787     return result;
00788 }
00789 
00790 
00791 /*==========================================================================
00792  =                                                                         =
00793  =                             Private methods                             =
00794  =                                                                         =
00795  ==========================================================================*/
00796 
00797 /***********************************************************************//**
00798  * @brief Initialise class members
00799  ***************************************************************************/
00800 void GCTAOnOffObservation::init_members(void)
00801 {
00802     // Initialise members
00803     m_instrument = "CTAOnOff";
00804     m_response   = NULL;
00805     m_ontime     = 0.0;
00806     m_livetime   = 0.0;
00807     m_deadc      = 1.0;
00808     m_on_spec.clear();
00809     m_off_spec.clear();
00810     m_arf.clear();
00811     m_rmf.clear();
00812     m_on_regions.clear();
00813     m_off_regions.clear();
00814 
00815     // Return
00816     return;
00817 }
00818 
00819 
00820 /***********************************************************************//**
00821  * @brief Copy class members
00822  *
00823  * @param[in] obs CTA On/Off observation.
00824  ***************************************************************************/
00825 void GCTAOnOffObservation::copy_members(const GCTAOnOffObservation& obs)
00826 {
00827     // Copy attributes
00828     m_instrument  = obs.m_instrument;
00829     m_ontime      = obs.m_ontime;
00830     m_livetime    = obs.m_livetime;
00831     m_deadc       = obs.m_deadc;
00832     m_on_spec     = obs.m_on_spec;
00833     m_off_spec    = obs.m_off_spec;
00834     m_arf         = obs.m_arf;
00835     m_rmf         = obs.m_rmf;
00836     m_on_regions  = obs.m_on_regions;
00837     m_off_regions = obs.m_off_regions;
00838 
00839     // Clone members
00840     m_response = (obs.m_response != NULL) ? obs.m_response->clone() : NULL;
00841 
00842     // Return
00843     return;
00844 }
00845 
00846 
00847 
00848 /***********************************************************************//**
00849  * @brief Delete class members
00850  ***************************************************************************/
00851 void GCTAOnOffObservation::free_members(void)
00852 {
00853     // Return
00854     return;
00855 }
00856 
00857 
00858 /***********************************************************************//**
00859  * @brief Set On/Off observation from a CTA observation
00860  *
00861  * @param[in] obs CTA observation.
00862  *
00863  * @exception GException::invalid_value
00864  *            No CTA event list found in CTA observation.
00865  *
00866  * Sets an On/Off observation from a CTA observation by filling the events
00867  * that fall in the On and Off regions into the PHA spectra and by computing
00868  * the corresponding ARF and RMF response functions.
00869  ***************************************************************************/
00870 void GCTAOnOffObservation::set(const GCTAObservation& obs)
00871 {
00872     // Get CTA event list pointer
00873     const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events());
00874     if (events == NULL) {
00875         std::string msg = "No event list found in CTA observation \""+
00876                           obs.name()+"\" (ID="+obs.id()+"). ON/OFF observation "
00877                           "can only be filled from event list.";
00878         throw GException::invalid_value(G_SET, msg);
00879     }
00880 
00881     // Loop over all events
00882     for (int i = 0; i < events->size(); ++i) {
00883 
00884         // Get measured event direction
00885         const GCTAEventAtom* atom = (*events)[i];
00886         GSkyDir              dir  = atom->dir().dir();
00887 
00888         // Fill in spectrum according to region containment
00889         if (m_on_regions.contains(dir)) {
00890             m_on_spec.fill(atom->energy());
00891         }
00892         if (m_off_regions.contains(dir)) {
00893             m_off_spec.fill(atom->energy());
00894         }
00895         
00896     } // endfor: looped over all events
00897 
00898     // Store the livetime as exposures of the spectra
00899     m_on_spec.exposure(obs.livetime());
00900     m_off_spec.exposure(obs.livetime());
00901 
00902     // Store the ontime, livetime and deadtime correction in the observation
00903     m_ontime   = obs.ontime();
00904     m_livetime = obs.livetime();
00905     m_deadc    = obs.deadc();
00906 
00907     // Compute response components
00908     compute_arf(obs);
00909     compute_bgd(obs);
00910     compute_alpha(obs);
00911     compute_rmf(obs);
00912     
00913     // Return
00914     return;
00915 }
00916 
00917 
00918 /***********************************************************************//**
00919  * @brief Compute ARF of On/Off observation
00920  *
00921  * @param[in] obs CTA observation.
00922  *
00923  * @exception GException::invalid_value
00924  *            No CTA response found in CTA observation.
00925  *
00926  * @todo Implement GCTAResponse::npred usage instead of computing Aeff
00927  *       (required the integration of PSF over On region).
00928  * @todo Allow general region, not only circles.
00929  ***************************************************************************/
00930 void GCTAOnOffObservation::compute_arf(const GCTAObservation& obs)
00931 {
00932     // Get reconstructed energy boundaries from on ARF
00933     GEbounds ereco = m_arf.ebounds();
00934     int      nreco = ereco.size();
00935 
00936     // Continue only if there are ARF bins
00937     if (nreco > 0) {
00938     
00939         // Get CTA response pointer. Throw an exception if no response is found
00940         const GCTAResponseIrf* response =
00941               dynamic_cast<const GCTAResponseIrf*>(obs.response());
00942         if (response == NULL) {
00943             std::string msg = "Response in CTA observation \""+obs.name()+"\" "
00944                               "(ID="+obs.id()+") is not of the GCTAResponseIrf "
00945                               "type.";
00946             throw GException::invalid_value(G_COMPUTE_ARF, msg);
00947         }
00948         
00949         // Get CTA observation pointing direction, zenith, and azimuth
00950         GCTAPointing obspnt  = obs.pointing();
00951         GSkyDir      obsdir  = obspnt.dir();
00952         double       zenith  = obspnt.zenith();
00953         double       azimuth = obspnt.azimuth();
00954 
00955         // Loop over reconstructed energies
00956         for (int i = 0; i < nreco; ++i) {
00957         
00958             // Get mean energy of bin
00959             double logEreco = ereco.elogmean(i).log10TeV();
00960             
00961             // Initialize effective area for this bin
00962             m_arf[i] = 0.0;
00963             
00964             // Loop over On sky regions
00965             for (int m = 0; m < m_on_regions.size(); ++m)  {
00966                 
00967                 // If region is of type GSkyRegionCircle
00968                 const GSkyRegionCircle* skyreg =
00969                       dynamic_cast<const GSkyRegionCircle*>(m_on_regions[m]);
00970                 if (skyreg != NULL) {
00971                     
00972                     // Get centre of circular region
00973                     GSkyDir centre(skyreg->centre());
00974                     
00975                     // Compute position of region centre in instrument coordinates
00976                     double theta = obsdir.dist(centre);
00977                     double phi   = obsdir.posang(centre);
00978                     
00979                     // Add up effective area
00980                     m_arf[i] += response->aeff(theta,
00981                                                phi,
00982                                                zenith,
00983                                                azimuth,
00984                                                logEreco);
00985                                     
00986                 } // endif: sky region is of type GSkyRegionCircle
00987                 
00988             } // endfor: looped over On regions
00989        
00990         } // endfor: looped over reconstructed energies
00991         
00992     } // endif: there were energy bins
00993 
00994     // Return
00995     return;
00996 }
00997 
00998 
00999 /***********************************************************************//**
01000  * @brief Compute background rate in Off region
01001  *
01002  * @param[in] obs CTA observation.
01003  *
01004  * @exception GException::invalid_argument
01005  *            Observation does not contain relevant response or background
01006  *            information
01007  *
01008  * Compute the background rate in units of events/s/MeV in all Off regions
01009  * and stores the background rate as additional column with name
01010  * "BACKGROUND" in the Auxiliary Response File (ARF).
01011  *
01012  * @todo Should implement integration of background model over region.
01013  * @todo Allow general region, not only circles.
01014  ***************************************************************************/
01015 void GCTAOnOffObservation::compute_bgd(const GCTAObservation& obs)
01016 {   
01017     // Get reconstructed energy boundaries from on ARF
01018     GEbounds ereco = m_arf.ebounds();
01019     int      nreco = ereco.size();
01020     
01021     // Continue only if there are ARF bins
01022     if (nreco > 0) {
01023 
01024         // Initialise background rates to zero
01025         std::vector<double> background(nreco, 0.0);
01026             
01027         // Get CTA observation pointing direction
01028         GCTAPointing obspnt = obs.pointing();
01029     
01030         // Get pointer on CTA IRF response
01031         const GCTAResponseIrf* rsp =
01032               dynamic_cast<const GCTAResponseIrf*>(obs.response());
01033         if (rsp == NULL) {
01034             std::string msg = "Specified observation does not contain an "
01035                               "IRF response.\n" + obs.print();
01036             throw GException::invalid_argument(G_COMPUTE_BGD, msg);
01037         }
01038 
01039         // Get pointer to CTA background
01040         const GCTABackground* bgd = rsp->background();
01041         if (bgd == NULL) {
01042             std::string msg = "Specified observation contains no "
01043                               "background information.\n" + obs.print();
01044             throw GException::invalid_argument(G_COMPUTE_BGD, msg);
01045         }
01046                     
01047         // Loop over Off regions
01048         for (int m = 0; m < m_off_regions.size(); ++m)  {
01049                         
01050             // Get region centre direction, fall through if region is not
01051             // of type GSkyRegionCircle
01052             const GSkyRegionCircle* skyreg =
01053                   dynamic_cast<const GSkyRegionCircle*>(m_off_regions[m]);
01054             if (skyreg == NULL) {
01055                 continue;
01056             }
01057                                 
01058             // Get centre of circular region
01059             GSkyDir centre = skyreg->centre();
01060                                 
01061             // Get instrument direction of region centre
01062             GCTAInstDir offdir = obspnt.instdir(centre);
01063                             
01064             // Loop over energy bins
01065             for (int i = 0; i < nreco; ++i) {
01066 
01067                 // Get log10(E/TeV) of mean reconstructed bin energy
01068                 double logEreco = m_on_spec.ebounds().elogmean(i).log10TeV();
01069 
01070                 // Get background rate in events/s/MeV
01071                 background[i] += (*bgd)(logEreco,
01072                                         offdir.detx(),
01073                                         offdir.dety()) * skyreg->solidangle();
01074 
01075             } // endfor: looped over energy bins
01076 
01077         } // endfor: looped over sky regions
01078 
01079         // Append background vector to ARF
01080         m_arf.append("BACKGROUND", background);
01081         
01082     } // endif: there were spectral bins
01083 
01084     // Return
01085     return;
01086 }
01087 
01088 
01089 /***********************************************************************//**
01090  * @brief Compute vector of alpha parameters
01091  *
01092  * @param[in] obs CTA observation.
01093  *
01094  * @exception GException::invalid_value
01095  *            No CTA response found in CTA observation.
01096  *
01097  * Compute the alpha parameters for all energy bins. The alpha parameter
01098  * gives the ratio between the On and Off region background acceptance
01099  * multiplied by the ratio between On and Off region solid angles.
01100  *
01101  * @todo Implement for general sky regions, not only circles
01102  * @todo Integrate over the sky region instead of taking the values at the
01103  *       sky region centre
01104  ***************************************************************************/
01105 void GCTAOnOffObservation::compute_alpha(const GCTAObservation& obs)
01106 {
01107     // Get reconstructed energy boundaries from on ARF
01108     GEbounds ereco = m_arf.ebounds();
01109     int      nreco = ereco.size();
01110     
01111     // Continue only if there are ARF bins
01112     if (nreco > 0) {
01113 
01114         // Initialise On/Off exposure ratios
01115         std::vector<double> alpha(nreco, 0.0);
01116     
01117         // Get CTA response pointer. Throw an exception if no response is found
01118         const GCTAResponseIrf* response =
01119               dynamic_cast<const GCTAResponseIrf*>(obs.response());
01120         if (response == NULL) {
01121             std::string msg = "Response in CTA observation \""+obs.name()+"\" "
01122                               "(ID="+obs.id()+") is not of the GCTAResponseIrf "
01123                               "type.";
01124             throw GException::invalid_value(G_COMPUTE_ALPHA, msg);
01125         }
01126         
01127         // Get CTA observation pointing direction, zenith, and azimuth
01128         GCTAPointing obspnt  = obs.pointing();
01129         GSkyDir      obsdir  = obspnt.dir();
01130 
01131         // Loop over reconstructed energies 
01132         for (int i = 0; i < nreco; ++i) {
01133         
01134             // Get mean log10 energy in TeV of bin
01135             double logEreco = ereco.elogmean(i).log10TeV();
01136             
01137             // Initialise effective area totals
01138             double aon(0.0);
01139             double aoff(0.0);
01140             
01141             // Loop over ON sky regions
01142             for (int m = 0; m < m_on_regions.size(); ++m)  {
01143                 
01144                 // If region is of type GSkyRegionCircle
01145                 const GSkyRegionCircle* skyreg =
01146                       dynamic_cast<const GSkyRegionCircle*>(m_on_regions[m]);
01147                 if (skyreg != NULL) {
01148                     
01149                     // Get centre of circular region
01150                     GSkyDir centreg = skyreg->centre();
01151                     
01152                     // Compute instrument direction from region centre
01153                     GCTAInstDir instdir(obspnt.instdir(centreg));
01154 
01155                     // Add up acceptance
01156                     aon += (*response->background())(logEreco,
01157                                                      instdir.detx(),
01158                                                      instdir.dety()) *
01159                            skyreg->solidangle();
01160                     
01161                 } // endif: sky region is of type GSkyRegionCircle
01162                 
01163             } // Looped over ON regions
01164                         
01165             // Loop over OFF sky regions
01166             for (int m = 0; m < m_off_regions.size(); ++m)  {
01167                 
01168                 // If region is of type GSkyRegionCircle
01169                 const GSkyRegionCircle* skyreg =
01170                       dynamic_cast<const GSkyRegionCircle*>(m_off_regions[m]);
01171                 if (skyreg != NULL) {
01172                     
01173                     // Get centre of circular region
01174                     GSkyDir centreg = skyreg->centre();
01175                     
01176                     // Compute instrument direction from region centre
01177                     GCTAInstDir instdir(obspnt.instdir(centreg));
01178 
01179                     // Add up acceptance
01180                     aoff += (*response->background())(logEreco,
01181                                                      instdir.detx(),
01182                                                      instdir.dety()) *
01183                             skyreg->solidangle();
01184                     
01185                 } // endif: sky region is of type GSkyRegionCircle
01186                 
01187             } // endfor: looped over OFF regions
01188             
01189             // Compute alpha for this energy bin
01190             if (aoff > 0.0) {
01191                 alpha[i] = aon/aoff;
01192             }
01193                                                  
01194         } // endfor: looped over reconstructed energies
01195 
01196         // Append alpha vector to ARF
01197         m_arf.append("ALPHA", alpha);
01198     
01199     } // endif: there were energy bins
01200 
01201     // Return
01202     return;
01203 }
01204 
01205 
01206 /***********************************************************************//**
01207  * @brief Compute RMF of On/Off observation
01208  *
01209  * @param[in] obs CTA observation.
01210  *
01211  * @exception GException::invalid_value
01212  *            Observation does not contain IRF response
01213  *
01214  * Compute the energy redistribution matrix for an On/Off observation. The
01215  * method requires that the RMF energy axes have been defined before.
01216  *
01217  * @todo Check if we really should use reconstructed energy in the Aeff
01218  *       access.
01219  * @todo Generalise to any kind of sky region
01220  ***************************************************************************/
01221 void GCTAOnOffObservation::compute_rmf(const GCTAObservation& obs)
01222 {
01223     // Get true and reconstructed energy boundaries from RMF
01224     GEbounds etrue = m_rmf.etrue();
01225     GEbounds ereco = m_rmf.emeasured();
01226     int      ntrue = etrue.size();
01227     int      nreco = ereco.size();
01228 
01229     // Continue only if there are RMF bins
01230     if (ntrue > 0 && nreco > 0) {
01231     
01232         // Get CTA response pointer
01233         const GCTAResponseIrf* response =
01234               dynamic_cast<const GCTAResponseIrf*>(obs.response());
01235         if (response == NULL) {
01236             std::string msg = "Response in CTA observation \""+obs.name()+"\" "
01237                               "(ID="+obs.id()+") is not of the GCTAResponseIrf "
01238                               "type.";
01239             throw GException::invalid_value(G_COMPUTE_RMF, msg);
01240         }
01241         
01242         // Get CTA observation pointing direction, zenith, and azimuth
01243         GCTAPointing obspnt  = obs.pointing();
01244         GSkyDir      obsdir  = obspnt.dir();
01245         double       zenith  = obspnt.zenith();
01246         double       azimuth = obspnt.azimuth();
01247         
01248         // Loop over reconstructed energy
01249         for (int ireco = 0; ireco < nreco; ++ireco) {
01250 
01251             // Get log10(E)
01252             double logEreco = ereco.elogmean(ireco).log10TeV();
01253 
01254             // Loop over true energy
01255             for (int itrue = 0; itrue < ntrue; ++itrue) {
01256 
01257                 // Initialise RMF element
01258                 m_rmf(itrue, ireco) = 0.0;
01259                 
01260                 // Compute true energy and initialise weight for this bin
01261                 double logEtrue = etrue.elogmean(itrue).log10TeV();
01262                 double weight   = 0.0;
01263                 
01264                 // Loop over ON sky regions
01265                 for (int m = 0; m < m_on_regions.size(); ++m)  {
01266                 
01267                     // Fall through if region is not of type GSkyRegionCircle
01268                     const GSkyRegionCircle* skyreg =
01269                           dynamic_cast<const GSkyRegionCircle*>(m_on_regions[m]);
01270                     if (skyreg == NULL) {
01271                         continue;
01272                     }
01273                     
01274                     // Get centre of circular region
01275                     GSkyDir centreg(skyreg->centre());
01276                     
01277                     // Compute position of region centre in instrument coordinates
01278                     double theta = obsdir.dist(centreg);
01279                     double phi   = obsdir.posang(centreg);
01280 
01281                     // Get effective area
01282                     double aeff = response->aeff(theta,
01283                                                  phi,
01284                                                  zenith,
01285                                                  azimuth,
01286                                                  logEreco);
01287                     
01288                     // Add up energy dispersion weighted by effective area
01289                     weight              += aeff;
01290                     m_rmf(itrue, ireco) += response->edisp(ereco.elogmean(ireco),
01291                                                            theta,
01292                                                            phi,
01293                                                            zenith,
01294                                                            azimuth,
01295                                                            logEtrue) * aeff;
01296                                     
01297                 } // endfor: looped over ON regions
01298                 
01299                 // Complete weighing by dividing by total effective area
01300                 if (weight > 0.0) {
01301                     m_rmf(itrue, ireco) /= weight;
01302                 }   
01303 
01304             } // endfor: looped over true energy
01305             
01306         } // endfor: looped over reconstructed energy
01307         
01308     } // endif: there were energy bins
01309 
01310     // Return
01311     return;
01312 }
01313 
01314 
01315 /***********************************************************************
01316  * @brief Compute \f$N_{\gamma}\f$ value and model parameter gradients
01317  *
01318  * @param[in] models Model container.
01319  * @param[in] ibin Energy bin number.
01320  * @param[in,out] grad Model gradient vector.
01321  *
01322  * Returns the predicted number of source events \f$N_{\gamma}\f$
01323  * in the On regions for a given energy bin. The method computes also
01324  *
01325  * \f[
01326  *    \left( \frac{\partial N_{\gamma}}{\partial p_{\rm sky}} \right)
01327  * \f]
01328  *
01329  * which are the gradients in the predicted number of source events with
01330  * respect to all model parameters.
01331  *
01332  * The method assumes that parameters are stored in the order
01333  * spatial-spectral-temporal.
01334  *
01335  * @todo I think this method only works for point sources. What happens
01336  *       for an extended source?
01337  ***********************************************************************/
01338 double GCTAOnOffObservation::N_gamma(const GModels& models,
01339                                      const int&     ibin,
01340                                      GVector*       grad) const
01341 {
01342     // Get total number of model parameters
01343     int npars = models.npars();
01344 
01345     // Initialize results
01346     double value = 0.0;
01347     for (int i = 0; i < npars; ++i) {
01348         (*grad)[i] = 0.0;
01349     }
01350     
01351     // Continue only if bin number is in range and there are model parameters
01352     if ((ibin >= 0) && (ibin < m_on_spec.size()) && (npars > 0)) {
01353 
01354         // Initialise parameter index
01355         int ipar = 0;
01356         
01357         // Get energy bin bounds
01358         const GEnergy emin   = m_on_spec.ebounds().emin(ibin);
01359         const GEnergy emax   = m_on_spec.ebounds().emax(ibin);
01360         const GEnergy emean  = m_on_spec.ebounds().elogmean(ibin);
01361         const double  ewidth = m_on_spec.ebounds().ewidth(ibin).MeV();
01362 
01363         // Compute normalisation factors
01364         double exposure  = m_on_spec.exposure();
01365         double norm_flux = m_arf[ibin] * exposure; // cm2 s
01366         double norm_grad = norm_flux   * ewidth;   // cm2 s MeV
01367                 
01368         // Loop over models
01369         for (int j = 0; j < models.size(); ++j) {
01370             
01371             // Get model pointer. Fall through if pointer is not valid
01372             const GModel* mptr = models[j];
01373             if (mptr == NULL) {
01374                 continue;
01375             }
01376                 
01377             // Fall through if model does not apply to specific instrument
01378             // and observation identifier
01379             if (!mptr->is_valid(instrument(), id())) {
01380                 ipar += mptr->size();
01381                 continue;
01382             }
01383                     
01384             // Fall through if this model component is a not sky component
01385             const GModelSky* sky = dynamic_cast<const GModelSky*>(mptr);
01386             if (sky == NULL) {
01387                 ipar += mptr->size();
01388                 continue;
01389             }
01390                     
01391             // Increase parameter counter for spatial parameter
01392             GModelSpatial* spatial = sky->spatial();
01393             if (spatial != NULL)  {
01394                 ipar += spatial->size();
01395             }
01396                         
01397             // Spectral component (the useful one)
01398             GModelSpectral* spectral = sky->spectral();
01399             if (spectral != NULL)  {
01400                                 
01401                 // Determine the number of gamma-ray events in model by
01402                 // computing the flux over the energy bin in ph/cm2/s
01403                 // and multiplying this flux by the effective area (cm2)
01404                 // and the livetime (s)
01405                 value += spectral->flux(emin, emax) * norm_flux;
01406                                 
01407                 // Determine the model gradients at the current energy. The
01408                 // eval() method needs a time in case that the spectral model
01409                 // has a time dependence. We simply use a dummy time here.
01410                 spectral->eval(emean, GTime(), true);
01411 
01412                 // Loop over spectral model parameters
01413                 for (int k = 0; k < spectral->size(); ++k, ++ipar)  {
01414                     GModelPar& par = (*spectral)[k];
01415                     if (par.is_free() && ipar < npars)  {
01416                         (*grad)[ipar] = par.factor_gradient() * norm_grad;
01417                     }
01418                 }
01419                                 
01420             } // endif: spectral component
01421                             
01422             // Increase parameter counter for temporal parameter
01423             GModelTemporal* temporal = sky->temporal();
01424             if (temporal != NULL)  {
01425                 ipar += temporal->size();
01426             }
01427             
01428         } // endfor: looped over model components
01429         
01430     } // endif: bin number is in the range and model container is not empty
01431     
01432     // Return number of gamma-ray events
01433     return value;
01434 }
01435 
01436 
01437 /***********************************************************************
01438  * @brief Compute \f$N_{\rm bgd}\f$ value and model parameter gradients
01439  *
01440  * @param[in] models Model container.
01441  * @param[in] ibin Energy bin index.
01442  * @param[in,out] grad Model gradient vector.
01443  * @return Predicted number of background events in Off regions.
01444  *
01445  * Returns the predicted number of background events \f$N_{\rm bgd}\f$
01446  * in the Off regions for a given energy bin. The method computes also
01447  *
01448  * \f[
01449  *    \left( \frac{\partial N_{\rm bgd}}{\partial p_{\rm bgd}} \right)
01450  * \f]
01451  *
01452  * which are the gradients in the predicted number of background events
01453  * with respect to all model parameters.
01454  *
01455  * The method assumes that the model parameters are stored in the order
01456  * spectral-temporal.
01457  *
01458  * @todo So far only handles the GCTAModelIrfBackground background model;
01459  *       Should be more generic, but there is maybe for the moment no
01460  *       other choice than implementing something for all possible
01461  *       background models; that's not very satisfactory ...
01462  ***********************************************************************/
01463 double GCTAOnOffObservation::N_bgd(const GModels& models,
01464                                    const int&     ibin,
01465                                    GVector*       grad) const
01466 {
01467     // Get total number of model parameters
01468     int npars = models.npars();
01469     
01470     // Initialize results
01471     double value = 0.0;
01472     for (int i = 0; i < npars; ++i) {
01473         (*grad)[i] = 0.0;
01474     }
01475 
01476     // Continue only if bin number is valid and if there are model parameters
01477     if ((ibin >= 0) && (ibin < m_off_spec.size()) && (npars > 0))  {
01478 
01479         // Initialise parameter index
01480         int ipar = 0;
01481     
01482         // Get reference to background rates (events/MeV/s)
01483         const std::vector<double>& background = m_arf["BACKGROUND"];
01484                 
01485         // Get energy bin mean and width
01486         const GEnergy emean  = m_off_spec.ebounds().elogmean(ibin);
01487         const double  ewidth = m_off_spec.ebounds().ewidth(ibin).MeV();
01488 
01489         // Compute normalisation factor (events)
01490         double exposure = m_off_spec.exposure();
01491         double norm     = background[ibin] * exposure * ewidth;
01492             
01493         // Loop over models
01494         for (int j = 0; j < models.size(); ++j) {
01495                 
01496             // Get model pointer. Fall through if pointer is not valid
01497             const GModel* mptr = models[j];
01498             if (mptr == NULL) {
01499                 continue;
01500             }
01501                     
01502             // Fall through if model does not apply to specific instrument
01503             // and observation identifier.
01504             if (!mptr->is_valid(this->instrument(), this->id())) {
01505                 ipar += mptr->size();
01506                 continue;
01507             }
01508                         
01509             // Fall through if model is not an IRF background component
01510             const GCTAModelIrfBackground* bgd =
01511                   dynamic_cast<const GCTAModelIrfBackground*>(mptr);
01512             if (bgd == NULL) {
01513                 ipar += mptr->size();
01514                 continue;
01515             }
01516                             
01517             // Get spectral component
01518             GModelSpectral* spectral = bgd->spectral();
01519             if (spectral != NULL)  {
01520 
01521                 // Determine the number of background events in model by
01522                 // computing the model normalization at the mean bin energy bin
01523                 // and multiplying the normalisation with the number of
01524                 // background events. The eval() method needs a time in case
01525                 // that the spectral model has a time dependence. We simply
01526                 // use a dummy time here.
01527                 value += spectral->eval(emean, GTime(), true) * norm;
01528 
01529                 // Compute the parameter gradients for all spectral model
01530                 // parameters
01531                 for (int k = 0; k < spectral->size(); ++k, ++ipar)  {
01532                     GModelPar& par = (*spectral)[k];
01533                     if (par.is_free() && ipar < npars)  {
01534                         (*grad)[ipar] += par.factor_gradient() * norm;
01535                     }
01536                 }
01537                                 
01538             } // endif: pointer to spectral component was not NULL
01539                             
01540             // Increase parameter counter for temporal parameter
01541             GModelTemporal* temporal = bgd->temporal();
01542             if (temporal != NULL)  {
01543                 ipar += temporal->size();
01544             }
01545                                                 
01546         } // endfor: looped over model components
01547             
01548     } // endif: bin number is in the range and model container is not empty
01549     
01550     // Return
01551     return value;
01552 }

Generated on Tue Jan 24 12:37:20 2017 for GammaLib by  doxygen 1.4.7