/***************************************************************************
* GResponse.cpp - Abstract response base class *
* ----------------------------------------------------------------------- *
* copyright (C) 2008-2015 by Juergen Knoedlseder *
* ----------------------------------------------------------------------- *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
* *
***************************************************************************/
/**
* @file GResponse.hpp
* @brief Abstract response base class interface definition
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include // access() function
#include "GTools.hpp"
#include "GMath.hpp"
#include "GException.hpp"
#include "GIntegral.hpp"
#include "GResponse.hpp"
#include "GEvent.hpp"
#include "GPhoton.hpp"
#include "GEnergy.hpp"
#include "GTime.hpp"
#include "GSource.hpp" // will become obsolete
#include "GEbounds.hpp" // will become obsolete
#include "GObservation.hpp"
#include "GModelSky.hpp"
#include "GModelSpatialPointSource.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_IRF_RADIAL "GResponse::irf_radial(GEvent&, GSource&,"\
" GObservation&)"
#define G_IRF_ELLIPTICAL "GResponse::irf_elliptical(GEvent&, GSource&,"\
" GObservation&)"
#define G_IRF_DIFFUSE "GResponse::irf_diffuse(GEvent&, GSource&,"\
" GObservation&)"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GResponse::GResponse(void)
{
// Initialise class members for clean destruction
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] rsp Response.
***************************************************************************/
GResponse::GResponse(const GResponse& rsp)
{
// Initialise class members for clean destruction
init_members();
// Copy members
copy_members(rsp);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GResponse::~GResponse(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] rsp Response.
* @return Response.
***************************************************************************/
GResponse& GResponse::operator=(const GResponse& rsp)
{
// Execute only if object is not identical
if (this != &rsp) {
// Free members
free_members();
// Initialise private members for clean destruction
init_members();
// Copy members
copy_members(rsp);
} // endif: object was not identical
// Return this object
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Convolve sky model with the instrument response
*
* @param[in] model Sky model.
* @param[in] event Event.
* @param[in] obs Observation.
* @param[in] grad Should model gradients be computed? (default: true)
* @return Event probability.
*
* Computes the event probability
*
* \f[
* P(p',E',t') = \int \int \int
* S(p,E,t) \times R(p',E',t'|p,E,t) \, dp \, dE \, dt
* \f]
*
* without taking into account any time dispersion. Energy dispersion is
* correctly handled by this method. If time dispersion is indeed needed,
* an instrument specific method needs to be provided.
***************************************************************************/
double GResponse::convolve(const GModelSky& model,
const GEvent& event,
const GObservation& obs,
const bool& grad) const
{
// Set number of iterations for Romberg integration.
static const int iter = 6;
// Initialise result
double prob = 0.0;
// Continue only if the model has a spatial component
if (model.spatial() != NULL) {
// Get source time (no dispersion)
GTime srcTime = event.time();
// Case A: Integration
if (use_edisp()) {
// Retrieve true energy boundaries
GEbounds ebounds = this->ebounds(event.energy());
// Loop over all boundaries
for (int i = 0; i < ebounds.size(); ++i) {
// Get boundaries in MeV
double emin = ebounds.emin(i).MeV();
double emax = ebounds.emax(i).MeV();
// Continue only if valid
if (emax > emin) {
// Setup integration function
edisp_kern integrand(this, &obs, &model, &event, srcTime, grad);
GIntegral integral(&integrand);
// Set number of iterations
integral.fixed_iter(iter);
// Do Romberg integration
emin = std::log(emin);
emax = std::log(emax);
prob += integral.romberg(emin, emax);
} // endif: interval was valid
} // endfor: looped over intervals
}
// Case B: No integration (assume no energy dispersion)
else {
// Get source energy (no dispersion)
GEnergy srcEng = event.energy();
// Evaluate probability
prob = eval_prob(model, event, srcEng, srcTime, obs, grad);
}
// Compile option: Check for NaN/Inf
#if defined(G_NAN_CHECK)
if (gammalib::is_notanumber(prob) || gammalib::is_infinite(prob)) {
std::cout << "*** ERROR: GResponse::convolve:";
std::cout << " NaN/Inf encountered";
std::cout << " (prob=" << prob;
std::cout << ", event=" << event;
std::cout << ", srcTime=" << srcTime;
std::cout << ")" << std::endl;
}
#endif
} // endif: spatial component valid
// Return probability
return prob;
}
/*==========================================================================
= =
= Protected methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GResponse::init_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] rsp Response.
***************************************************************************/
void GResponse::copy_members(const GResponse& rsp)
{
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GResponse::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Convolve sky model with the instrument response
*
* @param[in] model Sky model.
* @param[in] event Event.
* @param[in] srcEng Source energy.
* @param[in] srcTime Source time.
* @param[in] obs Observation.
* @param[in] grad Should model gradients be computed? (default: true)
* @return Event probability.
*
* Computes the event probability
*
* \f[
* P(p',E',t'|E,t) = \int S(p,E,t) \times R(p',E',t'|p,E,t) \, dp
* \f]
*
* for a given true energy \f$E\f$ and time \f$t\f$.
***************************************************************************/
double GResponse::eval_prob(const GModelSky& model,
const GEvent& event,
const GEnergy& srcEng,
const GTime& srcTime,
const GObservation& obs,
const bool& grad) const
{
// Initialise result
double prob = 0.0;
// Continue only if the model has a spatial component
if (model.spatial() != NULL) {
// Set source
GSource source(model.name(), model.spatial(), srcEng, srcTime);
// Get IRF value. This method returns the spatial component of the
// source model.
double irf = this->irf(event, source, obs);
// If required, apply instrument specific model scaling
if (model.has_scales()) {
irf *= model.scale(obs.instrument()).value();
}
// Case A: evaluate gradients
if (grad) {
// Evaluate source model
double spec = (model.spectral() != NULL) ? model.spectral()->eval_gradients(srcEng, srcTime) : 1.0;
double temp = (model.temporal() != NULL) ? model.temporal()->eval_gradients(srcTime) : 1.0;
// Set probability
prob = spec * temp * irf;
// Compile option: Check for NaN/Inf
#if defined(G_NAN_CHECK)
if (gammalib::is_notanumber(prob) || gammalib::is_infinite(prob)) {
std::cout << "*** ERROR: GResponse::eval_prob:";
std::cout << " NaN/Inf encountered";
std::cout << " (prob=" << prob;
std::cout << ", spec=" << spec;
std::cout << ", temp=" << temp;
std::cout << ", irf=" << irf;
std::cout << ")" << std::endl;
}
#endif
// Multiply factors to spectral gradients
if (model.spectral() != NULL) {
double fact = temp * irf;
if (fact != 1.0) {
for (int i = 0; i < model.spectral()->size(); ++i) {
(*model.spectral())[i].factor_gradient((*model.spectral())[i].factor_gradient() * fact);
}
}
}
// Multiply factors to temporal gradients
if (model.temporal() != NULL) {
double fact = spec * irf;
if (fact != 1.0) {
for (int i = 0; i < model.temporal()->size(); ++i) {
(*model.temporal())[i].factor_gradient((*model.temporal())[i].factor_gradient() * fact);
}
}
}
} // endif: gradient evaluation has been requested
// Case B: evaluate no gradients
else {
// Evaluate source model
double spec = (model.spectral() != NULL) ? model.spectral()->eval(srcEng, srcTime) : 1.0;
double temp = (model.temporal() != NULL) ? model.temporal()->eval(srcTime) : 1.0;
// Set probability
prob = spec * temp * irf;
// Compile option: Check for NaN/Inf
#if defined(G_NAN_CHECK)
if (gammalib::is_notanumber(prob) || gammalib::is_infinite(prob)) {
std::cout << "*** ERROR: GResponse::eval_prob:";
std::cout << " NaN/Inf encountered";
std::cout << " (prob=" << prob;
std::cout << ", spec=" << spec;
std::cout << ", temp=" << temp;
std::cout << ", irf=" << irf;
std::cout << ")" << std::endl;
}
#endif
}
} // endif: Gamma-ray source model had a spatial component
// Return event probability
return prob;
}
/***********************************************************************//**
* @brief Integration kernel for edisp_kern() method
*
* @param[in] x Function value.
*
* This method implements the integration kernel needed for the edisp_kern()
* method.
***************************************************************************/
double GResponse::edisp_kern::eval(const double& x)
{
// Set energy
GEnergy eng;
double expx = std::exp(x);
eng.MeV(expx);
// Get function value
double value = m_parent->eval_prob(*m_model, *m_event, eng, m_srcTime, *m_obs, m_grad);
// Save value if needed
#if defined(G_NAN_CHECK)
double value_out = value;
#endif
// Correct for variable substitution
value *= expx;
// Compile option: Check for NaN
#if defined(G_NAN_CHECK)
if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
std::cout << "*** ERROR: GResponse::edisp_kern::eval";
std::cout << "(x=" << x << "): ";
std::cout << " NaN/Inf encountered";
std::cout << " (value=" << value_out;
std::cout << " exp(x)=" << expx;
std::cout << ")" << std::endl;
}
#endif
// Return value
return value;
}