/***************************************************************************
* ctmapcube - Map cube generation tool *
* ----------------------------------------------------------------------- *
* copyright (C) 2016-2018 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 ctmapcube.cpp
* @brief Map cube generation tool implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "ctmapcube.hpp"
#include "GTools.hpp"
/* __ Method name definitions ____________________________________________ */
/* __ Debug definitions __________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Constants __________________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
ctmapcube::ctmapcube(void) : ctool(CTMAPCUBE_NAME, VERSION)
{
// 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.
***************************************************************************/
ctmapcube::ctmapcube(int argc, char *argv[]) :
ctool(CTMAPCUBE_NAME, VERSION, argc, argv)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] app Application.
***************************************************************************/
ctmapcube::ctmapcube(const ctmapcube& app) : ctool(app)
{
// Initialise members
init_members();
// Copy members
copy_members(app);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
ctmapcube::~ctmapcube(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] app Application.
* @return Application.
***************************************************************************/
ctmapcube& ctmapcube::operator=(const ctmapcube& 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 ctmapcube tool
*
* Clears ctmapcube tool.
***************************************************************************/
void ctmapcube::clear(void)
{
// Free members
free_members();
this->ctool::free_members();
// Clear base class (needed to conserve tool name and version)
this->GApplication::clear();
// Initialise members
this->ctool::init_members();
init_members();
// Write header into logger
log_header();
// Return
return;
}
/***********************************************************************//**
* @brief Generate map cube.
*
* This method reads the task parameters from the parfile and generates the
* map cube.
***************************************************************************/
void ctmapcube::run(void)
{
// If we're in debug mode then all output is also dumped on the screen
if (logDebug()) {
log.cout(true);
}
// Get task parameters
get_parameters();
// Write input model container into logger
log_models(NORMAL, m_models, "Input model");
// Generate map cube
create_cube();
// Write result cube into logger
log_header1(NORMAL, "Map cube");
log_string(NORMAL, m_cube.print(m_chatter));
// Optionally publish map cube
if (m_publish) {
publish();
}
// Return
return;
}
/***********************************************************************//**
* @brief Save map cube
*
* Saves the map cube into a FITS file.
***************************************************************************/
void ctmapcube::save(void)
{
// Write header
log_header1(TERSE, "Save map cube");
// Save map cube if filename is valid and the map cube is not empty
if ((*this)["outcube"].is_valid() && !m_cube.cube().is_empty()) {
// Get map cube filename
m_outcube = (*this)["outcube"].filename();
// Save map cube
m_cube.save(m_outcube, clobber());
}
// Write into logger what has been done
std::string fname = (m_outcube.is_empty()) ? "NONE" : m_outcube.url();
if (m_cube.cube().is_empty()) {
fname.append(" (cube is empty, no file created)");
}
log_value(NORMAL, "Map cube file", fname);
// Return
return;
}
/***********************************************************************//**
* @brief Publish map cube
*
* @param[in] name Map cube name.
*
* Publishes the map cube on a VO Hub.
***************************************************************************/
void ctmapcube::publish(const std::string& name)
{
// Write header into logger
log_header1(TERSE, "Publish map cube");
// Set default name is user name is empty
std::string user_name(name);
if (user_name.empty()) {
user_name = CTMAPCUBE_NAME;
}
// Write map cube name into logger
log_value(NORMAL, "Map cube name", user_name);
// Publish map cube
m_cube.cube().publish(user_name);
// Return
return;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void ctmapcube::init_members(void)
{
// Initialise members
m_outcube.clear();
m_ptsrcsig = 1.0;
m_publish = false;
m_chatter = static_cast(2);
// Initialise protected members
m_models.clear();
m_cube.clear();
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] app Application.
***************************************************************************/
void ctmapcube::copy_members(const ctmapcube& app)
{
// Copy attributes
m_outcube = app.m_outcube;
m_ptsrcsig = app.m_ptsrcsig;
m_publish = app.m_publish;
m_chatter = app.m_chatter;
// Copy protected members
m_models = app.m_models;
m_cube = app.m_cube;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void ctmapcube::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Get application parameters
*
* Get all task parameters from parameter file or (if required) by querying
* the user.
***************************************************************************/
void ctmapcube::get_parameters(void)
{
// Read model definition file if required
if (m_models.size() == 0) {
// Get model filename
GFilename inmodel = (*this)["inmodel"].filename();
// Load models from file
m_models.load(inmodel);
} // endif: there were no models
// Get energy binning parameters
GEnergies energies(create_ebounds());
// Get spatial binning parameters
double xref = (*this)["xref"].real();
double yref = (*this)["yref"].real();
std::string proj = (*this)["proj"].string();
std::string coordsys = (*this)["coordsys"].string();
double binsz = (*this)["binsz"].real();
int nxpix = (*this)["nxpix"].integer();
int nypix = (*this)["nypix"].integer();
// Get remaining hidden parameters
m_ptsrcsig = (*this)["ptsrcsig"].real();
m_publish = (*this)["publish"].boolean();
m_chatter = static_cast((*this)["chatter"].integer());
// If needed later, query output filename now
if (read_ahead()) {
(*this)["outcube"].query();
}
// Allocate map cube
GSkyMap cube(proj, coordsys, xref, yref, -binsz, binsz, nxpix, nypix,
energies.size());
// Allocate map cube
m_cube = GModelSpatialDiffuseCube(cube, energies);
// Write parameters into logger
log_parameters(TERSE);
// Return
return;
}
/***********************************************************************//**
* @brief Generate map cube
*
* Generate map cube by looping over all sky models in the container.
***************************************************************************/
void ctmapcube::create_cube(void)
{
// Write header into logger
log_header1(TERSE, "Generate map cube");
// Loop over all models
for (int i = 0; i < m_models.size(); ++i) {
// Get pointer to sky model
GModelSky* model = dynamic_cast(m_models[i]);
// Fall through if model is not a sky model
if (model == NULL) {
// Signal that model is skipped
log_value(NORMAL, "Skip model", m_models[i]->name());
// Continue
continue;
}
// Signal that model is used
log_value(NORMAL, "Use model", m_models[i]->name());
// Add sky model
if (dynamic_cast(model->spatial()) != NULL) {
add_ptsrc_model(model);
}
else {
add_model(model);
}
} // endfor: looped over energies
// Return
return;
}
/***********************************************************************//**
* @brief Add one model to map cube
*
* @param[in] model Pointer to point source sky model.
*
* Adds one model to map cube.
***************************************************************************/
void ctmapcube::add_model(GModelSky* model)
{
// Get cube energies
GEnergies energies = m_cube.energies();
// Get pointer to sky map
GSkyMap* map = const_cast(&m_cube.cube());
// Get the model's bounding circle
GSkyDir centre;
double radius;
get_bounding_circle(model, ¢re, &radius);
// Add some margin to circle radius
radius += 0.1;
// Loop over all map cube pixels
for (int ipixel = 0; ipixel < map->npix(); ++ipixel) {
// Get pixel sky direction
GSkyDir dir = map->inx2dir(ipixel);
// Skip pixel if it's outside the bounding circle
if (centre.dist_deg(dir) > radius) {
continue;
}
// Loop over all energy bins
for (int iebin = 0; iebin < energies.size(); ++iebin) {
// Set photon
GPhoton photon(dir, energies[iebin], GTime());
// Add model value
map->operator()(ipixel, iebin) += model->value(photon);
} // endfor: looped over all energies
} // endfor: looped over all pixels
// Return
return;
}
/***********************************************************************//**
* @brief Add one point source model to map cube
*
* @param[in] model Pointer to point source sky model.
*
* Adds one point source model to map cube.
***************************************************************************/
void ctmapcube::add_ptsrc_model(GModelSky* model)
{
// Get pointer to sky map
GSkyMap* map = const_cast(&m_cube.cube());
// If Gaussian sigma is positive then replace the point sources by a
// Gaussian with the user defined width
if (m_ptsrcsig > 0.0) {
// Get the point source centre (radius is ignored)
GSkyDir centre;
double radius;
get_bounding_circle(model, ¢re, &radius);
// Set the Gaussian sigma in degrees (m_ptsrcsig is in arcmin)
double sigma = m_ptsrcsig/60.0;
// Create a Gaussian spatial model
GModelSpatialRadialGauss spatial(centre, sigma);
// Create a new model reusing the spectral and temporal components
// of the original model
GModelSky skymodel(spatial, *(model->spectral()), *(model->temporal()));
// Add model
add_model(&skymodel);
} // endif: Gaussian sigma was positive
// ... otherwise set a single map pixel
else {
// Get point source spatial component
GModelSpatialPointSource* ptsrc =
static_cast(model->spatial());
// Get point source direction
GSkyDir dir = ptsrc->dir();
// Get cube energies
GEnergies energies = m_cube.energies();
// Get test sky map for flux computation
GSkyMap test_map(*map);
// Continue only if map contains point source sky direction
if (map->contains(dir)) {
// Get map pixel
int ipixel = map->dir2inx(dir);
// Loop over all energy bins
for (int iebin = 0; iebin < energies.size(); ++iebin) {
// Set photon using the point source sky direction
GPhoton photon(dir, energies[iebin], GTime());
// Compute model value
double value = model->value(photon);
// Compute flux normalization
test_map(ipixel, iebin) = value;
double flux = test_map.flux(ipixel, iebin);
double norm = (flux > 0.0) ? value / flux : 0.0;
// Add model to sky map
map->operator()(ipixel, iebin) += norm * value;
} // endfor: looped over all energies
} // endif: map contained sky direction
} // endelse: Gaussian sigma was non positive
// Return
return;
}
/***********************************************************************//**
* @brief Get bounding circle for a sky model
*
* @param[in] model Pointer to sky model.
* @param[out] dir Centre of bounding circle.
* @param[out] radius Radius of bounding circle (degrees).
*
* Determine for any spatial model the bounding circle. The bounding circle
* is a circular region that fully enclosed the model.
*
* The method does not verify the validity of the pointer arguments.
***************************************************************************/
void ctmapcube::get_bounding_circle(GModelSky* model,
GSkyDir* dir,
double* radius) const
{
// Interpret the spatial model component as point source, radial source
// or elliptical source
GModelSpatialPointSource* ptsrc =
dynamic_cast(model->spatial());
GModelSpatialRadial* radial =
dynamic_cast(model->spatial());
GModelSpatialElliptical* elliptical =
dynamic_cast(model->spatial());
// If the spatial component as a point source then extract the source
// position and set the radius of the bounding box to 0.1 degrees
if (ptsrc != NULL) {
*dir = ptsrc->dir();
*radius = 0.1;
}
// ... otherwise, if the spatial component is a radial source then extract
// the source position and set the radius of the bounding box from the
// value returned by theta_max() in radians
else if (radial != NULL) {
*dir = radial->dir();
*radius = radial->theta_max() * gammalib::rad2deg;
}
// ... otherwise, if the spatial component is an elliptical source then
// extract the source position and set the radius of the bounding box from
// the value returned by theta_max() in radians
else if (elliptical != NULL) {
*dir = elliptical->dir();
*radius = elliptical->theta_max() * gammalib::rad2deg;
}
// ... otherwise use the entire sky
else {
*radius = 180.0;
}
// Return
return;
}