/***********************************************************************//**
 * @brief Evaluate function
 *
 * @param[in] event Observed event.
 * @param[in] obs Observation.
 * @return Function value.
 *
 * @exception GException::invalid_argument
 *            Specified observation is not of the expected type.
 *
 * @todo Make sure that DETX and DETY are always set in GCTAInstDir.
 ***************************************************************************/
double GCTAModelIrfBackground::eval(const GEvent& event,
                                    const GObservation& obs) const
{
    // Get pointer on CTA observation
    const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>(&obs);
    if (cta == NULL) {
        std::string msg = "Specified observation is not a CTA observation.\n" +
                          obs.print();
        throw GException::invalid_argument(G_EVAL, msg);
    }

    // Get pointer on CTA IRF response
    const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(cta->response());
    if (rsp == NULL) {
        std::string msg = "Specified observation does not contain an IRF response.\n" +
                          obs.print();
        throw GException::invalid_argument(G_EVAL, msg);
    }

    // Retrieve pointer to CTA background
    const GCTABackground* bgd = rsp->background();
    if (bgd == NULL) {
        std::string msg = "Specified observation contains no background"
                          " information.\n" + obs.print();
        throw GException::invalid_argument(G_EVAL, msg);
    }

    // Extract CTA instrument direction from event
    const GCTAInstDir* dir  = dynamic_cast<const GCTAInstDir*>(&(event.dir()));
    if (dir == NULL) {
        std::string msg = "No CTA instrument direction found in event.";
        throw GException::invalid_argument(G_EVAL, msg);
    }

    // Set DETX and DETY in instrument direction
    GCTAInstDir inst_dir = cta->pointing().instdir(dir->dir());

    // Evaluate function
    double logE = event.energy().log10TeV();
    double spat = (*bgd)(logE, inst_dir.detx(), inst_dir.dety());
    double spec = (spectral() != NULL)
                  ? spectral()->eval(event.energy(), event.time()) : 1.0;
    double temp = (temporal() != NULL)
                  ? temporal()->eval(event.time()) : 1.0;

    // Compute value
    double value = spat * spec * temp;

    // Apply deadtime correction
    value *= obs.deadc(event.time());

    // Return value
    return value;
}
/***********************************************************************//**
 * @brief Evaluate function
 *
 * @param[in] event Observed event.
 * @param[in] obs Observation.
 * @return Function value.
 *
 * @exception GException::invalid_argument
 *            No CTA instrument direction found in event.
 *
 * Evaluates tha CTA background model which is a factorization of a
 * spatial, spectral and temporal model component. This method also applies
 * a deadtime correction factor, so that the normalization of the model is
 * a real rate (counts/exposure time).
 *
 * @todo Add bookkeeping of last value and evaluate only if argument 
 *       changed
 ***************************************************************************/
double GCTAModelBackground::eval(const GEvent& event,
                                 const GObservation& obs) const
{
    // Get pointer on CTA observation
    const GCTAObservation* ctaobs = dynamic_cast<const GCTAObservation*>(&obs);
    if (ctaobs == NULL) {
        std::string msg = "Specified observation is not a CTA observation.\n" +
                          obs.print();
        throw GException::invalid_argument(G_EVAL, msg);
    }

    // Extract CTA instrument direction
    const GCTAInstDir* dir  = dynamic_cast<const GCTAInstDir*>(&(event.dir()));
    if (dir == NULL) {
        std::string msg = "No CTA instrument direction found in event.";
        throw GException::invalid_argument(G_EVAL, msg);
    }

    // Create a Photon from the event.
    // We need the GPhoton to evaluate the spatial model.
    // For the background, GEvent and GPhoton are identical
    // since the IRFs are not folded in
    GPhoton photon(dir->dir(), event.energy(), event.time());

    // Evaluate function and gradients
    double spat = (spatial() != NULL)
                  ? spatial()->eval(photon) : 1.0;
    double spec = (spectral() != NULL)
                  ? spectral()->eval(event.energy(), event.time()) : 1.0;
    double temp = (temporal() != NULL)
                  ? temporal()->eval(event.time()) : 1.0;

    // Compute value
    double value = spat * spec * temp;

    // Apply deadtime correction
    value *= obs.deadc(event.time());

    // Return
    return value;
}
Exemple #3
0
/***********************************************************************//**
 * @brief Return spatially integrated background model
 *
 * @param[in] obsEng Measured event energy.
 * @param[in] obsTime Measured event time.
 * @param[in] obs Observation.
 * @return Spatially integrated model.
 *
 * @exception GException::invalid_argument
 *            The specified observation is not a CTA observation.
 *
 * Spatially integrates the cube background model for a given measured event
 * energy and event time. This method also applies a deadtime correction
 * factor, so that the normalization of the model is a real rate
 * (counts/MeV/s).
 ***************************************************************************/
double GCTAModelCubeBackground::npred(const GEnergy&      obsEng,
                                      const GTime&        obsTime,
                                      const GObservation& obs) const
{
    // Initialise result
    double npred     = 0.0;
    bool   has_npred = false;

    // Build unique identifier
    std::string id = obs.instrument() + "::" + obs.id();

    // Check if Npred value is already in cache
    #if defined(G_USE_NPRED_CACHE)
    if (!m_npred_names.empty()) {

        // Search for unique identifier, and if found, recover Npred value
        // and break
        for (int i = 0; i < m_npred_names.size(); ++i) {
            if (m_npred_names[i] == id && m_npred_energies[i] == obsEng) {
                npred     = m_npred_values[i];
                has_npred = true;
                #if defined(G_DEBUG_NPRED)
                std::cout << "GCTAModelCubeBackground::npred:";
                std::cout << " cache=" << i;
                std::cout << " npred=" << npred << std::endl;
                #endif
                break;
            }
        }

    } // endif: there were values in the Npred cache
    #endif

    // Continue only if no Npred cache value has been found
    if (!has_npred) {

        // Evaluate only if model is valid
        if (valid_model()) {

            // Get pointer on CTA observation
            const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>(&obs);
            if (cta == NULL) {
                std::string msg = "Specified observation is not a CTA"
                                  " observation.\n" + obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Get pointer on CTA cube response
            const GCTAResponseCube* rsp = dynamic_cast<const GCTAResponseCube*>(cta->response());
            if (rsp == NULL) {
                std::string msg = "Specified observation does not contain"
                                  " a cube response.\n" + obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Get log10 of energy in TeV
            double logE = obsEng.log10TeV();

            // Retrieve CTA background
            const GCTACubeBackground bgd = rsp->background();

            // Integrate the background map at a certain energy
            npred = bgd.integral(logE);

            // Store result in Npred cache
            #if defined(G_USE_NPRED_CACHE)
            m_npred_names.push_back(id);
            m_npred_energies.push_back(obsEng);
            m_npred_times.push_back(obsTime);
            m_npred_values.push_back(npred);
            #endif

            // Debug: Check for NaN
            #if defined(G_NAN_CHECK)
            if (gammalib::is_notanumber(npred) || gammalib::is_infinite(npred)) {
                std::string origin  = "GCTAModelCubeBackground::npred";
                std::string message = " NaN/Inf encountered (npred=" +
                                      gammalib::str(npred) + ")";
                gammalib::warning(origin, message);
            }
            #endif

        } // endif: model was valid

    } // endif: Npred computation required

    // Multiply in spectral and temporal components
    npred *= spectral()->eval(obsEng, obsTime);
    npred *= temporal()->eval(obsTime);

    // Apply deadtime correction
    npred *= obs.deadc(obsTime);

    // Return Npred
    return npred;
}
/***********************************************************************//**
 * @brief Return spatially integrated data model
 *
 * @param[in] obsEng Measured event energy.
 * @param[in] obsTime Measured event time.
 * @param[in] obs Observation.
 * @return Spatially integrated model.
 *
 * @exception GException::invalid_argument
 *            No CTA event list found in observation.
 *            No CTA pointing found in observation.
 *
 * Spatially integrates the data model for a given measured event energy and
 * event time. This method also applies a deadtime correction factor, so that
 * the normalization of the model is a real rate (counts/exposure time).
 ***************************************************************************/
double GCTAModelBackground::npred(const GEnergy&      obsEng,
                                  const GTime&        obsTime,
                                  const GObservation& obs) const
{
    // Initialise result
    double npred     = 0.0;
    bool   has_npred = false;

    // Build unique identifier
    std::string id = obs.instrument() + "::" + obs.id();

    // Check if Npred value is already in cache
    #if defined(G_USE_NPRED_CACHE)
    if (!m_npred_names.empty()) {

        // Search for unique identifier, and if found, recover Npred value
		// and break
		for (int i = 0; i < m_npred_names.size(); ++i) {
			if (m_npred_names[i] == id && m_npred_energies[i] == obsEng) {
				npred     = m_npred_values[i];
				has_npred = true;
				#if defined(G_DEBUG_NPRED)
				std::cout << "GCTAModelBackground::npred:";
				std::cout << " cache=" << i;
				std::cout << " npred=" << npred << std::endl;
				#endif
				break;
			}
		}

    } // endif: there were values in the Npred cache
    #endif

    // Continue only if no Npred cache value was found
    if (!has_npred) {

        // Evaluate only if model is valid
        if (valid_model()) {

            // Get CTA event list
			const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events());
            if (events == NULL) {
                std::string msg = "No CTA event list found in observation.\n" +
                                  obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            #if !defined(G_NPRED_AROUND_ROI)
			// Get CTA pointing direction
			GCTAPointing* pnt = dynamic_cast<GCTAPointing*>(obs.pointing());
            if (pnt == NULL) {
                std::string msg = "No CTA pointing found in observation.\n" +
                                  obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }
            #endif

            // Get reference to ROI centre
            const GSkyDir& roi_centre = events->roi().centre().dir();

			// Get ROI radius in radians
			double roi_radius = events->roi().radius() * gammalib::deg2rad;

			// Get distance from ROI centre in radians
            #if defined(G_NPRED_AROUND_ROI)
			double roi_distance = 0.0;
            #else
			double roi_distance = roi_centre.dist(pnt->dir());
            #endif

			// Initialise rotation matrix to transform from ROI system to
            // celestial coordinate system
			GMatrix ry;
			GMatrix rz;
			ry.eulery(roi_centre.dec_deg() - 90.0);
			rz.eulerz(-roi_centre.ra_deg());
			GMatrix rot = (ry * rz).transpose();

			// Compute position angle of ROI centre with respect to model
			// centre (radians)
            #if defined(G_NPRED_AROUND_ROI)
            double omega0 = 0.0;
            #else
			double omega0 = pnt->dir().posang(events->roi().centre().dir());
            #endif

			// Setup integration function
			GCTAModelBackground::npred_roi_kern_theta integrand(spatial(),
                                                                obsEng,
                                                                obsTime,
                                                                rot,
                                                                roi_radius,
                                                                roi_distance,
                                                                omega0);

			// Setup integrator
			GIntegral integral(&integrand);
			integral.eps(1e-3);

			// Setup integration boundaries
            #if defined(G_NPRED_AROUND_ROI)
			double rmin = 0.0;
			double rmax = roi_radius;
            #else
			double rmin = (roi_distance > roi_radius) ? roi_distance-roi_radius : 0.0;
			double rmax = roi_radius + roi_distance;
            #endif

			// Spatially integrate radial component
			npred = integral.romb(rmin, rmax);

	        // Store result in Npred cache
	        #if defined(G_USE_NPRED_CACHE)
	        m_npred_names.push_back(id);
	        m_npred_energies.push_back(obsEng);
	        m_npred_times.push_back(obsTime);
	        m_npred_values.push_back(npred);
	        #endif

	        // Debug: Check for NaN
	        #if defined(G_NAN_CHECK)
	        if (gammalib::is_notanumber(npred) || gammalib::is_infinite(npred)) {
	            std::cout << "*** ERROR: GCTAModelBackground::npred:";
	            std::cout << " NaN/Inf encountered";
	            std::cout << " (npred=" << npred;
	            std::cout << ", roi_radius=" << roi_radius;
	            std::cout << ")" << std::endl;
	        }
	        #endif

        } // endif: model was valid

    } // endif: Npred computation required

	// Multiply in spectral and temporal components
	npred *= spectral()->eval(obsEng, obsTime);
	npred *= temporal()->eval(obsTime);

	// Apply deadtime correction
	npred *= obs.deadc(obsTime);

    // Return Npred
    return npred;
}
/***********************************************************************//**
 * @brief Evaluate function and gradients
 *
 * @param[in] event Observed event.
 * @param[in] obs Observation.
 * @return Function value.
 *
 * @exception GException::invalid_argument
 *            No CTA instrument direction found in event.
 *
 * Evaluates tha CTA background model and parameter gradients. The CTA
 * background model is a factorization of a spatial, spectral and
 * temporal model component. This method also applies a deadtime correction
 * factor, so that the normalization of the model is a real rate
 * (counts/exposure time).
 *
 * @todo Add bookkeeping of last value and evaluate only if argument 
 *       changed
 ***************************************************************************/
double GCTAModelBackground::eval_gradients(const GEvent& event,
                                           const GObservation& obs) const
{
    // Get pointer on CTA observation
    const GCTAObservation* ctaobs = dynamic_cast<const GCTAObservation*>(&obs);
    if (ctaobs == NULL) {
        std::string msg = "Specified observation is not a CTA observation.\n" +
                          obs.print();
        throw GException::invalid_argument(G_EVAL_GRADIENTS, msg);
    }

    // Extract CTA instrument direction
    const GCTAInstDir* dir  = dynamic_cast<const GCTAInstDir*>(&(event.dir()));
    if (dir == NULL) {
        std::string msg = "No CTA instrument direction found in event.";
        throw GException::invalid_argument(G_EVAL_GRADIENTS, msg);
    }

    // Create a Photon from the event
    // We need the photon to evaluate the spatial model
    // For the background, GEvent and GPhoton are identical
    // since the IRFs are not folded in
    GPhoton photon = GPhoton(dir->dir(), event.energy(),event.time());

    // Evaluate function and gradients
    double spat = (spatial() != NULL)
                  ? spatial()->eval_gradients(photon) : 1.0;
    double spec = (spectral() != NULL)
                  ? spectral()->eval_gradients(event.energy(), event.time()) : 1.0;
    double temp = (temporal() != NULL)
                  ? temporal()->eval_gradients(event.time()) : 1.0;

    // Compute value
    double value = spat * spec * temp;

    // Apply deadtime correction
    double deadc = obs.deadc(event.time());
    value       *= deadc;

    // Multiply factors to spatial gradients
    if (spatial() != NULL) {
        double fact = spec * temp * deadc;
        if (fact != 1.0) {
            for (int i = 0; i < spatial()->size(); ++i)
                (*spatial())[i].factor_gradient( (*spatial())[i].factor_gradient() * fact );
        }
    }

    // Multiply factors to spectral gradients
    if (spectral() != NULL) {
        double fact = spat * temp * deadc;
        if (fact != 1.0) {
            for (int i = 0; i < spectral()->size(); ++i)
                (*spectral())[i].factor_gradient( (*spectral())[i].factor_gradient() * fact );
        }
    }

    // Multiply factors to temporal gradients
    if (temporal() != NULL) {
        double fact = spat * spec * deadc;
        if (fact != 1.0) {
            for (int i = 0; i < temporal()->size(); ++i)
                (*temporal())[i].factor_gradient( (*temporal())[i].factor_gradient() * fact );
        }
    }

    // Return value
    return value;
}
/***********************************************************************//**
 * @brief Return spatially integrated background model
 *
 * @param[in] obsEng Measured event energy.
 * @param[in] obsTime Measured event time.
 * @param[in] obs Observation.
 * @return Spatially integrated model.
 *
 * @exception GException::invalid_argument
 *            The specified observation is not a CTA observation.
 *
 * Spatially integrates the effective area background model for a given
 * measured event energy and event time. This method also applies a deadtime
 * correction factor, so that the normalization of the model is a real rate
 * (counts/MeV/s).
 ***************************************************************************/
double GCTAModelAeffBackground::npred(const GEnergy&      obsEng,
                                      const GTime&        obsTime,
                                      const GObservation& obs) const
{
    // Set number of iterations for Romberg integration.
    //static const int iter_theta = 6;
    //static const int iter_phi   = 6;

    // Initialise result
    double npred     = 0.0;
    bool   has_npred = false;

    // Build unique identifier
    std::string id = obs.instrument() + "::" + obs.id();

    // Check if Npred value is already in cache
    #if defined(G_USE_NPRED_CACHE)
    if (!m_npred_names.empty()) {

        // Search for unique identifier, and if found, recover Npred value
        // and break
        for (int i = 0; i < m_npred_names.size(); ++i) {
            if (m_npred_names[i] == id && m_npred_energies[i] == obsEng) {
                npred     = m_npred_values[i];
                has_npred = true;
                #if defined(G_DEBUG_NPRED)
                std::cout << "GCTAModelAeffBackground::npred:";
                std::cout << " cache=" << i;
                std::cout << " npred=" << npred << std::endl;
                #endif
                break;
            }
        }

    } // endif: there were values in the Npred cache
    #endif

    // Continue only if no Npred cache value has been found
    if (!has_npred) {

        // Evaluate only if model is valid
        if (valid_model()) {

            // Get log10 of energy in TeV
            double logE = obsEng.log10TeV();

            // Spatially integrate effective area component
            npred = this->aeff_integral(obs, logE);

            // Store result in Npred cache
            #if defined(G_USE_NPRED_CACHE)
            m_npred_names.push_back(id);
            m_npred_energies.push_back(obsEng);
            m_npred_times.push_back(obsTime);
            m_npred_values.push_back(npred);
            #endif

            // Debug: Check for NaN
            #if defined(G_NAN_CHECK)
            if (gammalib::is_notanumber(npred) || gammalib::is_infinite(npred)) {
                std::string origin  = "GCTAModelAeffBackground::npred";
                std::string message = " NaN/Inf encountered (npred=" +
                                      gammalib::str(npred) + ")";
                gammalib::warning(origin, message);
            }
            #endif

        } // endif: model was valid

    } // endif: Npred computation required

    // Multiply in spectral and temporal components
    npred *= spectral()->eval(obsEng, obsTime);
    npred *= temporal()->eval(obsTime);

    // Apply deadtime correction
    npred *= obs.deadc(obsTime);

    // Return Npred
    return npred;
}
/***********************************************************************//**
 * @brief Evaluate function
 *
 * @param[in] event Observed event.
 * @param[in] obs Observation.
 * @param[in] gradients Compute gradients?
 * @return Function value.
 *
 * @exception GException::invalid_argument
 *            Specified observation is not of the expected type.
 *
 * If the @p gradients flag is true the method will also set the parameter
 * gradients of the model parameters.
 *
 * @todo Make sure that DETX and DETY are always set in GCTAInstDir.
 ***************************************************************************/
double GCTAModelAeffBackground::eval(const GEvent&       event,
                                     const GObservation& obs,
                                     const bool&         gradients) const
{
    // Get pointer on CTA observation
    const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>(&obs);
    if (cta == NULL) {
        std::string msg = "Specified observation is not a CTA observation.\n" +
                          obs.print();
        throw GException::invalid_argument(G_EVAL, msg);
    }

    // Get pointer on CTA IRF response
    const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(cta->response());
    if (rsp == NULL) {
        std::string msg = "Specified observation does not contain an IRF response.\n" +
                          obs.print();
        throw GException::invalid_argument(G_EVAL, msg);
    }

    // Retrieve pointer to CTA Effective Area
    const GCTAAeff* aeff = rsp->aeff();
    if (aeff == NULL) {
        std::string msg = "Specified observation contains no effective area"
                          " information.\n" + obs.print();
        throw GException::invalid_argument(G_EVAL, msg);
    }

    // Extract CTA instrument direction from event
    const GCTAInstDir* dir  = dynamic_cast<const GCTAInstDir*>(&(event.dir()));
    if (dir == NULL) {
        std::string msg = "No CTA instrument direction found in event.";
        throw GException::invalid_argument(G_EVAL, msg);
    }

    // Set DETX and DETY in instrument direction
    GCTAInstDir inst_dir = cta->pointing().instdir(dir->dir());

    // Set theta and phi from instrument coordinates
    double theta = std::sqrt(inst_dir.detx() * inst_dir.detx() +
                             inst_dir.dety() * inst_dir.dety());
    double phi   = gammalib::atan2d(inst_dir.dety(), inst_dir.detx()) *
                   gammalib::deg2rad;

    // Evaluate function
    double logE = event.energy().log10TeV();
    double spat = (*aeff)(logE, theta, phi,
                          cta->pointing().zenith(),
                          cta->pointing().azimuth(), false);
    double spec = (spectral() != NULL)
                  ? spectral()->eval(event.energy(), event.time(), gradients)
                  : 1.0;
    double temp = (temporal() != NULL)
                  ? temporal()->eval(event.time(), gradients) : 1.0;

    // Compute value
    double value = spat * spec * temp;

    // Apply deadtime correction
    double deadc = obs.deadc(event.time());
    value       *= deadc;

    // Optionally compute partial derivatives
    if (gradients) {

        // Multiply factors to spectral gradients
        if (spectral() != NULL) {
            double fact = spat * temp * deadc;
            if (fact != 1.0) {
                for (int i = 0; i < spectral()->size(); ++i)
                    (*spectral())[i].factor_gradient((*spectral())[i].factor_gradient() * fact );
            }
        }

        // Multiply factors to temporal gradients
        if (temporal() != NULL) {
            double fact = spat * spec * deadc;
            if (fact != 1.0) {
                for (int i = 0; i < temporal()->size(); ++i)
                    (*temporal())[i].factor_gradient((*temporal())[i].factor_gradient() * fact );
            }
        }

    } // endif: computed partial derivatives

    // Return value
    return value;
}
/***********************************************************************//**
 * @brief Return spatially integrated background model
 *
 * @param[in] obsEng Measured event energy.
 * @param[in] obsTime Measured event time.
 * @param[in] obs Observation.
 * @return Spatially integrated model.
 *
 * @exception GException::invalid_argument
 *            The specified observation is not a CTA observation.
 *
 * Spatially integrates the instrumental background model for a given
 * measured event energy and event time. This method also applies a deadtime
 * correction factor, so that the normalization of the model is a real rate
 * (counts/MeV/s).
 ***************************************************************************/
double GCTAModelIrfBackground::npred(const GEnergy&      obsEng,
                                     const GTime&        obsTime,
                                     const GObservation& obs) const
{
    // Initialise result
    double npred     = 0.0;
    bool   has_npred = false;

    // Build unique identifier
    std::string id = obs.instrument() + "::" + obs.id();

    // Check if Npred value is already in cache
#if defined(G_USE_NPRED_CACHE)
    if (!m_npred_names.empty()) {

        // Search for unique identifier, and if found, recover Npred value
        // and break
        for (int i = 0; i < m_npred_names.size(); ++i) {
            if (m_npred_names[i] == id && m_npred_energies[i] == obsEng) {
                npred     = m_npred_values[i];
                has_npred = true;
#if defined(G_DEBUG_NPRED)
                std::cout << "GCTAModelIrfBackground::npred:";
                std::cout << " cache=" << i;
                std::cout << " npred=" << npred << std::endl;
#endif
                break;
            }
        }

    } // endif: there were values in the Npred cache
#endif

    // Continue only if no Npred cache value has been found
    if (!has_npred) {

        // Evaluate only if model is valid
        if (valid_model()) {

            // Get pointer on CTA observation
            const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>(&obs);
            if (cta == NULL) {
                std::string msg = "Specified observation is not a CTA"
                                  " observation.\n" + obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Get pointer on CTA IRF response
            const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(cta->response());
            if (rsp == NULL) {
                std::string msg = "Specified observation does not contain"
                                  " an IRF response.\n" + obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Retrieve pointer to CTA background
            const GCTABackground* bgd = rsp->background();
            if (bgd == NULL) {
                std::string msg = "Specified observation contains no background"
                                  " information.\n" + obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Get CTA event list
            const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events());
            if (events == NULL) {
                std::string msg = "No CTA event list found in observation.\n" +
                                  obs.print();
                throw GException::invalid_argument(G_NPRED, msg);
            }

            // Get reference to ROI centre
            const GSkyDir& roi_centre = events->roi().centre().dir();

            // Get ROI radius in radians
            double roi_radius = events->roi().radius() * gammalib::deg2rad;

            // Get log10 of energy in TeV
            double logE = obsEng.log10TeV();

            // Setup integration function
            GCTAModelIrfBackground::npred_roi_kern_theta integrand(bgd, logE);

            // Setup integrator
            GIntegral integral(&integrand);
            integral.eps(g_cta_inst_background_npred_theta_eps);

            // Spatially integrate radial component
            npred = integral.romberg(0.0, roi_radius);

            // Store result in Npred cache
#if defined(G_USE_NPRED_CACHE)
            m_npred_names.push_back(id);
            m_npred_energies.push_back(obsEng);
            m_npred_times.push_back(obsTime);
            m_npred_values.push_back(npred);
#endif

            // Debug: Check for NaN
#if defined(G_NAN_CHECK)
            if (gammalib::is_notanumber(npred) || gammalib::is_infinite(npred)) {
                std::string origin  = "GCTAModelIrfBackground::npred";
                std::string message = " NaN/Inf encountered (npred=" +
                                      gammalib::str(npred) + ", roi_radius=" +
                                      gammalib::str(roi_radius) + ")";
                gammalib::warning(origin, message);
            }
#endif

        } // endif: model was valid

    } // endif: Npred computation required

    // Multiply in spectral and temporal components
    npred *= spectral()->eval(obsEng, obsTime);
    npred *= temporal()->eval(obsTime);

    // Apply deadtime correction
    npred *= obs.deadc(obsTime);

    // Return Npred
    return npred;
}
Exemple #9
0
/***********************************************************************//**
 * @brief Return value of instrument response function
 *
 * @param[in] event Observed event.
 * @param[in] photon Incident photon.
 * @param[in] obs Observation.
 * @return Instrument response function (cm2 sr-1)
 *
 * @exception GException::invalid_argument
 *            Observation is not a COMPTEL observation.
 *            Event is not a COMPTEL event bin.
 *
 * Returns the instrument response function for a given observed photon
 * direction as function of the assumed true photon direction. The result
 * is given by
 * \f[IRF = \frac{IAQ \times DRG \times DRX}{ontime \times ewidth}\f]
 * where
 * \f$IRF\f$ is the instrument response function,
 * \f$IAQ\f$ is the COMPTEL response matrix (sr-1),
 * \f$DRG\f$ is the geometry factor (cm2),
 * \f$DRX\f$ is the exposure (s),
 * \f$ontime\f$ is the ontime (s), and
 * \f$ewidth\f$ is the energy width (MeV).
 *
 * The observed photon direction is spanned by the 3 values (Chi,Psi,Phibar).
 * (Chi,Psi) is the scatter direction of the event, given in sky coordinates.
 * Phibar is the Compton scatter angle, computed from the energy deposits.
 ***************************************************************************/
double GCOMResponse::irf(const GEvent&       event,
                         const GPhoton&      photon,
                         const GObservation& obs) const
{
    // Extract COMPTEL observation
    const GCOMObservation* observation = dynamic_cast<const GCOMObservation*>(&obs);
    if (observation == NULL) {
        std::string cls = std::string(typeid(&obs).name());
        std::string msg = "Observation of type \""+cls+"\" is not a COMPTEL "
                          "observations. Please specify a COMPTEL observation "
                          "as argument.";
        throw GException::invalid_argument(G_IRF, msg);
    }

    // Extract COMPTEL event bin
    const GCOMEventBin* bin = dynamic_cast<const GCOMEventBin*>(&event);
    if (bin == NULL) {
        std::string cls = std::string(typeid(&event).name());
        std::string msg = "Event of type \""+cls+"\" is  not a COMPTEL event. "
                          "Please specify a COMPTEL event as argument.";
        throw GException::invalid_argument(G_IRF, msg);
    }

    // Extract event parameters
    const GCOMInstDir& obsDir = bin->dir();

    // Extract photon parameters
    const GSkyDir& srcDir  = photon.dir();
    const GTime&   srcTime = photon.time();

    // Compute angle between true photon arrival direction and scatter
    // direction (Chi,Psi)
    double phigeo = srcDir.dist_deg(obsDir.dir());

    // Compute scatter angle index
    int iphibar = int(obsDir.phibar() / m_phibar_bin_size);

    // Extract IAQ value by linear inter/extrapolation in Phigeo
    double iaq = 0.0;
    if (iphibar < m_phibar_bins) {
        double phirat  = phigeo / m_phigeo_bin_size; // 0.5 at bin centre
        int    iphigeo = int(phirat);                // index into which Phigeo falls
        double eps     = phirat - iphigeo - 0.5;     // 0.0 at bin centre
        if (iphigeo < m_phigeo_bins) {
            int i = iphibar * m_phigeo_bins + iphigeo;
            if (eps < 0.0) { // interpolate towards left
                if (iphigeo > 0) {
                    iaq = (1.0 + eps) * m_iaq[i] - eps * m_iaq[i-1];
                }
                else {
                    iaq = (1.0 - eps) * m_iaq[i] + eps * m_iaq[i+1];
                }
            }
            else {           // interpolate towards right
                if (iphigeo < m_phigeo_bins-1) {
                    iaq = (1.0 - eps) * m_iaq[i] + eps * m_iaq[i+1];
                }
                else {
                    iaq = (1.0 + eps) * m_iaq[i] - eps * m_iaq[i-1];
                }
            }
        }
    }

    // Get DRG value (units: cm2)
    double drg = observation->drg()(obsDir.dir(), iphibar);

    // Get DRX value (units: sec)
    double drx = observation->drx()(srcDir);

    // Get ontime
    double ontime = observation->ontime(); // sec

    // Compute IRF value
    double irf = iaq * drg * drx / ontime;

    // Apply deadtime correction
    irf *= obs.deadc(srcTime);

    // Compile option: Check for NaN/Inf
    #if defined(G_NAN_CHECK)
    if (gammalib::is_notanumber(irf) || gammalib::is_infinite(irf)) {
        std::cout << "*** ERROR: GCOMResponse::irf:";
        std::cout << " NaN/Inf encountered";
        std::cout << " (irf=" << irf;
        std::cout << ", iaq=" << iaq;
        std::cout << ", drg=" << drg;
        std::cout << ", drx=" << drx;
        std::cout << ")";
        std::cout << std::endl;
    }
    #endif

    // Return IRF value
    return irf;
}