/***************************************************************************
* ctbkgcube - Background cube generation tool *
* ----------------------------------------------------------------------- *
* copyright (C) 2014 by Chia-Chun Lu *
* ----------------------------------------------------------------------- *
* *
* 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 ctbkgcube.cpp
* @brief Background cube generation tool definition
* @author Chia-Chun Lu
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "ctbkgcube.hpp"
#include "GTools.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_GET_PARAMETERS "ctbkgcube::get_parameters()"
#define G_GET_OBS "ctbkgcube::get_obs()"
#define G_SET_FROM_CNTMAP "ctbkgcube::set_from_cntmap(std::string&)"
#define G_FILL_CUBE "ctbkgcube::fill_cube(GCTAObservation*)"
/* __ Debug definitions __________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Constants __________________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
ctbkgcube::ctbkgcube(void) : ctool(CTBKGCUBE_NAME, CTBKGCUBE_VERSION)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Observations constructor
*
* param[in] obs Observation container.
*
* This method creates an instance of the class by copying an existing
* observations container.
***************************************************************************/
ctbkgcube::ctbkgcube(const GObservations& obs) :
ctool(CTBKGCUBE_NAME, CTBKGCUBE_VERSION)
{
// Initialise members
init_members();
// Set observations
m_obs = obs;
// Return
return;
}
/***********************************************************************//**
* @brief Command line constructor
*
* @param[in] argc Number of arguments in command line.
* @param[in] argv Array of command line arguments.
***************************************************************************/
ctbkgcube::ctbkgcube(int argc, char *argv[]) :
ctool(CTBKGCUBE_NAME, CTBKGCUBE_VERSION, argc, argv)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] app Application.
***************************************************************************/
ctbkgcube::ctbkgcube(const ctbkgcube& app) : ctool(app)
{
// Initialise members
init_members();
// Copy members
copy_members(app);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
ctbkgcube::~ctbkgcube(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] app Application.
* @return Returns application.
***************************************************************************/
ctbkgcube& ctbkgcube::operator=(const ctbkgcube& app)
{
// Execute only if object is not identical
if (this != &app) {
// Copy base class members
this->ctool::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 instance
***************************************************************************/
void ctbkgcube::clear(void)
{
// Free members
free_members();
this->ctool::free_members();
this->GApplication::free_members();
// Initialise members
this->GApplication::init_members();
this->ctool::init_members();
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Generate the background cube(s).
*
* This method reads the task parameters from the parfile, sets up the
* observation container, loops over all CTA observations in the container
* and generates a background cube from the CTA observations.
***************************************************************************/
void ctbkgcube::run(void)
{
// If we're in debug mode then all output is also dumped on the screen
if (logDebug()) {
log.cout(true);
}
// Initialise run parameters
m_livetime = 0.0;
// Get task parameters
get_parameters();
// Write parameters into logger
if (logTerse()) {
log_parameters();
log << std::endl;
}
// Write observation(s) into logger
if (logTerse()) {
log << std::endl;
if (m_obs.size() > 1) {
log.header1("Observations");
}
else {
log.header1("Observation");
}
log << m_obs << std::endl;
}
// Log input model
if (logTerse()) {
log << std::endl;
log.header1("Input model");
log << m_obs.models() << std::endl;
}
// Write header
if (logTerse()) {
log << std::endl;
log.header1("Prepare model");
}
// Copy models from observation container and reset output model
// container
m_bkgmdl = m_obs.models();
m_outmdl.clear();
m_cube_model = -1;
// Remove all models that are not CTA background models from the
// container and put all removed components in the output
// container
int num = m_bkgmdl.size();
for (int i = num-1; i >= 0; --i) {
// Flag removal
bool remove = true;
// Do we have a CTA, HESS, MAGIC or VERITAS specific model?
if (m_bkgmdl[i]->is_valid("CTA", "") ||
m_bkgmdl[i]->is_valid("HESS", "") ||
m_bkgmdl[i]->is_valid("MAGIC", "") ||
m_bkgmdl[i]->is_valid("VERITAS", "")) {
// Do we have a background model?
if (dynamic_cast(m_bkgmdl[i]) != NULL) {
// ... then keep model
remove = false;
} // endif: had a background model
} // endif: had a CTA, HESS, MAGIC or VERITAS model
// Log results
if (logTerse()) {
if (remove) {
log << gammalib::parformat("Remove model");
}
else {
log << gammalib::parformat("Keep model");
}
log << m_bkgmdl[i]->name();
log << " ";
log << m_bkgmdl[i]->type();
log << "(";
log << m_bkgmdl[i]->instruments();
log << ")";
log << std::endl;
}
// If removal is requested, append model to output container and
// remove it from the background model container ...
if (remove) {
m_outmdl.append(*(m_bkgmdl[i]));
m_bkgmdl.remove(i);
}
} // endfor: looped over all background models
// Write header
if (logTerse()) {
log << std::endl;
log.header1("Generate background cube");
}
// Initialise observation counter
int n_observations = 0;
// Loop over all observations in the container
for (int i = 0; i < m_obs.size(); ++i) {
// Get CTA observation
GCTAObservation* obs = dynamic_cast(m_obs[i]);
// Continue only if observation is a CTA observation
if (obs != NULL) {
// Write header for observation
if (logTerse()) {
if (obs->name().length() > 1) {
log.header3("Observation "+obs->name());
}
else {
log.header3("Observation "+gammalib::str(i));
}
}
// Increment number of observations
n_observations++;
// Fill the cube
fill_cube(obs);
} // endif: CTA observation found
} // endfor: looped over observations
// Re-normalize cube to get units of counts/MeV/s/sr
if (m_livetime > 0.0) {
// Loop over all bins in background cube and divide the content
// by the total livetime.
for (int i = 0; i < m_bkgcube.size(); ++i) {
GCTAEventBin* bin = m_bkgcube[i];
double rate = bin->counts() / m_livetime;
bin->counts(rate);
}
} // endif: livetime was positive
// Log results
if (logTerse()) {
log << gammalib::parformat("Number of observations");
log << n_observations << std::endl;
}
// Create a background model for output background cube and append
// that model to the input model in place of the original
// background model
GEnergies energies;
for (int i = 0; i < m_bkgcube.ebins(); ++i) {
energies.append(m_bkgcube.energy(i));
}
GModelSpatialDiffuseCube spatial(m_bkgcube.map(), energies, 1.0);
GModelSpectralPlaw spectral(1.0, 0.0, GEnergy(1.0, "TeV"));
GCTAModelCubeBackground model(spatial, spectral);
model.name("ctbkgcube default background model");
m_cube_model = m_outmdl.size(); // Store the slot number for save()
m_outmdl.append(model);
// Log output model
if (logTerse()) {
log << std::endl;
log.header1("Output model");
log << m_outmdl << std::endl;
}
// Return
return;
}
/***********************************************************************//**
* @brief Save background cube
*
* Save the background cube into the file specified by the outfile parameter.
***************************************************************************/
void ctbkgcube::save(void)
{
// Write header
if (logTerse()) {
log << std::endl;
log.header1("Save background cube");
}
// Create energies container from energy boundaries
GEnergies energies;
for (int i = 0; i < m_ebounds.size(); ++i) {
energies.append(m_ebounds.elogmean(i));
}
// Get output filenames
std::string outfile = (*this)["outfile"].filename();
std::string outmodel = (*this)["outmodel"].filename();
// Create empty FITS file
GFits fits;
// Write background cube
m_bkgcube.map().write(fits);
// Write energies
energies.write(fits);
// Save FITS file
fits.saveto(outfile, clobber());
// Write output models if filename is valid
if ((outmodel.length() > 0) && (gammalib::tolower(outmodel) != "none")) {
// Set filename of map cube.
if (m_cube_model >= 0) {
GCTAModelCubeBackground* model =
dynamic_cast(m_outmdl[m_cube_model]);
if (model != NULL) {
GModelSpatialDiffuseCube* spatial =
dynamic_cast(model->spatial());
if (spatial != NULL) {
spatial->filename(outfile);
}
}
}
// Save output model container
m_outmdl.save(outmodel);
}
// Return
return;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void ctbkgcube::init_members(void)
{
// Initialise members
m_obs.clear();
m_bkgcube.clear();
m_bkgmdl.clear();
m_outmdl.clear();
m_ebounds.clear();
m_livetime = 0.0;
m_cube_model = -1;
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] app Application.
***************************************************************************/
void ctbkgcube::copy_members(const ctbkgcube& app)
{
// Copy members
m_obs = app.m_obs;
m_bkgcube = app.m_bkgcube;
m_bkgmdl = app.m_bkgmdl;
m_outmdl = app.m_outmdl;
m_ebounds = app.m_ebounds;
m_livetime = app.m_livetime;
m_cube_model = app.m_cube_model;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void ctbkgcube::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Get application parameters
*
* @exception GException::invalid_value
* No background model definition XML file specified.
*
* Get all task parameters from parameter file or (if required) by querying
* the user. The parameters are read in the correct order.
***************************************************************************/
void ctbkgcube::get_parameters(void)
{
// If we do not have any observations in the container then load them
// using the "infile" parameter
if (m_obs.size() == 0) {
get_obs();
}
// If there are no models associated with the observations then load now
// the model definition from the XML file
if (m_obs.models().size() == 0) {
if ((*this)["inmodel"].is_undefined()) {
std::string msg = "No model definition XML file specified. "
"Please set the \"inmodel\" parameter to the "
"XML file that contains the background model "
"definition.";
throw GException::invalid_value(G_GET_PARAMETERS, msg);
}
std::string inmodel = (*this)["inmodel"].filename();
GModels models(inmodel);
m_obs.models(models);
}
// If no counts map is specified then setup the background cube from
// the user parameters
std::string cntmap = (*this)["cntmap"].filename();
if ((gammalib::toupper(cntmap) == "NONE") ||
(gammalib::strip_whitespace(cntmap) == "")) {
// Get user parameters for counts map definition
std::string proj = (*this)["proj"].string();
std::string coordsys = (*this)["coordsys"].string();
double xref = (*this)["xref"].real();
double yref = (*this)["yref"].real();
double binsz = (*this)["binsz"].real();
int nxpix = (*this)["nxpix"].integer();
int nypix = (*this)["nypix"].integer();
// Get energy definition
m_ebounds = get_ebounds();
// Define skymap for background cube
GSkymap map(proj, coordsys, xref, yref,
-binsz, binsz, nxpix, nypix,
m_ebounds.size());
// Allocate background cube using a dummy GTI
GGti gti;
gti.append(GTime(0.0), GTime(1.0)); // Dummy GTI
m_bkgcube = GCTAEventCube(map, m_ebounds, gti);
}
// ... otherwise setup the background cube from the counts map
else {
// Set background cube from counts map
set_from_cntmap(cntmap);
}
// Read output filenames (if needed)
if (read_ahead()) {
(*this)["outfile"].filename();
(*this)["outmodel"].filename();
}
// Return
return;
}
/***********************************************************************//**
* @brief Get observation definition
*
* @exception GException::invalid_value
* No event list, counts cube or observation definition file
* specified.
*
* Get observation definition from the user parameters.
***************************************************************************/
void ctbkgcube::get_obs(void)
{
// Get input filename
std::string filename = (*this)["infile"].filename();
// Check whether infile parameter is undefined
if (gammalib::toupper(filename) == "NONE") {
std::string msg = "No event list, counts cube or observation definition "
"file specified. Please set the \"infile\" parameter "
"to either an event list filename, a counts cube "
"filename or an observation definition filename.";
throw GException::invalid_value(G_GET_OBS, msg);
}
// Try first to open as FITS file
try {
// Allocate CTA observation
GCTAObservation obs;
// Load input file in CTA observation
obs.load(filename);
// Append CTA observation to container
m_obs.append(obs);
}
// ... otherwise try to open as XML file
catch (GException::fits_open_error &e) {
// Load observations from XML file
m_obs.load(filename);
}
// Return
return;
}
/***********************************************************************//**
* @brief Set background cube definition from counts map
*
* @exception GException::invalid_value
* Invalid counts map projection or invalid events encountered.
*
* Set background cube definition from counts map.
***************************************************************************/
void ctbkgcube::set_from_cntmap(const std::string& filename)
{
// Allocate CTA observation
GCTAObservation obs;
// Load counts map in CTA observation
obs.load(filename);
// Set background cube from counts map
const GCTAEventCube* cube = dynamic_cast(obs.events());
// Continue only if cube is valid
if (cube != NULL) {
// Get sky map projection
const GWcs* wcs = dynamic_cast(cube->map().projection());
// Continue only if projection is valid
if (wcs != NULL) {
// Get user parameters for counts map definition
std::string proj = wcs->code();
std::string coordsys = wcs->coordsys();
double xref = wcs->crval(0);
double yref = wcs->crval(1);
double dx = wcs->cdelt(0);
double dy = wcs->cdelt(1);
int nx = cube->map().nx();
int ny = cube->map().ny();
// Get energy definition
m_ebounds = cube->ebounds();
// Define skymap for background cube
GSkymap map(proj, coordsys, xref, yref,
dx, dy, nx, ny,
m_ebounds.size());
// Allocate background cube
m_bkgcube = GCTAEventCube(map, m_ebounds, obs.events()->gti());
} // endif: WCS projection was valid
// ... projection is not of WCS type
else {
std::string msg = "Counts map project is not of WCS type.";
throw GException::invalid_value(G_SET_FROM_CNTMAP, msg);
}
} // endif: observation contained an events cube
// ... there is not events cube
else {
std::string msg = "No events cube found in file \""
""+filename+"\".";
throw GException::invalid_value(G_SET_FROM_CNTMAP, msg);
}
// Return
return;
}
/***********************************************************************//**
* @brief Generate background cube
*
* @param[in] obs Pointer to CTA observation.
*
* Fills the background cube with the model value for a CTA observation.
* This method will not need to load the event file as it gets the event
* ROI and GTI directly from the observation.
***************************************************************************/
void ctbkgcube::fill_cube(GCTAObservation* obs)
{
// Continue only if observation pointer is valid
if (obs != NULL) {
// Initialise statistics
double sum = 0.0;
int n_bins_inside = 0;
int n_bins_outside = 0;
// Extract region of interest from CTA observation
GCTARoi roi = obs->roi();
// Set GTI of actual observations as the GTI of the event cube
m_bkgcube.gti(obs->gti());
// Get observation livetime
double livetime = obs->livetime();
// Loop over all bins in background cube
for (int i = 0; i < m_bkgcube.size(); ++i) {
// Get event bin
GCTAEventBin* bin = m_bkgcube[i];
// Continue only if binned in contained in ROI
if (roi.contains(*bin)) {
// Compute model value for event bin. The model value is
// given in counts/MeV/s/sr.
double model = m_bkgmdl.eval(*bin, *obs);
// Compute number events
sum += model * bin->size();
// Multiply by livetime to get the correct weighting for
// each observation. We divide by the total livetime later
// to get the background model in units of counts/MeV/s/sr.
model *= livetime;
// Add existing number of counts
model += bin->counts();
// Store cumulated value (units: counts/MeV/sr)
bin->counts(model);
// Increment contained bin counter
n_bins_inside++;
} // endif: bin was contained in RoI
// ... otherwise count bin as an outsider
else {
n_bins_outside++;
}
} // endfor: looped over all bins
// Accumulate livetime
m_livetime += livetime;
// Log results
if (logTerse()) {
log << gammalib::parformat("Bins within RoI");
log << n_bins_inside << std::endl;
log << gammalib::parformat("Bins outside RoI");
log << n_bins_outside << std::endl;
log << gammalib::parformat("Background events in cube");
log << sum << std::endl;
log << gammalib::parformat("Cube livetime");
log << livetime << " sec" << std::endl;
}
} // endif: observation pointer was not valid
// Return
return;
}