/***************************************************************************
* ctskymap - Sky mapping tool *
* ----------------------------------------------------------------------- *
* copyright (C) 2011-2017 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 ctskymap.cpp
* @brief Sky mapping tool implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "ctskymap.hpp"
#include "GSkyRegions.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_GET_PARAMETERS "ctskymap::get_parameter()"
#define G_MAP_EVENTS "ctskymap::map_events(GCTAObservation*)"
#define G_MAP_BACKGROUND_IRF "ctskymap::map_background_irf(GCTAObservation*)"
#define G_MAP_BACKGROUND_RING "ctskymap::map_background_ring("\
"GCTAObservation*)"
/* __ Debug definitions __________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
*
* Constructs an empty sky mapping tool.
***************************************************************************/
ctskymap::ctskymap(void) : ctobservation(CTSKYMAP_NAME, VERSION)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Observations constructor
*
* param[in] obs Observation container.
*
* Constructs sky mapping tool from an observation container.
***************************************************************************/
ctskymap::ctskymap(const GObservations& obs) :
ctobservation(CTSKYMAP_NAME, VERSION, obs)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Command line constructor
*
* @param[in] argc Number of arguments in command line.
* @param[in] argv Array of command line arguments.
*
* Constructs sky mapping tool using command line arguments for user
* parameter setting.
***************************************************************************/
ctskymap::ctskymap(int argc, char *argv[]) :
ctobservation(CTSKYMAP_NAME, VERSION, argc, argv)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] app Sky mapping tool.
***************************************************************************/
ctskymap::ctskymap(const ctskymap& app) : ctobservation(app)
{
// Initialise members
init_members();
// Copy members
copy_members(app);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
ctskymap::~ctskymap(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] app Sky mapping tool.
* @return Sky mapping tool.
***************************************************************************/
ctskymap& ctskymap::operator=(const ctskymap& app)
{
// Execute only if object is not identical
if (this != &app) {
// Copy base class members
this->ctobservation::operator=(app);
// Free members
free_members();
// Initialise members
init_members();
// Copy members
copy_members(app);
} // endif: object was not identical
// Return this object
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear sky mapping tool
*
* Clears sky mapping tool.
***************************************************************************/
void ctskymap::clear(void)
{
// Free members
free_members();
this->ctool::free_members();
this->ctobservation::free_members();
// Clear base class (needed to conserve tool name and version)
this->GApplication::clear();
// Initialise members
this->ctool::init_members();
this->ctobservation::init_members();
init_members();
// Write header into logger
log_header();
// Return
return;
}
/***********************************************************************//**
* @brief Run the sky mapping tool
*
* Generates a sky map from event list by looping over all unbinned CTA
* observation in the observation container and filling all events into
* a sky map.
***************************************************************************/
void ctskymap::run(void)
{
// Switch screen logging on in debug mode
if (logDebug()) {
log.cout(true);
}
// Get parameters
get_parameters();
// Write input observation container into logger
log_observations(NORMAL, m_obs, "Input observation");
// Write header into logger
log_header1(TERSE, gammalib::number("Map observation", m_obs.size()));
// Loop over all unbinned CTA observations in the container
for (GCTAObservation* obs = first_unbinned_observation(); obs != NULL;
obs = next_unbinned_observation()) {
// Map events into sky map
map_events(obs);
// Compute background sky map
map_background(obs);
// Dispose events to free memory
obs->dispose_events();
} // endfor: looped over observations
// If background subtraction is selected then compute significance map
if (m_bkgsubtract != "NONE") {
// Compute the significance map
map_significance();
// Subtract background map from counts map
m_skymap -= m_bkgmap;
}
// Optionally publish sky map
if (m_publish) {
publish();
}
// Return
return;
}
/***********************************************************************//**
* @brief Save sky map
*
* Saves the sky map into a FITS file. The FITS file name is specified by the
* @p outname parameter.
***************************************************************************/
void ctskymap::save(void)
{
// Write header
log_header1(TERSE, "Save sky map");
// Get sky map filename
m_outmap = (*this)["outmap"].filename();
// Save sky map if filename and the map are not empty
if (!m_outmap.is_empty() && !m_skymap.is_empty()) {
// Create empty FITS file
GFits fits;
// Write sky map into FITS file
GFitsHDU* hdu = m_skymap.write(fits);
// Write keywords into sky map extension
write_ogip_keywords(hdu);
write_hdu_keywords(hdu);
// If background subtraction is requested then write background map
// and significance map to FITS file
if (m_bkgsubtract != "NONE") {
// Write background map into FITS file
hdu = m_bkgmap.write(fits);
// Set background map extension name
if (hdu != NULL) {
hdu->extname("BACKGROUND");
}
// Write keywords into background extension
write_ogip_keywords(hdu);
write_hdu_keywords(hdu);
// Write significance map into FITS file
hdu = m_sigmap.write(fits);
// Set significance map extension name
if (hdu != NULL) {
hdu->extname("SIGNIFICANCE");
}
// Write keywords into significance extension
write_ogip_keywords(hdu);
write_hdu_keywords(hdu);
} // endif: background subtraction was requested
// Save FITS file to disk
fits.saveto(m_outmap, clobber());
} // endif: filename and map were not empty
// Write into logger what has been done
std::string fname = (m_outmap.is_empty()) ? "NONE" : m_outmap.url();
if (m_skymap.is_empty()) {
fname.append(" (map is empty, no file created)");
}
log_value(NORMAL, "Sky map file", fname);
// Return
return;
}
/***********************************************************************//**
* @brief Publish sky map
*
* @param[in] name Sky map name.
***************************************************************************/
void ctskymap::publish(const std::string& name)
{
// Write header into logger
log_header1(TERSE, "Publish sky map");
// Set default name if user name is empty
std::string user_name(name);
if (user_name.empty()) {
user_name = CTSKYMAP_NAME;
}
// Write sky map name into logger
log_value(NORMAL, "Sky map name", user_name);
// Publish sky map
m_skymap.publish(user_name);
// Return
return;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void ctskymap::init_members(void)
{
// Initialise members
m_skymap.clear();
m_bkgmap.clear();
m_sigmap.clear();
m_exclmap.clear();
m_alphamap.clear();
m_onmap.clear();
m_emin = 0.0;
m_emax = 0.0;
m_bkgsubtract = "NONE";
m_roiradius = 0.0;
m_inradius = 0.0;
m_outradius = 0.0;
m_publish = false;
m_chatter = static_cast(2);
// Initialise cache
m_solidangle.clear();
m_dirs.clear();
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] app Application.
***************************************************************************/
void ctskymap::copy_members(const ctskymap& app)
{
// Copy attributes
m_skymap = app.m_skymap;
m_bkgmap = app.m_bkgmap;
m_sigmap = app.m_sigmap;
m_exclmap = app.m_exclmap;
m_alphamap = app.m_alphamap;
m_onmap = app.m_onmap;
m_emin = app.m_emin;
m_emax = app.m_emax;
m_bkgsubtract = app.m_bkgsubtract;
m_roiradius = app.m_roiradius;
m_inradius = app.m_inradius;
m_outradius = app.m_outradius;
m_publish = app.m_publish;
m_chatter = app.m_chatter;
// Copy cache
m_solidangle = app.m_solidangle;
m_dirs = app.m_dirs;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void ctskymap::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Get application parameters
*
* Get all task parameters from parameter file or (if required) by querying
* the user. Most parameters are only required if no observation exists so
* far in the observation container. In this case, a single CTA observation
* will be added to the container, using the definition provided in the
* parameter file.
***************************************************************************/
void ctskymap::get_parameters(void)
{
// Setup observations from "inobs" parameter. Do not request response
// information and do not accept counts cubes.
setup_observations(m_obs, false, true, false);
// Create sky map based on task parameters
m_skymap = create_map(m_obs);
// Get further parameters
m_emin = (*this)["emin"].real();
m_emax = (*this)["emax"].real();
m_bkgsubtract = (*this)["bkgsubtract"].string();
// Get RING background parameters
if (m_bkgsubtract == "RING") {
// Get parameters
m_roiradius = (*this)["roiradius"].real();
m_inradius = (*this)["inradius"].real();
m_outradius = (*this)["outradius"].real();
// Make sure that (roiradius < inradius < outradius)
if (m_roiradius > m_inradius) {
std::string msg("'roiradius' must be smaller than 'inradius'");
throw GException::invalid_value(G_GET_PARAMETERS, msg);
}
else if (m_inradius > m_outradius) {
std::string msg("'inradius' must be smaller than 'outradius'");
throw GException::invalid_value(G_GET_PARAMETERS, msg);
}
} // endif: read RING background parameters
// If IRF background subtraction is requested then make sure that the
// CTA observations in the observation container have response information
if (m_bkgsubtract != "NONE") {
set_response(m_obs);
}
// Get remaining parameters
m_publish = (*this)["publish"].boolean();
m_chatter = static_cast((*this)["chatter"].integer());
// Read ahead parameters
if (read_ahead()) {
m_outmap = (*this)["outmap"].filename();
}
// Create background map and significance map if background subtraction
// is requested
if (m_bkgsubtract != "NONE") {
// Create backgrond and significance maps
m_bkgmap = create_map(m_obs);
m_sigmap = create_map(m_obs);
// Setup the exclusions map
map_exclusions((*this)["inexclusion"].filename());
// Cache the pixel solid angles and sky directions
m_solidangle.reserve(m_bkgmap.npix());
m_dirs.reserve(m_bkgmap.npix());
for (int i = 0; i < m_bkgmap.npix(); ++i) {
m_solidangle.push_back(m_bkgmap.solidangle(i));
m_dirs.push_back(m_bkgmap.inx2dir(i));
}
// If doing a ring background subtraction, generate an alpha map and
// make sure to cache the counts for each observation
if (m_bkgsubtract == "RING") {
m_alphamap = create_map(m_obs);
m_onmap = create_map(m_obs);
}
} // endif: background subtraction selected
// Write parameters into logger
log_parameters(TERSE);
// Return
return;
}
/***********************************************************************//**
* @brief Map events into a sky map
*
* @param[in] obs CTA observation.
*
* @exception GException::no_list
* No event list found in observation.
*
* This method maps the events found in a CTA events list into a sky map.
***************************************************************************/
void ctskymap::map_events(GCTAObservation* obs)
{
// Get non-const pointer on a CTA event list
GCTAEventList* events = dynamic_cast
(const_cast(obs->events()));
// Make sure that the observation holds a CTA event list. If this
// is not the case then throw an exception.
if (events == NULL) {
throw GException::no_list(G_MAP_EVENTS);
}
// Setup energy range covered by data
GEnergy emin;
GEnergy emax;
GEbounds ebds;
emin.TeV(m_emin);
emax.TeV(m_emax);
// Initialise binning statistics
int num_outside_roi = 0;
int num_outside_map = 0;
int num_outside_erange = 0;
int num_in_map = 0;
// Extract region of interest from observation
GCTARoi roi = obs->roi();
// Fill sky map
for (int i = 0; i < events->size(); ++i) {
// Get event
GCTAEventAtom* event = (*events)[i];
// Skip if energy is out of range
if (event->energy() < emin || event->energy() > emax) {
num_outside_erange++;
continue;
}
// Determine sky pixel
GCTAInstDir* inst = (GCTAInstDir*)&(event->dir());
GSkyDir dir = inst->dir();
GSkyPixel pixel = m_skymap.dir2pix(dir);
// Skip if pixel is out of range
if (pixel.x() < -0.5 || pixel.x() > (m_skymap.nx() - 0.5) ||
pixel.y() < -0.5 || pixel.y() > (m_skymap.ny() - 0.5)) {
num_outside_map++;
continue;
}
// If RoI is valid then skip if instrument direction is not within RoI
if (roi.is_valid() && !roi.contains(*inst)) {
num_outside_roi++;
continue;
}
// Fill event in skymap
m_skymap(pixel, 0) += 1.0;
num_in_map++;
} // endfor: looped over all events
// Log binning results
log_value(NORMAL, "Events in list", obs->events()->size());
log_value(NORMAL, "Events in map", num_in_map);
log_value(NORMAL, "Events outside RoI", num_outside_roi);
log_value(NORMAL, "Events outside map area", num_outside_map);
log_value(NORMAL, "Events outside energies", num_outside_erange);
// Write sky map into header
log_header1(EXPLICIT, "Sky map");
log_string(EXPLICIT, m_skymap.print(m_chatter));
// Return
return;
}
/***********************************************************************//**
* @brief Estimates the background in sky map for observation
*
* @param[in] obs CTA observation.
*
* Estimates the background in the sky map for a given observation and adds
* this estimate to the background sky map. The background estimation method
* is specified by the "bkgsubtract" parameter which can take the following
* values:
*
* NONE - No background estimation
* IRF - Background estimation based on IRF template
* RING - Ring background estimation
***************************************************************************/
void ctskymap::map_background(GCTAObservation* obs)
{
// Dispatch to appropriate background estimation method
if (m_bkgsubtract == "IRF") {
map_background_irf(obs);
}
else if (m_bkgsubtract == "RING") {
map_background_ring(obs);
}
// Return
return;
}
/***********************************************************************//**
* @brief Estimates the background in sky map based on IRF template
*
* @param[in] obs CTA observation.
*
* @exception GException::invalid_value
* No response information available for observation.
* No background template available in instrument response function.
*
* Estimates the background in the sky map using the IRF template for a given
* observation and adds this estimate to the background sky map. The IRF
* template is integrated numerically in energy.
***************************************************************************/
void ctskymap::map_background_irf(GCTAObservation* obs)
{
// Get IRF response
const GCTAResponseIrf* rsp = dynamic_cast(obs->response());
// Throw an exception if observation has no instrument response function
if (rsp == NULL) {
std::string msg = "No response information available for "+
get_obs_header(obs)+" to compute IRF background. "
"Please specify response information or use "
"another background subtraction method.";
throw GException::invalid_value(G_MAP_BACKGROUND_IRF, msg);
}
// Get IRF background template
const GCTABackground* bkg = rsp->background();
// Throw an exception if observation has no IRF background template
if (bkg == NULL) {
std::string msg = "No IRF background template found in instrument "
"response function for "+
get_obs_header(obs)+". Please specify an instrument "
"response function containing a background template.";
throw GException::invalid_value(G_MAP_BACKGROUND_IRF, msg);
}
// Compute natural logarithm of energy range in MeV
double lnEmin = std::log(m_emin * 1.0e6);
double lnEmax = std::log(m_emax * 1.0e6);
// Extract region of interest from observation
GCTARoi roi = obs->roi();
// Initialise statistics
int calls = 0;
double total = 0.0;
// Loop over all background map pixels
for (int i = 0; i < m_bkgmap.npix(); ++i) {
// Get sky direction of pixel
GSkyDir skydir = m_bkgmap.inx2dir(i);
// Convert sky direction in instrument direction
GCTAInstDir instdir = obs->pointing().instdir(skydir);
// If RoI is valid and instrument direction is not within RoI then
// skip pixel
if (roi.is_valid() && !roi.contains(instdir)) {
continue;
}
// Setup integration function
ctskymap::irf_kern integrand(bkg, &instdir);
GIntegral integral(&integrand);
// Set precision (has been carefully adjusted using a test simulation
// over the energy range 20 GeV - 120 TeV)
integral.eps(1.0e-6);
// Do Romberg integration
double value = integral.romberg(lnEmin, lnEmax);
// Update number of background function calls
calls += integral.calls();
// Multiply background rate with livetime and solid angle
value *= obs->livetime() * m_solidangle[i];
// Add number of background events to background map
m_bkgmap(i,0) += value;
// Update total number of background events
total += value;
} // endfor: looped over background map pixels
// Log background subtraction results
log_value(NORMAL, "Events in background", int(total+0.5));
log_value(NORMAL, "Background evaluations", calls);
// Return
return;
}
/***********************************************************************//**
* @brief Estimates the background in sky map based on the ring background
* method
*
* @param[in] obs CTA observation.
*
* @exception GException::invalid_value
* No response information available for observation.
* No background template available in instrument response function.
*
* Estimates the background in the sky map by summing the events within a
* ring centered at a given pixel's position. The pixels in the ring are
* weighted also by the background IRF value.
***************************************************************************/
void ctskymap::map_background_ring(GCTAObservation* obs)
{
// Get IRF response (to scale background counts)
const GCTAResponseIrf* rsp = dynamic_cast(obs->response());
// Throw an exception if observation has no instrument response function
if (rsp == NULL) {
std::string msg = "No response information available for "+
get_obs_header(obs)+" to compute IRF background. "
"Please specify response information or use "
"another background subtraction method.";
throw GException::invalid_value(G_MAP_BACKGROUND_RING, msg);
}
// Get IRF background template
const GCTABackground* bkg = rsp->background();
// Throw an exception if observation has no IRF background template
if (bkg == NULL) {
std::string msg = "No IRF background template found in instrument "
"response function for "+
get_obs_header(obs)+". Please specify an instrument "
"response function containing a background template.";
throw GException::invalid_value(G_MAP_BACKGROUND_RING, msg);
}
// Compute natural logarithm of energy range in MeV
double lnEmin = std::log(m_emin * 1.0e6);
double lnEmax = std::log(m_emax * 1.0e6);
// Extract region of interest from observation
GCTARoi roi = obs->roi();
// Initialise statistics
int calls = 0;
double exposure = obs->livetime();
// Loop over all map pixels
for (int i = 0; i < m_bkgmap.npix(); ++i) {
// Get sky direction of pixel
GSkyDir& skydir = m_dirs[i];
// Convert sky direction in instrument direction
GCTAInstDir instdir = obs->pointing().instdir(skydir);
// If RoI is valid and instrument direction is not within RoI then
// skip pixel
if (roi.is_valid() && !roi.contains(instdir)) {
continue;
}
// Setup integration function
ctskymap::irf_kern integrand(bkg, &instdir);
GIntegral integral(&integrand);
// Set precision
integral.eps(1.0e-6);
// Do Romberg integration to get the sensitivity (note: this assumes
// the background IRF is a good approximation for the radial
// sensitivity)
m_alphamap(i,0) += integral.romberg(lnEmin, lnEmax) *
m_solidangle[i] * exposure;
// Store the counts in this bin
m_onmap(i,0) += m_skymap(i,0);
// Update number of background function calls
calls += integral.calls();
} // endfor: Loop for caching bkg IRF sensitivity
// Log background subtraction results
log_value(NORMAL, "Alpha map evaluations", calls);
// Zero out the counts map to prepare it for the next observation
m_skymap = 0.0;
// Return
return;
}
/***********************************************************************//**
* @brief Write keywords in FITS HDU
*
* @param[in,out] hdu Pointer to FITS HDU.
*
* Writes keywords in FITS HDU.
***************************************************************************/
void ctskymap::write_hdu_keywords(GFitsHDU* hdu) const
{
// Continue only if pointer is valid
if (hdu != NULL) {
// Set keywords
hdu->card("BKGSUB", m_bkgsubtract, "Background substraction method");
hdu->card("E_MIN", m_emin, "[TeV] Lower energy boundary");
hdu->card("E_MAX", m_emax, "[TeV] Upper energy boundary");
hdu->card("EUNIT", "TeV", "Units for E_MIN and E_MAX");
} // endif: pointer was valid
// Return
return;
}
/***********************************************************************//**
* @brief Computes the bin-by-bin significance if background subtraction
* requested
*
* Method computes the bin-by bin significance if a background subtraction
* method is specified. If method is "IRF", Poisson statistics (in the
* Gaussian limit) are assumed. If method is "RING" a Li & Ma significance
* is computed for each bin (as well as a map of the computed alpha values).
***************************************************************************/
void ctskymap::map_significance(void)
{
// Compute significance from "RING" method (Li & Ma eq. 17)
if (m_bkgsubtract == "RING") {
// Log message about what is being done
log_header1(NORMAL, "Computing Ring background map");
log_value(NORMAL, "Total pixels to process", m_onmap.npix());
// Store the number of bins with an inappropriate alpha parameter
int num_bad_alpha = 0;
// Loop through each bin in the on-counts map
for (int i = 0; i < m_onmap.npix(); ++i) {
// Initialise the on/off counts and alpha for this bin
double n_on = 0.0;
double n_off = 0.0;
double alpha = 0.0;
// Since this can take a long time, keep the user updated on
// the progress when another 10% of pixels is processed
if (i % (m_onmap.npix()/10) == 0) {
log_value(NORMAL, "Pixels remaining", m_onmap.npix()-i);
}
// Get bin coordinates
GSkyDir& skydir = m_dirs[i];
// Compute the alpha and counts for this bin
compute_ring_values(m_onmap, m_alphamap, skydir,
n_on, n_off, alpha);
// If alpha is zero then increment the bad alpha counter
if (alpha == 0.0) {
num_bad_alpha++;
}
// ... otherwise store the results
else {
// Store the on-counts and alpha-weighted off-counts
m_skymap(i,0) = n_on;
m_bkgmap(i,0) = alpha * n_off;
// Compute and store significance (Li & Ma eq. 17)
if (n_on == 0.0) {
m_sigmap(i,0) = -2.0 * n_off * std::log(1.0+alpha);
}
else {
m_sigmap(i,0) = (n_on < (alpha*n_off) ? -2.0 : 2.0) *
(n_on * std::log((1.0+alpha) *
n_on / (alpha * (n_on+n_off))) +
n_off * std::log((1.0+alpha) *
n_off / (n_on+n_off)));
}
} // endelse: alpha was non-zero
} // endfor: looped over all pixels
// Log the number of bad-alpha bins
log_value(NORMAL, "Bins with alpha=0", num_bad_alpha);
// Now take the square root. Since some bins can be negative, first
// take the absolute value of the map, square root that, then multiply
// each bin by its sign to preserve the +/- significance.
m_sigmap = sign(m_sigmap) * sqrt(abs(m_sigmap));
} // endif: ring background selected
// Compute significance using Poisson statistics in the Gaussian limit
else {
m_sigmap = (m_skymap - m_bkgmap) / sqrt(m_skymap);
}
// Return
return;
}
/***********************************************************************//**
* @brief Computes Non, Noff and alpha for a counts map and sensitivity map
*
* @param[in] counts Counts map
* @param[in] sensitivity Sensitivity map
* @param[in] position Position of the ring & roi centers
* @param[out] non Returned estimate of ON counts
* @param[out] noff Returned estimate of OFF counts
* @param[out] alpha Returned estimate of alpha
*
* This method computes the Non counts Noff values at a given position. It
* also computes the alpha parameter from the passed sensitivity map.
***************************************************************************/
void ctskymap::compute_ring_values(const GSkyMap& counts,
const GSkyMap& sensitivity,
const GSkyDir& position,
double& non,
double& noff,
double& alpha)
{
// Reset Non and Noff
non = 0.0;
noff = 0.0;
// Initialise On and Off alpha
double alpha_on = 0.0;
double alpha_off = 0.0;
// Define the regions necessary to do the pixel checks
GSkyRegionCircle roi_reg(position, m_roiradius);
GSkyRegionCircle inner_reg(position, m_inradius);
GSkyRegionCircle outer_reg(position, m_outradius);
// Loop over every pixel in the observation to compute Non, Noff
for (int j = 0; j < counts.npix(); ++j) {
// Get the index and sky direction of this pixel
GSkyDir& skydir = m_dirs[j];
// Check if pixel is inside the background region
if ((m_exclmap(j,0) == 0.0) && outer_reg.contains(skydir) &&
!inner_reg.contains(skydir)) {
// Update n_off
noff += counts(j,0);
// Update alpha_off
alpha_off += sensitivity(j,0);
}
// ... otherwise check if pixel is inside source region
else if (roi_reg.contains(skydir)) {
// Update n_on for significance computation
non += counts(j,0);
// Update alpha_on
alpha_on += sensitivity(j,0);
} // endif: source and background region check
} // endfor: looped over pixels
// Compute alpha. If the off region does not have any sensitivity then
// set Non = Noff = 0
if (alpha_off == 0.0) {
alpha = 0.0;
non = 0.0;
noff = 0.0;
}
else {
alpha = alpha_on / alpha_off;
}
// Return
return;
}
/***********************************************************************//**
* @brief Generates map of pixel exclusions
*
* @param[in] filename Exclusion file name.
*
* Generates a sky map of the pixels that are to be excluded from the
* background estimation. Pixels with values different from 0 will be
* excluded.
***************************************************************************/
void ctskymap::map_exclusions(const GFilename& filename)
{
// Create exlusion map
m_exclmap = create_map(m_obs);
// Set all pixels to 0 (no pixel excluded)
m_exclmap = 0.0;
// Make sure the exclusions filename is valid
if (is_valid_filename(filename)) {
// Fill the exclusions based on the regions supplied
if (filename.is_fits()) {
map_exclusions_fits(filename);
}
else {
map_exclusions_reg(filename);
}
} // endif: filename was valid
// Return
return;
}
/***********************************************************************//**
* @brief Fills exclusions map from FITS image
*
* @param[in] filename FITS image file name.
*
* Sets all exclusion map pixels to 1 that correspond to non-zero pixels in
* the exclusion sky map FITS file.
***************************************************************************/
void ctskymap::map_exclusions_fits(const GFilename& filename)
{
// Load the fits image
GSkyMap inmap(filename);
// Loop through the individual pixels in the exclusion map
for (int i = 0; i < m_exclmap.npix(); ++i) {
// Get the pixel direction
GSkyDir dir = m_exclmap.pix2dir(i);
// Check this sky position in the fits map
if (inmap.contains(dir) && (inmap(inmap.dir2inx(dir)) != 0.0)) {
// Set the pixel to 1
m_exclmap(i) = 1.0;
} // endif: pixel,region overlap check
} // endfor: looped over exclusion map pixels
// Return
return;
}
/***********************************************************************//**
* @brief Fills exclusions map from DS9 region file
*
* @param[in] filename DS9 region file name.
*
* Sets all exclusion map pixels to 1 that are contained in any of the DS9
* regions.
***************************************************************************/
void ctskymap::map_exclusions_reg(const GFilename& filename)
{
// Load the exclusion regions
GSkyRegions regions(filename);
// Loop through the individual pixels in the exclusion map
for (int i = 0; i < m_exclmap.npix(); ++i) {
// Get the pixel position
GSkyDir dir = m_exclmap.pix2dir(i);
// If pixel position overlaps with the regions
if (regions.contains(dir)) {
m_exclmap(i) = 1.0;
}
} // endfor: looped over exclusion map pixels
// Return
return;
}
/***********************************************************************//**
* @brief Estimates the background in sky map based on IRF template
*
* @param[in] lnE Natural logarithm of energy in MeV.
***************************************************************************/
double ctskymap::irf_kern::eval(const double& lnE)
{
// Get log10 of energy in TeV
double logE = lnE * gammalib::inv_ln10 - 6;
// Get function value
double value = (*m_bgd)(logE, m_dir->detx(), m_dir->dety());
// Correct for variable substitution
value *= std::exp(lnE);
// Return value
return value;
}