/***************************************************************************
* GCOMModelDRBFitting.cpp - COMPTEL DRB model fitting class *
* ----------------------------------------------------------------------- *
* copyright (C) 2012-2016 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 GCOMModelDRBFitting.cpp
* @brief COMPTEL DRB model fitting class implementation
* @author Juergen Knoedlseder
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "GException.hpp"
#include "GTools.hpp"
#include "GModelRegistry.hpp"
#include "GCOMModelDRBFitting.hpp"
#include "GCOMObservation.hpp"
#include "GCOMEventBin.hpp"
/* __ Constants __________________________________________________________ */
/* __ Globals ____________________________________________________________ */
const GCOMModelDRBFitting g_com_drb_fitting_seed;
const GModelRegistry g_com_drb_fitting_registry(&g_com_drb_fitting_seed);
/* __ Method name definitions ____________________________________________ */
#define G_EVAL "GCOMModelDRBFitting::eval(GEvent&, GObservation&, bool&)"
#define G_READ "GCOMModelDRBFitting::read(GXmlElement&)"
#define G_WRITE "GCOMModelDRBFitting::write(GXmlElement&)"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
//#define G_DUMP_MC //!< Dump MC information
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
*
* Constructs an empty COMPTEL DRB fitting model.
***************************************************************************/
GCOMModelDRBFitting::GCOMModelDRBFitting(void) : GModelData()
{
// Initialise members
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Constructor
*
* @param[in] xml XML element.
*
* Constructs a COMPTEL DRB fitting model from the information that is found
* in an XML element. Please refer to the method GCOMModelDRBFitting::read
* to learn more about the information that is expected in the XML element.
***************************************************************************/
GCOMModelDRBFitting::GCOMModelDRBFitting(const GXmlElement& xml) :
GModelData(xml)
{
// Initialise members
init_members();
// Read XML
read(xml);
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] model COMPTEL DRB fitting model.
*
* Constructs a COMPTEL DRB fitting model by copying information from an
* existing model. Note that the copy is a deep copy, so the original object
* can be destroyed after the copy without any loss of information.
***************************************************************************/
GCOMModelDRBFitting::GCOMModelDRBFitting(const GCOMModelDRBFitting& model) :
GModelData(model)
{
// Initialise private members for clean destruction
init_members();
// Copy members
copy_members(model);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
*
* Destroys a COMPTEL DRB fitting model.
***************************************************************************/
GCOMModelDRBFitting::~GCOMModelDRBFitting(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] model COMPTEL DRB fitting model.
* @return COMPTEL DRB fitting model
*
* Assigns the information from a COMPTEL DRB fitting model to the actual
* object. Note that a deep copy of the information is performed, so the
* original object can be destroyed after the assignment without any loss of
* information.
***************************************************************************/
GCOMModelDRBFitting& GCOMModelDRBFitting::operator=(const GCOMModelDRBFitting& model)
{
// Execute only if object is not identical
if (this != &model) {
// Copy base class members
this->GModelData::operator=(model);
// Free members
free_members();
// Initialise members
init_members();
// Copy members
copy_members(model);
} // endif: object was not identical
// Return
return *this;
}
/*==========================================================================
= =
= Public methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Clear instance
*
* Resets the object to a clean initial state. All information that resided
* in the object will be lost.
***************************************************************************/
void GCOMModelDRBFitting::clear(void)
{
// Free class members (base and derived classes, derived class first)
free_members();
this->GModelData::free_members();
this->GModel::free_members();
// Initialise members
this->GModel::init_members();
this->GModelData::init_members();
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone instance
*
* @return Pointer to deep copy of COMPTEL DRB fitting model.
*
* Clone a COMPTEL DRB fitting model. Cloning performs a deep copy of the
* information, so the original object can be destroyed after cloning without
* any loss of information.
***************************************************************************/
GCOMModelDRBFitting* GCOMModelDRBFitting::clone(void) const
{
return new GCOMModelDRBFitting(*this);
}
/***********************************************************************//**
* @brief Evaluate function
*
* @param[in] event Observed event.
* @param[in] obs Observation.
* @param[in] gradients Compute gradients?
* @return Background model value.
*
* @exception GException::invalid_argument
* Observation is not a COMPTEL observation.
* Event is not a COMPTEL event bin.
*
* Evaluates the COMPTEL DRB fitting model.
***************************************************************************/
double GCOMModelDRBFitting::eval(const GEvent& event,
const GObservation& obs,
const bool& gradients) const
{
// Extract COMPTEL observation
const GCOMObservation* observation = dynamic_cast(&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_EVAL, msg);
}
// Extract COMPTEL event bin
const GCOMEventBin* bin = dynamic_cast(&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_EVAL, msg);
}
// Initialise value
double value = 0.0;
// Optionally initialise gradients
if (gradients) {
for (int i = 0; i < m_values.size(); ++i) {
m_values[i].factor_gradient(0.0);
}
}
// Get bin index
int index = bin->index();
// Get bin size.
double size = bin->size();
// Continue only if bin size is positive
if (size > 0.0) {
// Initialise scaling factor
double scale = 1.0;
// Get DRB model value
value = observation->drb().pixels()[index] / size;
// If the model has a single scaling factor then use the first
// parameter as scaling factor of the model
if (m_scale) {
// Get scale factor
scale = m_values[0].value();
// Optionally compute gradients
if (gradients) {
// Compute partial derivative
double grad = (m_values[0].is_free())
? value * m_values[0].scale() : 0.0;
// Set gradient
m_values[0].factor_gradient(grad);
} // endif: gradient computation was requested
} // endif: model is a scaling factor
// ... otherwise perform a linear interpolation
else {
// Get Phibar value
double phibar = bin->dir().phibar();
// Update evaluation cache
update_cache();
// Set node array for linear interpolation
m_nodes.set_value(phibar);
// Get indices and weights for interpolation
int inx_left = m_nodes.inx_left();
int inx_right = m_nodes.inx_right();
double wgt_left = m_nodes.wgt_left();
double wgt_right = m_nodes.wgt_right();
// Get scale factor
scale = m_values[inx_left].value() * wgt_left +
m_values[inx_right].value() * wgt_right;
// Optionally compute gradients
if (gradients) {
// Compute partial derivatives
double g_left = (m_values[inx_left].is_free())
? wgt_left * value * m_values[inx_left].scale()
: 0.0;
double g_right = (m_values[inx_right].is_free())
? wgt_right * value * m_values[inx_right].scale()
: 0.0;
// Set gradients
m_values[inx_left].factor_gradient(g_left);
m_values[inx_right].factor_gradient(g_right);
} // endif: gradient computation was requested
} // endelse: performed linear interpolation
// Compute background value
value *= scale;
// Compile option: Check for NaN/Inf
#if defined(G_NAN_CHECK)
if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
std::cout << "*** ERROR: GCOMModelDRBFitting::eval";
std::cout << "(index=" << index << "):";
std::cout << " NaN/Inf encountered";
std::cout << " (value=" << value;
std::cout << ", scale=" << scale;
std::cout << ")" << std::endl;
}
#endif
} // endif: binsize was positive
// Return
return value;
}
/***********************************************************************//**
* @brief Return spatially integrated data model
*
* @param[in] obsEng Measured event energy.
* @param[in] obsTime Measured event time.
* @param[in] obs Observation.
*
* Spatially integrates the data model for a given measured event energy and
* event time.
*
* @todo Implement method.
***************************************************************************/
double GCOMModelDRBFitting::npred(const GEnergy& obsEng,
const GTime& obsTime,
const GObservation& obs) const
{
// Initialise result
double npred = 0.0;
// Return
return npred;
}
/***********************************************************************//**
* @brief Return simulated list of events
*
* @param[in] obs Observation.
* @param[in] ran Random number generator.
*
* Draws a sample of events from the COMPTEL DRB fitting model using a Monte
* Carlo simulation.
*
* @todo Implement method.
***************************************************************************/
GCOMEventCube* GCOMModelDRBFitting::mc(const GObservation& obs,
GRan& ran) const
{
// Initialise new event cube
GCOMEventCube* cube = new GCOMEventCube;
// Return
return cube;
}
/***********************************************************************//**
* @brief Read model from XML element
*
* @param[in] xml XML element.
*
* @exception GException::invalid_value
* Model definition requires at least one node.
*
* Read the COMPTEL DRB fitting model from an XML element.
*
* The model is composed of nodes that define the normalization value as
* function of Phibar value. The following XML file syntax is expected:
*
*
*
*
*
*
* ...
*
*
*
*
*
***************************************************************************/
void GCOMModelDRBFitting::read(const GXmlElement& xml)
{
// Free space for nodes
m_phibars.clear();
m_values.clear();
// Get number of nodes from XML file
int nodes = xml.elements("node");
// Throw an error if there are no nodes
if (nodes < 1) {
std::string msg = "DRB fitting model requires at least one Phibar "
"node. Please correct XML format.";
throw GException::invalid_value(G_READ, msg);
}
// Loop over all nodes
for (int i = 0; i < nodes; ++i) {
// Allocate node parameters
GModelPar phibar;
GModelPar normalization;
// Get node
const GXmlElement* node = xml.element("node", i);
// Read Phibar parameter
const GXmlElement* par = gammalib::xml_get_par(G_READ, *node, "Phibar");
phibar.read(*par);
// Read Normalization parameter
par = gammalib::xml_get_par(G_READ, *node, "Normalization");
normalization.read(*par);
// Set parameter names
std::string phibar_name = "Phibar layer "+gammalib::str(i);
std::string normalization_name = "Scale factor "+gammalib::str(i);
// Set Phibar attributes
phibar.name(phibar_name);
phibar.unit("deg");
phibar.has_grad(false);
// Set normalization attributes
normalization.name(normalization_name);
normalization.unit("");
normalization.has_grad(true);
// Push node parameters on list
m_phibars.push_back(phibar);
m_values.push_back(normalization);
} // endfor: looped over nodes
// Set model name
name(xml.attribute("name"));
// Set instruments
instruments(xml.attribute("instrument"));
// Set observation identifiers
ids(xml.attribute("id"));
// Check flag if TS value should be computed
bool tscalc = (xml.attribute("tscalc") == "1") ? true : false;
// Set flag if TS value should be computed
this->tscalc(tscalc);
// Set pointers
set_pointers();
// Set evluation cache
set_cache();
// Return
return;
}
/***********************************************************************//**
* @brief Write model into XML element
*
* @param[in] xml XML element.
*
* @exception GException::model_invalid_spectral
* Existing XML element is not of required type
* @exception GException::model_invalid_parnum
* Invalid number of model parameters or nodes found in XML element.
* @exception GException::model_invalid_parnames
* Invalid model parameter names found in XML element.
*
* Write the COMPTEL DRB fitting model into an XML element.
*
* The model is composed of nodes that define the normalization value as
* function of Phibar value. The following XML file syntax is expected:
*
*
*
*
*
*
* ...
*
*
*
*
*
***************************************************************************/
void GCOMModelDRBFitting::write(GXmlElement& xml) const
{
// Initialise pointer on source
GXmlElement* src = NULL;
// Search corresponding source
int n = xml.elements("source");
for (int k = 0; k < n; ++k) {
GXmlElement* element = xml.element("source", k);
if (element->attribute("name") == name()) {
src = element;
break;
}
}
// If no source with corresponding name was found then append one.
// Set also the type and the instrument.
if (src == NULL) {
src = xml.append("source");
src->attribute("name", name());
src->attribute("type", type());
if (instruments().length() > 0) {
src->attribute("instrument", instruments());
}
}
// Verify model type
if (src->attribute("type") != type()) {
std::string msg = "Invalid model type \""+src->attribute("type")+"\" "
"found in XML file. Model type \""+type()+"\" "
"expected.";
throw GException::invalid_value(G_WRITE, msg);
}
// Determine number of nodes
int nodes = m_phibars.size();
// If XML element has 0 nodes then append nodes
if (src->elements() == 0) {
for (int i = 0; i < nodes; ++i) {
src->append(GXmlElement("node"));
}
}
// Verify that XML element has the required number of nodes
if (src->elements() != nodes || src->elements("node") != nodes) {
std::string msg = "Invalid number of nodes "+
gammalib::str(src->elements("node"))+
" found in XML file, but model requires exactly "+
gammalib::str(nodes)+" nodes.";
throw GException::invalid_value(G_WRITE, msg);
}
// Loop over all nodes
for (int i = 0; i < nodes; ++i) {
// Get node
GXmlElement* node = src->element("node", i);
// Write Phibar parameter
GXmlElement* par = gammalib::xml_need_par(G_WRITE, *node, "Phibar");
m_phibars[i].write(*par);
// Write Normalization parameter
par = gammalib::xml_need_par(G_WRITE, *node, "Normalization");
m_values[i].write(*par);
} // endfor: looped over nodes
// Return
return;
}
/***********************************************************************//**
* @brief Print model information
*
* @param[in] chatter Chattiness (defaults to NORMAL).
* @return String containing model information.
***************************************************************************/
std::string GCOMModelDRBFitting::print(const GChatter& chatter) const
{
// Initialise result string
std::string result;
// Continue only if chatter is not silent
if (chatter != SILENT) {
// Append header
result.append("=== GCOMModelDRBFitting ===");
// Append attributes
result.append("\n"+print_attributes());
// Append node summary
result.append("\n"+gammalib::parformat("Number of nodes"));
result.append(gammalib::str(m_phibars.size()));
result.append("\n"+gammalib::parformat("Number of parameters"));
result.append(gammalib::str(size()));
for (int i = 0; i < size(); ++i) {
result.append("\n"+m_pars[i]->print(chatter));
}
} // endif: chatter was not silent
// Return result
return result;
}
/*==========================================================================
= =
= Private methods =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Initialise class members
***************************************************************************/
void GCOMModelDRBFitting::init_members(void)
{
// Initialise members
m_phibars.clear();
m_values.clear();
// Initialise cache
m_scale = true;
m_fixed = true;
m_old_phibars.clear();
m_nodes.clear();
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] model Model.
***************************************************************************/
void GCOMModelDRBFitting::copy_members(const GCOMModelDRBFitting& model)
{
// Copy members
m_phibars = model.m_phibars;
m_values = model.m_values;
// Copy cache
m_scale = model.m_scale;
m_fixed = model.m_fixed;
m_old_phibars = model.m_old_phibars;
m_nodes = model.m_nodes;
// Set parameter pointers
set_pointers();
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GCOMModelDRBFitting::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Set pointers
*
* Set pointers to all model parameters. The pointers are stored in a vector
* that is member of the GModel base class.
***************************************************************************/
void GCOMModelDRBFitting::set_pointers(void)
{
// Clear parameter pointer(s)
m_pars.clear();
// Get number of nodes
int nodes = m_phibars.size();
// Set parameter pointers for all nodes
for (int i = 0; i < nodes; ++i) {
// Signal that Phibar values have no gradients
m_phibars[i].has_grad(false);
// Signal that values have gradients
m_values[i].has_grad(true);
// Set pointer
m_pars.push_back(&(m_phibars[i]));
m_pars.push_back(&(m_values[i]));
} // endfor: looped over nodes
// Return
return;
}
/***********************************************************************//**
* @brief Set evaluation cache
*
* Set the evaluation cache for fast computations. The evaluation cache
* consists of the Phibar values stored in a node array. The evaluation
* cache is only filled if at least 2 nodes are available. If only a single
* node is available, the model is considered as a simple scaling factor.
***************************************************************************/
void GCOMModelDRBFitting::set_cache(void) const
{
// Clear any existing values
m_old_phibars.clear();
m_nodes.clear();
m_fixed = true;
m_scale = true; // Signals that model is a scale factor
// Determine number of parameters
int num = m_phibars.size();
// Continue only if we have at least two nodes
if (num >= 2) {
// Signal that model is not a scale factor
m_scale = false;
// Set Phibar node array. Signal if one of the Phibar values is free
for (int i = 0; i < num; ++i) {
double phibar = m_phibars[i].value();
m_nodes.append(phibar);
m_old_phibars.push_back(phibar);
if (m_phibars[i].is_free()) {
m_fixed = false;
}
}
} // endif: there were at least two nodes
// Return
return;
}
/***********************************************************************//**
* @brief Update evaluation cache
*
* Updates the evaluation cache by computing the
* changed.
***************************************************************************/
void GCOMModelDRBFitting::update_cache(void) const
{
// Determine number of parameters
int num = m_phibars.size();
// Continue only if we have at least two nodes
if (num >= 2) {
// Update Phibar values
for (int i = 0; i < num; ++i) {
double phibar = m_phibars[i].value();
if (phibar != m_old_phibars[i]) {
m_nodes[i] = phibar;
m_old_phibars[i] = phibar;
}
}
} // endif: there were at least two nodes
// Return
return;
}