/***********************************************************************//** * @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 Read observations from XML document * * @param[in] xml XML document. * * @exception GException::invalid_instrument * Invalid instrument encountered. * * Reads observations from the first observation list that is found in the * XML document. The decoding of the instrument specific observation * definition is done within the observation's read() method. * * @todo Observation names and IDs are not verified so far for uniqueness. * This would be required to achieve an unambiguous update of parameters * in an already existing XML file when using the write method. ***************************************************************************/ void GObservations::read(const GXml& xml) { // Get pointer on observation library GXmlElement* lib = xml.element("observation_list", 0); // Loop over all observations int n = lib->elements("observation"); for (int i = 0; i < n; ++i) { // Get pointer on observation GXmlElement* obs = static_cast<GXmlElement*>(lib->element("observation", i)); // Get attributes std::string name = obs->attribute("name"); std::string id = obs->attribute("id"); std::string instrument = obs->attribute("instrument"); // Get model GObservationRegistry registry; GObservation* ptr = registry.alloc(instrument); // If observation is valid then read its definition from XML file if (ptr != NULL) { // Read definition ptr->read(*obs); // Set attributes ptr->name(name); ptr->id(id); } // endif: observation was valid // ... otherwise throw an exception else { throw GException::invalid_instrument(G_READ, instrument); } // Append observation to container append(*ptr); // Free model (appending clones the observation) delete ptr; } // endfor: looped over all observations // Return return; }
/***********************************************************************//** * @brief Append observation to container * * @param[in] obs Observation. * * This method appends an observation to the container by cloning it. ***************************************************************************/ void GObservations::append(GObservation& obs) { // Clone observation and append to list m_obs.push_back(obs.clone()); // Return return; }
/***********************************************************************//** * @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; }
/***********************************************************************//** * @brief Return spatially integrated sky model * * @param[in] obsEng Measured photon energy. * @param[in] obsTime Measured photon arrival time. * @param[in] obs Observation. * * @exception GException::no_response * No valid instrument response function defined. * * Computes * \f[N"_{\rm pred} = \int_{\rm ROI} * S(\vec{p}, E, t) PSF(\vec{p'}, E', t' | \vec{d}, \vec{p}, E, t) \, * {\rm d}\vec{p'}\f] * where * \f$S(\vec{p}, E, t)\f$ is the source model, * \f$PSF(\vec{p'}, E', t' | \vec{d}, \vec{p}, E, t)\f$ is the point * spread function, * \f$\vec{p'}\f$ is the measured photon direction, * \f$E'\f$ is the measured photon energy, * \f$t'\f$ is the measured photon arrival time, * \f$\vec{p}\f$ is the true photon arrival direction, * \f$E\f$ is the true photon energy, * \f$t\f$ is the true photon arrival time, and * \f$d\f$ is the instrument pointing. * * \f${\rm ROI}\f$ is the region of interest that is stored in the * GObservation::m_roi member. The integration over the ROI is performed * by the GResponse::npred() method. * * @todo The actual method is only correct if no energy and time dispersion * exists. For the moment we set srcEng=obsEng and srcTime=obsTime. * Formally, Equation (2) of the instrument document has to be * computed, which is an integration over source energy, time * and arrival direction. For the moment, only the integration over * arrival direction is performed by GResponse::npred(). ***************************************************************************/ double GModelSky::npred(const GEnergy& obsEng, const GTime& obsTime, const GObservation& obs) const { // Initialise result double npred = 0.0; // Continue only if model is valid) if (valid_model()) { // Get response function GResponse* rsp = obs.response(); if (rsp == NULL) { throw GException::no_response(G_NPRED); } // Here we make the simplifying approximations // srcEng=obsEng and srcTime=obsTime. To be fully correct we should // integrate over true energy and true time here ... at least true // time if we want to consider energy dispersion ... GEnergy srcEng = obsEng; GTime srcTime = obsTime; // Set source GSource source(this->name(), *m_spatial, srcEng, srcTime); // Compute response components double npred_spatial = rsp->npred(source, obs); double npred_spectral = spectral()->eval(srcEng); double npred_temporal = temporal()->eval(srcTime); // Compute response npred = npred_spatial * npred_spectral * npred_temporal; // Compile option: Check for NaN/Inf #if defined(G_NAN_CHECK) if (isnotanumber(npred) || isinfinite(npred)) { std::cout << "*** ERROR: GModelSky::npred:"; std::cout << " NaN/Inf encountered"; std::cout << " (npred=" << npred; std::cout << ", npred_spatial=" << npred_spatial; std::cout << ", npred_spectral=" << npred_spectral; std::cout << ", npred_temporal=" << npred_temporal; std::cout << ", srcEng=" << srcEng; std::cout << ", srcTime=" << srcTime; std::cout << ")" << std::endl; } #endif } // endif: model was valid // Return npred return npred; }
/***********************************************************************//** * @brief Perform integration over spectral component * * @param[in] event Observed event. * @param[in] srcTime True photon arrival time. * @param[in] obs Observation. * @param[in] grad Evaluate gradients. * * @exception GException::no_response * Observation has no valid instrument response * @exception GException::feature_not_implemented * Energy integration not yet implemented * * This method integrates the source model over the spectral component. If * the response function has no energy dispersion then no spectral * integration is needed and the observed photon energy is identical to the * true photon energy. * * @todo Needs implementation of spectral integration to handle energy * dispersion. ***************************************************************************/ double GModelSky::spectral(const GEvent& event, const GTime& srcTime, const GObservation& obs, bool grad) const { // Initialise result double value = 0.0; // Get response function GResponse* rsp = obs.response(); if (rsp == NULL) { throw GException::no_response(G_SPECTRAL); } // Determine if energy integration is needed bool integrate = rsp->hasedisp(); // Case A: Integraion if (integrate) { throw GException::feature_not_implemented(G_SPECTRAL); } // Case B: No integration (assume no energy dispersion) else { value = spatial(event, event.energy(), srcTime, obs, grad); } // Compile option: Check for NaN/Inf #if defined(G_NAN_CHECK) if (isnotanumber(value) || isinfinite(value)) { std::cout << "*** ERROR: GModelSky::spectral:"; std::cout << " NaN/Inf encountered"; std::cout << " (value=" << value; std::cout << ", event=" << event; std::cout << ", srcTime=" << srcTime; std::cout << ")" << std::endl; } #endif // Return value return value; }
/***********************************************************************//** * @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 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; }
/***********************************************************************//** * @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 Spatially integrate effective area for given energy * * @param[in] obs Observation. * @param[in] logE Log10 of reference energy in TeV. * @return Spatially integrated effective area for given energy. * * @exception GException::invalid_argument * Invalid observation encountered. * * Spatially integrates the effective area for a given reference energy * over the region of interest. ***************************************************************************/ double GCTAModelAeffBackground::aeff_integral(const GObservation& obs, const double& logE) const { // Initialise result double value = 0.0; // Set number of iterations for Romberg integration. static const int iter_theta = 6; static const int iter_phi = 6; // 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 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_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 ROI radius in radians double roi_radius = events->roi().radius() * gammalib::deg2rad; // Setup integration function GCTAModelAeffBackground::npred_roi_kern_theta integrand(aeff, logE, iter_phi); // Setup integration GIntegral integral(&integrand); // Set fixed number of iterations integral.fixed_iter(iter_theta); // Spatially integrate radial component value = integral.romberg(0.0, roi_radius); // Debug: Check for NaN #if defined(G_NAN_CHECK) if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { std::string origin = "GCTAModelAeffBackground::aeff_integral"; std::string message = " NaN/Inf encountered (value=" + gammalib::str(value) + ", roi_radius=" + gammalib::str(roi_radius) + ")"; gammalib::warning(origin, message); } #endif // Return 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; }
/***********************************************************************//** * @brief Evaluate function and gradients * * @param[in] event Observed event. * @param[in] obs Observation. * @return Function value. * * @exception GException::invalid_argument * Specified observation is not of the expected type. ***************************************************************************/ double GCTAModelCubeBackground::eval_gradients(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_GRADIENTS, msg); } // Get pointer on CTA IRF 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_EVAL_GRADIENTS, 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_GRADIENTS, msg); } // Retrieve reference to CTA cube background const GCTACubeBackground& bgd = rsp->background(); // Evaluate function //double logE = event.energy().log10TeV(); double spat = bgd((*dir), event.energy()); 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. Note that background rates are already per // livetime, hence no deadtime correction is needed here. double value = spat * spec * temp; // Multiply factors to spectral gradients if (spectral() != NULL) { double fact = spat * temp; 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; 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 Returns spatial model component * * @param[in] event Observed event. * @param[in] srcEng True photon energy. * @param[in] srcTime True photon arrival time. * @param[in] obs Observation. * @param[in] grad Evaluate gradients. * * @exception GException::no_response * Observation has no valid instrument response * * This method computes the spatial model component for a given true photon * energy and arrival time. ***************************************************************************/ double GModelSky::spatial(const GEvent& event, const GEnergy& srcEng, const GTime& srcTime, const GObservation& obs, bool grad) const { // Initialise result double value = 0.0; // Continue only if the model has a spatial component if (m_spatial != NULL) { // Get response function GResponse* rsp = obs.response(); if (rsp == NULL) { throw GException::no_response(G_SPATIAL); } // Set source GSource source(this->name(), *m_spatial, srcEng, srcTime); // Get IRF value. This method returns the spatial component of the // source model. double irf = rsp->irf(event, source, obs); // Case A: evaluate gradients if (grad) { // Evaluate source model double spec = (spectral() != NULL) ? spectral()->eval_gradients(srcEng) : 1.0; double temp = (temporal() != NULL) ? temporal()->eval_gradients(srcTime) : 1.0; // Set value value = spec * temp * irf; // Compile option: Check for NaN/Inf #if defined(G_NAN_CHECK) if (isnotanumber(value) || isinfinite(value)) { std::cout << "*** ERROR: GModelSky::spatial:"; std::cout << " NaN/Inf encountered"; std::cout << " (value=" << value; std::cout << ", spec=" << spec; std::cout << ", temp=" << temp; std::cout << ", irf=" << irf; std::cout << ")" << std::endl; } #endif // Multiply factors to spectral gradients if (spectral() != NULL) { double fact = temp * irf; if (fact != 1.0) { for (int i = 0; i < spectral()->size(); ++i) { (*spectral())[i].gradient((*spectral())[i].gradient() * fact); } } } // Multiply factors to temporal gradients if (temporal() != NULL) { double fact = spec * irf; if (fact != 1.0) { for (int i = 0; i < temporal()->size(); ++i) { (*temporal())[i].gradient((*temporal())[i].gradient() * fact); } } } } // endif: gradient evaluation has been requested // Case B: evaluate no gradients else { // Evaluate source model double spec = (m_spectral != NULL) ? m_spectral->eval(srcEng) : 1.0; double temp = (m_temporal != NULL) ? m_temporal->eval(srcTime) : 1.0; // Set value value = spec * temp * irf; // Compile option: Check for NaN/Inf #if defined(G_NAN_CHECK) if (isnotanumber(value) || isinfinite(value)) { std::cout << "*** ERROR: GModelSky::spatial:"; std::cout << " NaN/Inf encountered"; std::cout << " (value=" << value; 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 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 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 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); // Compute IRF value double irf = this->irf(event, source, obs); // Continue only if IRF value is positive if (irf > 0.0) { // If required, apply instrument specific model scaling if (model.has_scales()) { irf *= model.scale(obs.instrument()).value(); } // Evaluate spectral and temporal components double spec = (model.spectral() != NULL) ? model.spectral()->eval(srcEng, srcTime, grad) : 1.0; double temp = (model.temporal() != NULL) ? model.temporal()->eval(srcTime, grad) : 1.0; // Compute probability prob = spec * temp * irf; // Optionally compute partial derivatives if (grad) { // 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: computed partial derivatives // 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: IRF value was positive // ... otherwise if gradient computation is requested then set the // spectral and temporal gradients to zero else if (grad) { // Reset spectral gradients if (model.spectral() != NULL) { for (int i = 0; i < model.spectral()->size(); ++i) { (*model.spectral())[i].factor_gradient(0.0); } } // Reset temporal gradients if (model.temporal() != NULL) { for (int i = 0; i < model.temporal()->size(); ++i) { (*model.temporal())[i].factor_gradient(0.0); } } } // endelse: IRF value was not positive } // endif: Gamma-ray source model had a spatial component // Return event probability return prob; }