/***************************************************************************
* ctbutterfly - butterfly calculation tool *
* ----------------------------------------------------------------------- *
* copyright (C) 2014 by Michael Mayer *
* ----------------------------------------------------------------------- *
* *
* 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 ctbutterfly.cpp
* @brief Butterfly computation tool
* @author Michael Mayer
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include "ctbutterfly.hpp"
#include "GTools.hpp"
/* __ Method name definitions ____________________________________________ */
#define G_GET_PARAMETERS "ctbutterfly::get_parameters()"
#define G_SAVE "ctbutterfly::save()"
/* __ Debug definitions __________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
ctbutterfly::ctbutterfly(void) : ctool(CTBUTTERFLY_NAME, CTBUTTERFLY_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.
***************************************************************************/
ctbutterfly::ctbutterfly(const GObservations& obs) :
ctool(CTBUTTERFLY_NAME, CTBUTTERFLY_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.
***************************************************************************/
ctbutterfly::ctbutterfly(int argc, char *argv[]) :
ctool(CTBUTTERFLY_NAME, CTBUTTERFLY_VERSION, argc, argv)
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] app Application.
***************************************************************************/
ctbutterfly::ctbutterfly(const ctbutterfly& app) : ctool(app)
{
// Initialise members
init_members();
// Copy members
copy_members(app);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
ctbutterfly::~ctbutterfly(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] app Application.
* @return Application.
***************************************************************************/
ctbutterfly& ctbutterfly::operator=(const ctbutterfly& 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 ctbutterfly::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 Computes the butterfly
*
* This method takes the spectral fit and its covariance matrix to compute
* the confidence band via Gaussian error propagation
***************************************************************************/
void ctbutterfly::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 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;
}
// Get model instance for further computations
GModels models = m_obs.models();
// Compute covariance matrix if necessary
if (m_covariance.size() == 0) {
// Write header
if (logTerse()) {
log << std::endl;
log.header1("Compute covariance matrix");
}
// Evaluate curvature matrix at the actual parameters
GOptimizerPars pars = models.pars();
GObservations::likelihood likelihood = m_obs.function();
likelihood.eval(pars);
// Get covariance matrix by inverting the curvature matrix
m_covariance = likelihood.curvature()->invert();
}
// Write header
if (logTerse()) {
log << std::endl;
log.header1("Generate butterfly");
}
// Initialise dummy time to evaluate spectral model
GTime time = GTime();
// Initialise vector of gradients
GVector grad = GVector(m_obs.models().npars());
// Loop over energy bins positions
for (int i = 0; i < m_ebounds.size(); ++i) {
// Initialise number of gradients
int num_gradient = 0;
// Get the energy of current bin
GEnergy energy = m_ebounds.elogmean(i);
// Header for verbose logging
if (logExplicit()) {
std::string msg = "Computing butterfly for bin number "+
gammalib::str(i)+" at "+energy.print();
log << std::endl;
log.header2(msg);
}
// Initialise model flux value
double model_flux = 0.0;
// Loop over models
for (int j = 0; j < models.size(); ++j) {
// Check wether model is a skymodel
GModelSky* skymodel = dynamic_cast(models[j]);
// Yes ...
if (skymodel != NULL) {
// Skip spatial models
num_gradient += skymodel->spatial()->size();
// Get pointer to spectral model
GModelSpectral* spectral = skymodel->spectral();
// Set flux value of source of interest
if (skymodel->name() == m_srcname) {
model_flux = spectral->eval_gradients(energy, time);
} // endif: model was source of interest
// Loop over model parameters, get gradients
// and assign them to the vector
for (int k = 0; k < spectral->size(); ++k) {
grad[num_gradient] = (*spectral)[k].gradient();
num_gradient++;
} // endfor: looped over spectral parameters
// Skip temporal models
num_gradient += skymodel->temporal()->size();
} // endif: model was sky model
else {
// Skip other models (e.g. GModelData instances)
num_gradient += models[j]->size();
} // endelse: Model was not GModelSky
} // endfor: Looped over models
// Multiply covariance to the gradient vector
GVector vector = m_covariance * grad;
// Get the error from the scalar product
double error = std::sqrt(grad * vector);
// Store flux, value and energy for saving
m_fluxes.push_back(model_flux);
m_energies.push_back(energy.MeV());
m_errors.push_back(error);
// Log information
if (logExplicit()) {
log << " Flux at ";
log << energy.print();
log << ": ";
log << model_flux;
log << " +- ";
log << error;
log << " ph/cm2/s/MeV";
}
} //endfor: loop over energy bin
// Return
return;
}
/***********************************************************************//**
* @brief Save butterfly diagram
*
* Saves the butterfly diagram into an ascii file using a column separated
* value (CSV) format with blanks as separators.
***************************************************************************/
void ctbutterfly::save(void)
{
// Write header
if (logTerse()) {
log << std::endl;
log.header1("Save Butterfly to file");
}
// Get output filename
m_outfile = (*this)["outfile"].filename();
// Create CSV table with 3 columns
GCsv table(m_fluxes.size(), 3);
// Fill CSV table
for (int i = 0; i < m_fluxes.size(); ++i) {
table.real(i, 0, m_energies[i]);
table.real(i, 1, m_fluxes[i]);
table.real(i, 2, m_errors[i]);
}
// Save CSV table
table.save(m_outfile, " ", clobber());
// Return
return;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void ctbutterfly::init_members(void)
{
// Initialise members
m_infile.clear();
m_srcname.clear();
m_outfile.clear();
m_ebounds.clear();
// Initialise protected members
m_obs.clear();
m_covariance.clear();
m_energies.clear();
m_fluxes.clear();
m_errors.clear();
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] app Application.
***************************************************************************/
void ctbutterfly::copy_members(const ctbutterfly& app)
{
// Copy attributes
m_infile = app.m_infile;
m_srcname = app.m_srcname;
m_outfile = app.m_outfile;
m_ebounds = app.m_ebounds;
// Copy protected members
m_obs = app.m_obs;
m_covariance = app.m_covariance;
m_energies = app.m_energies;
m_fluxes = app.m_fluxes;
m_errors = app.m_errors;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void ctbutterfly::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Get application parameters
*
* @exception GException::invalid_value
* Test source not found or no RA/DEC parameters found for test
* source.
*
* 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 ctbutterfly::get_parameters(void)
{
// If there are no observations in container then add a single CTA
// observation using the parameters from the parameter file
if (m_obs.size() == 0) {
// Allocate CTA observation
GCTAObservation obs;
// Get event file name
std::string filename = (*this)["infile"].filename();
// Try first to open as FITS file
try {
// Load data
obs.load(filename);
// Set response
set_obs_response(&obs);
// Append 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);
// Check if all observations have response information. If
// not, get the calibration database parameters and set
// the response properly
set_response(m_obs);
} // endcatch: file was an XML file
} // endif: there was no observation in the container
// If there is are no models associated with the observations then
// load now the model definition
if (m_obs.models().size() == 0) {
// Get models XML filename
std::string filename = (*this)["srcmdl"].filename();
// Setup models for optimizing.
m_obs.models(GModels(filename));
} // endif: no models were associated with observations
// Get name of test source and check container for this name
m_srcname = (*this)["srcname"].string();
if (!m_obs.models().contains(m_srcname)) {
std::string msg = "Source \""+m_srcname+"\" not found in model "
"container. Please add a source with that name "
"or check for possible typos.";
throw GException::invalid_value(G_GET_PARAMETERS, msg);
}
// Get energy binning information
m_ebounds = get_ebounds();
// Get matrix file name and load if possible
std::string matrixfilename = (*this)["matrix"].filename();
if (matrixfilename != "NONE") {
std::string msg = "Loading of matrix from file not implemented yet. "
"Use filename = \"NONE\" to induce a recomputation of "
"the matrix internally.";
throw GException::feature_not_implemented(G_GET_PARAMETERS, msg);
// m_covariance.load(matrixfilename);
}
// Optionally read ahead parameters so that they get correctly
// dumped into the log file
if (read_ahead()) {
m_outfile = (*this)["outfile"].filename();
}
// Return
return;
}