/***************************************************************************
* GModelSpectralExponential.cpp - Exponential spectral model class *
* ----------------------------------------------------------------------- *
* copyright (C) 2018-2020 by Luigi Tibaldo *
* ----------------------------------------------------------------------- *
* *
* 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 GModelSpectralExponential.cpp
* @brief Exponential spectral model class implementation
* @author Luigi Tibaldo
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "GException.hpp"
#include "GTools.hpp"
#include "GRan.hpp"
#include "GIntegral.hpp"
#include "GEnergies.hpp"
#include "GModelSpectralExponential.hpp"
#include "GModelSpectralNodes.hpp"
#include "GModelSpectralRegistry.hpp"
/* __ Constants __________________________________________________________ */
/* __ Globals ____________________________________________________________ */
const GModelSpectralExponential g_spectral_expo_seed;
const GModelSpectralRegistry g_spectral_expo_registry(&g_spectral_expo_seed);
/* __ Method name definitions ____________________________________________ */
#define G_MC "GModelSpectralExponential::mc(GEnergy&, GEnergy&, GTime&, "\
"GRan&)"
#define G_WRITE "GModelSpectralExponential::write(GXmlElement&)"
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GModelSpectralExponential::GModelSpectralExponential(void) :
GModelSpectral()
{
// Initialise private members for clean destruction
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief XML constructor
*
* @param[in] xml XML element containing spectral model information.
*
* Constructs an exponential spectral model by extracting information from
* an XML element.
***************************************************************************/
GModelSpectralExponential::GModelSpectralExponential(const GXmlElement& xml) :
GModelSpectral()
{
// Initialise members
init_members();
// Read information from XML element
read(xml);
// Return
return;
}
/***********************************************************************//**
* @brief Model constructor
*
* @param[in] spec Spectral model
*
* Constructs exponential spectral model by setting the exponent model.
***************************************************************************/
GModelSpectralExponential::GModelSpectralExponential(const GModelSpectral* spec) :
GModelSpectral()
{
// Initialise members
init_members();
// Set exponent
exponent(spec);
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] model Exponential spectral model.
***************************************************************************/
GModelSpectralExponential::GModelSpectralExponential(const GModelSpectralExponential& model) :
GModelSpectral(model)
{
// Initialise members
init_members();
// Copy members
copy_members(model);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GModelSpectralExponential::~GModelSpectralExponential(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] model Exponential spectral model.
* @return Exponential spectral model.
***************************************************************************/
GModelSpectralExponential& GModelSpectralExponential::operator=(const GModelSpectralExponential& model)
{
// Execute only if object is not identical
if (this != &model) {
// Copy base class members
this->GModelSpectral::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 Exponential spectral model
***************************************************************************/
void GModelSpectralExponential::clear(void)
{
// Free class members (base and derived classes, derived class first)
free_members();
this->GModelSpectral::free_members();
// Initialise members
this->GModelSpectral::init_members();
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Clone Exponential spectral model model
*
* @return Pointer to deep copy of Exponential spectral model.
***************************************************************************/
GModelSpectralExponential* GModelSpectralExponential::clone(void) const
{
// Clone spectral power law model
return new GModelSpectralExponential(*this);
}
/***********************************************************************//**
* @brief Evaluate function
*
* @param[in] srcEng True photon energy.
* @param[in] srcTime True photon arrival time.
* @param[in] gradients Compute gradients?
* @return Model value (ph/cm2/s/MeV).
*
* Evaluates
*
* \f[
* \exp{M}(\rm srcEng, srcTime)
* \f]
*
* where \f${M}\f$ is the exponent model component.
*
* If the @p gradients flag is true the method will also compute the partial
* derivatives of each parameter with respect to the parameters using
*
* \f[
* \frac{\delta S}{\delta P_{\rm i}}\exp{M}
* \f]
*
* where \f${P_{\rm i}}\f$ is the i-th parameter.
***************************************************************************/
double GModelSpectralExponential::eval(const GEnergy& srcEng,
const GTime& srcTime,
const bool& gradients) const
{
// Initialise result
double value = 0.0;
// Check if exponent is defined
if (m_exponent != NULL) {
// Calculate exponent value
value = m_exponent->eval(srcEng, srcTime, gradients);
// Calculate exponential
value = std::exp(value);
// Modify gradients if requested
if (gradients) {
// Loop over model parameters
for (int ipar = 0; ipar < m_exponent->size(); ++ipar) {
// Get reference to model parameter
GModelPar& par = m_exponent->operator[](ipar);
// Scale parameter gradient
par.gradient(par.gradient()*value);
} // endfor: loop over model parameters
} // endif: compute grdients
} //endif compute value and gradients
// Compile option: Check for NaN/Inf
#if defined(G_NAN_CHECK)
if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
std::cout << "*** ERROR: GModelSpectralExponential::eval";
std::cout << "(srcEng=" << srcEng;
std::cout << ", srcTime=" << srcTime << "):";
std::cout << " NaN/Inf encountered";
std::cout << " (value=" << value;
std::cout << ")" << std::endl;
}
#endif
// Return
return value;
}
/***********************************************************************//**
* @brief Returns model photon flux between [emin, emax] (units: ph/cm2/s)
*
* @param[in] emin Minimum photon energy.
* @param[in] emax Maximum photon energy.
* @return Photon flux (ph/cm2/s).
*
* Computes the photon flux of Exponential spectral model
***************************************************************************/
double GModelSpectralExponential::flux(const GEnergy& emin,
const GEnergy& emax) const
{
// Initialise flux
double flux = 0.0;
// Compute only if integration range is valid and exponent are available
if (emin < emax && m_exponent != NULL) {
// Initialise function to integrate
flux_kern kernel(m_exponent);
// Initialise integral class with function
GIntegral integral(&kernel);
// Set integration precision
integral.eps(1.0e-8);
// Calculate integral between emin and emax
flux = integral.romberg(emin.MeV(), emax.MeV());
} // endif: integration range was valid
// Return flux
return flux;
}
/***********************************************************************//**
* @brief Returns model energy flux between [emin, emax] (units: erg/cm2/s)
*
* @param[in] emin Minimum photon energy.
* @param[in] emax Maximum photon energy.
* @return Energy flux (erg/cm2/s).
*
* Computes the energy flux of Exponential spectral model
***************************************************************************/
double GModelSpectralExponential::eflux(const GEnergy& emin,
const GEnergy& emax) const
{
// Initialise eflux
double eflux = 0.0;
// Compute only if integration range is valid and exponent are available
if (emin < emax && m_exponent != NULL) {
// Initialise function to integrate
eflux_kern kernel(m_exponent);
// Initialise integral class with function
GIntegral integral(&kernel);
// Set integration precision
integral.eps(1.0e-8);
// Calculate integral between emin and emax
eflux = integral.romberg(emin.MeV(), emax.MeV());
} // endif: integration range was valid
// Return flux
return eflux;
}
/***********************************************************************//**
* @brief Returns Monte Carlo energy between [emin, emax]
*
* @param[in] emin Minimum photon energy.
* @param[in] emax Maximum photon energy.
* @param[in] time True photon arrival time.
* @param[in,out] ran Random number generator.
* @return Energy.
*
* @exception GException::erange_invalid
* Energy range is invalid (emin < emax required).
*
* Returns Monte Carlo energy by randomly drawing from a Exponential
* spectral model.
***************************************************************************/
GEnergy GModelSpectralExponential::mc(const GEnergy& emin,
const GEnergy& emax,
const GTime& time,
GRan& ran) const
{
// Throw an exception if energy range is invalid
if (emin >= emax) {
std::string msg = "Minimum energy "+emin.print()+" is equal or larger "
"than maximum energy "+emax.print()+". Please specify "
"a minimum energy that is smaller than the maximum "
"energy.";
throw GException::invalid_argument(G_MC, msg);
}
// Throw exception if exponent is undefined
if (m_exponent == NULL) {
std::string msg = "Exponent model is undefined.";
throw GException::invalid_value(G_MC, msg);
}
// Update MC cache
update_mc_cache(emin, emax);
// Set energy
GEnergy energy = m_mc_spectrum.mc(emin, emax, time, ran);
// Return energy
return energy;
}
/***********************************************************************//**
* @brief Read model from XML element
*
* @param[in] xml XML element.
*
* Reads the spectral information from an XML element. The XML element shall
* have the following format
*
*
*
*
*
*
*
***************************************************************************/
void GModelSpectralExponential::read(const GXmlElement& xml)
{
// Get exponent XML element
const GXmlElement* spec = xml.element("spectrum", 0);
// Continue only if the XML element contains children
if (spec->elements() > 0) {
// Allocate a spectral registry object
GModelSpectralRegistry registry;
// Read spectral model
GModelSpectral* ptr = registry.alloc(*spec);
// Set spectral component as exponent
exponent(ptr);
// Free spectral model
delete ptr;
} // endif: XML element contains children
// Return
return;
}
/***********************************************************************//**
* @brief Write model into XML element
*
* @param[in] xml XML element.
*
* @exception GException::model_invalid_spectral
* Existing XML element is not of the expected type.
*
* Writes the spectral information into an XML element. The XML element
* will have the following format:
*
*
*
*
*
*
*
* If no exponential model component is defined the method writes the
* following XML structure
*
*
*
*
***************************************************************************/
void GModelSpectralExponential::write(GXmlElement& xml) const
{
// Set model type
if (xml.attribute("type") == "") {
xml.attribute("type", type());
}
// Verify model type
if (xml.attribute("type") != type()) {
throw GException::model_invalid_spectral(G_WRITE, xml.attribute("type"),
"Spectral model is not of type \""+type()+"\".");
}
// Create a spectrum node
xml.append(GXmlElement("spectrum"));
// Get spectrum node
GXmlElement* spec = xml.element("spectrum", 0);
// Write spectral component if it exists
if (m_exponent != NULL) {
m_exponent->write(*spec);
}
// Return
return;
}
/***********************************************************************//**
* @brief Set exponent
*
* @param[in] spec Spectral model to use as exponent.
*
* Set a spectral model as exponent
***************************************************************************/
void GModelSpectralExponential::exponent(const GModelSpectral* spec)
{
// Set exponent
m_exponent = spec->clone();
// Get number of spectral parameters from model
int npars = m_exponent->size();
// Store pointers to spectral parameters
m_pars.clear();
for (int ipar = 0; ipar < npars; ++ipar) {
// Get model parameter reference
GModelPar& par = m_exponent->operator[](ipar);
// Append model parameter pointer to internal container
m_pars.push_back(&par);
}
// Return
return;
}
/***********************************************************************//**
* @brief Return exponent
*
* Returns pointer to exponent spectral model
***************************************************************************/
const GModelSpectral* GModelSpectralExponential::exponent(void) const
{
// Returns exponent
return m_exponent;
}
/***********************************************************************//**
* @brief Print Exponential spectral model information
*
* @param[in] chatter Chattiness.
* @return String containing model information.
***************************************************************************/
std::string GModelSpectralExponential::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("=== GModelSpectralExponential ===");
// Append information
result.append("\n"+gammalib::parformat("Number of parameters"));
result.append(gammalib::str(size()));
// Print parameter information
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 GModelSpectralExponential::init_members(void)
{
// Initialise model type
m_type = "Exponential";
// Clear exponent
m_exponent = NULL;
// Clear MC cache
m_mc_spectrum.clear();
m_mc_emin.clear();
m_mc_emax.clear();
m_mc_values.clear();
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] model Exponential spectral model.
***************************************************************************/
void GModelSpectralExponential::copy_members(const GModelSpectralExponential& model)
{
// Copy members
m_type = model.m_type;
// Copy MC cache
m_mc_spectrum = model.m_mc_spectrum;
m_mc_emin = model.m_mc_emin;
m_mc_emax = model.m_mc_emax;
m_mc_values = model.m_mc_values;
// Set exponent
if (model.m_exponent != NULL) {
exponent(model.m_exponent);
}
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GModelSpectralExponential::free_members(void)
{
// Free exponent
if (m_exponent != NULL) delete m_exponent;
// Signal free pointer
m_exponent = NULL;
// Return
return;
}
/***********************************************************************//**
* @brief Update Monte Carlo pre computation cache
*
* @param[in] emin Minimum photon energy.
* @param[in] emax Maximum photon energy.
*
* Updates the precomputation cache for Monte Carlo simulations. In case that
* the energy boundaries have changed or at least one of the model parameters
* has changed the method computes a spectral node function which has 100
* nodes per decade containing the Exponential spectral model values and
* stores that into a Monte Carlo cache. This cache is then used by the mc()
* method for simulations.
***************************************************************************/
void GModelSpectralExponential::update_mc_cache(const GEnergy& emin,
const GEnergy& emax) const
{
// Check if one of the parameters has changed. If the dimension of the
// parameter value cache differs from the number of parameters then notify
// a change. This will clear the cache and store the parameter values
// later
bool par_changed = (m_mc_values.size() != size());
if (par_changed == false) {
for (int i = 0; i < size(); ++i) {
if (m_pars[i]->value() != m_mc_values[i]) {
par_changed = true;
break;
}
}
}
// Update cache if energy range or parameters have changed
if (par_changed || emin != m_mc_emin || emax != m_mc_emax) {
// Store energy range
m_mc_emin = emin;
m_mc_emax = emax;
// If parameters have changed then store the current parameter
// values for a comparison check for the next method call
if (par_changed) {
m_mc_values.clear();
for (int i = 0; i < size(); ++i) {
m_mc_values.push_back(m_pars[i]->value());
}
}
// Clear spectral nodes
m_mc_spectrum.clear();
// Compute number of nodes. We use here 100 nodes per log energy and
// assure that at least 100 nodes are used.
int nodes = int((emax.log10MeV() - emin.log10MeV()) * 100.0);
if (nodes < 100) {
nodes = 100;
}
// Initialise energy array with fixed number of nodes
GEnergies energies = GEnergies(nodes, m_mc_emin, m_mc_emax, "LOG");
// Append nodes to spectral function
for (int i = 0; i < energies.size(); ++i) {
m_mc_spectrum.append(energies[i], eval(energies[i]));
}
} // endif: emin and emax have changed
// Return
return;
}