/***************************************************************************
* GModelSpectralSmoothBrokenPlaw.cpp *
* Smoothly broken power law spectrum class *
* ----------------------------------------------------------------------- *
* copyright (C) 2017 by Joshua Cardenzana *
* ----------------------------------------------------------------------- *
* *
* 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 GModelSpectralSmoothBrokenPlaw.cpp
* @brief Smoothly broken power law spectrum class implementation
* @author Joshua Cardenzana
*/
/* __ Includes ___________________________________________________________ */
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include "GException.hpp"
#include "GIntegral.hpp"
#include "GTools.hpp"
#include "GRan.hpp"
#include "GModelSpectralSmoothBrokenPlaw.hpp"
#include "GModelSpectralRegistry.hpp"
/* __ Constants __________________________________________________________ */
/* __ Globals ____________________________________________________________ */
const GModelSpectralSmoothBrokenPlaw g_spectral_sblaw_seed1("SmoothBrokenPowerLaw",
"Prefactor",
"Index1",
"PivotEnergy",
"Index2",
"BreakEnergy",
"BreakSmoothness");
const GModelSpectralRegistry g_spectral_sblaw_registry1(&g_spectral_sblaw_seed1);
const GModelSpectralSmoothBrokenPlaw g_spectral_sblaw_seed2("SmoothBrokenPowerLaw",
"Prefactor",
"Index1",
"Scale",
"Index2",
"BreakValue",
"Beta");
const GModelSpectralRegistry g_spectral_sblaw_registry2(&g_spectral_sblaw_seed2);
/* __ Method name definitions ____________________________________________ */
#define G_MC "GModelSpectralSmoothBrokenPlaw::mc(GEnergy&, GEnergy&, GTime&,"\
" GRan&)"
#define G_READ "GModelSpectralSmoothBrokenPlaw::read(GXmlElement&)"
#define G_WRITE "GModelSpectralSmoothBrokenPlaw::write(GXmlElement&)"
/* __ Macros _____________________________________________________________ */
/* __ Coding definitions _________________________________________________ */
/* __ Debug definitions __________________________________________________ */
/*==========================================================================
= =
= Constructors/destructors =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Void constructor
***************************************************************************/
GModelSpectralSmoothBrokenPlaw::GModelSpectralSmoothBrokenPlaw(void) :
GModelSpectral()
{
// Initialise private members for clean destruction
init_members();
// Return
return;
}
/***********************************************************************//**
* @brief Model type and parameter name constructor
*
* @param[in] type Model type.
* @param[in] prefactor Name of prefactor parameter.
* @param[in] index1 Name of index1 parameter.
* @param[in] pivot Name of pivot parameter.
* @param[in] index2 Name of index2 parameter.
* @param[in] breakenergy Name of breakenergy parameter.
* @param[in] beta Name of beta parameter.
***************************************************************************/
GModelSpectralSmoothBrokenPlaw::GModelSpectralSmoothBrokenPlaw(
const std::string& type,
const std::string& prefactor,
const std::string& index1,
const std::string& pivot,
const std::string& index2,
const std::string& breakenergy,
const std::string& beta) :
GModelSpectral()
{
// Initialise members
init_members();
// Set model type
m_type = type;
// Set parameter names
m_norm.name(prefactor);
m_index1.name(index1);
m_pivot.name(pivot);
m_index2.name(index2);
m_breakenergy.name(breakenergy);
m_beta.name(beta);
// Return
return;
}
/***********************************************************************//**
* @brief Parameter constructor
*
* @param[in] prefactor Smoothly broken power law pre factor (ph/cm2/s/MeV).
* @param[in] index1 Smoothly broken power law index1.
* @param[in] pivot Smoothly broken power law pivot energy
* @param[in] index2 Smoothly broken power law index1.
* @param[in] breakenergy Break energy.
* @param[in] beta Break smoothness parameter
*
* Constructs a smoothly broken power law using the model parameters
* power law @p prefactor (ph/cm2/s/MeV),
* spectral @p index1,
* @p pivot energy,
* spectral @p index2,
* @p breakenergy of spectral break, and
* smoothness parameter @p beta.
***************************************************************************/
GModelSpectralSmoothBrokenPlaw::GModelSpectralSmoothBrokenPlaw(
const double& prefactor,
const double& index1,
const GEnergy& pivot,
const double& index2,
const GEnergy& breakenergy,
const double& beta) :
GModelSpectral()
{
// Initialise members
init_members();
// Set parameters
m_norm.value(prefactor);
m_index1.value(index1);
m_pivot.value(pivot.MeV()); // Internally stored in MeV
m_index2.value(index2);
m_breakenergy.value(breakenergy.MeV()); // Internally stored in MeV
m_beta.value(beta);
// Perform autoscaling of parameter
autoscale();
// Return
return;
}
/***********************************************************************//**
* @brief XML constructor
*
* @param[in] xml XML element containing position information.
*
* Constructs a smoothly broken power law spectral model by extracting
* information from an XML element. See the read() method for more
* information about the expected structure of the XML element.
***************************************************************************/
GModelSpectralSmoothBrokenPlaw::GModelSpectralSmoothBrokenPlaw(const GXmlElement& xml) :
GModelSpectral()
{
// Initialise members
init_members();
// Read information from XML element
read(xml);
// Return
return;
}
/***********************************************************************//**
* @brief Copy constructor
*
* @param[in] model Smoothly broken power law model.
***************************************************************************/
GModelSpectralSmoothBrokenPlaw::GModelSpectralSmoothBrokenPlaw(
const GModelSpectralSmoothBrokenPlaw& model) :
GModelSpectral(model)
{
// Initialise members
init_members();
// Copy members
copy_members(model);
// Return
return;
}
/***********************************************************************//**
* @brief Destructor
***************************************************************************/
GModelSpectralSmoothBrokenPlaw::~GModelSpectralSmoothBrokenPlaw(void)
{
// Free members
free_members();
// Return
return;
}
/*==========================================================================
= =
= Operators =
= =
==========================================================================*/
/***********************************************************************//**
* @brief Assignment operator
*
* @param[in] model Smoothly broken power law model.
* @return Smoothly broken power law model.
***************************************************************************/
GModelSpectralSmoothBrokenPlaw& GModelSpectralSmoothBrokenPlaw::operator=(
const GModelSpectralSmoothBrokenPlaw& 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 smoothly broken power law model
***************************************************************************/
void GModelSpectralSmoothBrokenPlaw::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 smoothly broken power law model
*
* @return Pointer to deep copy of smoothly broken power law model.
***************************************************************************/
GModelSpectralSmoothBrokenPlaw* GModelSpectralSmoothBrokenPlaw::clone(void) const
{
// Clone spectral broken power law model
return new GModelSpectralSmoothBrokenPlaw(*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[
* S_{\rm E}(E | t) = k_0 \left( \frac{E}{E_0} \right)^{\gamma_1}
* \left[ 1 + \left( \frac{E}{E_b} \right)^{\frac{\gamma_1 - \gamma_2}{\beta}}
* \right]^{-\beta}
* \f]
*
* where:
* - \f$k_0\f$ is the normalization or prefactor,
* - \f$\gamma_1\f$ is the spectral index before the break,
* - \f$\gamma_2\f$ is the spectral index after the break,
* - \f$E_0\f$ is the pivot energy,
* - \f$E_b\f$ is the break energy,
* - \f$\beta\f$ is the break smoothness.
*
* If the @p gradients flag is true the method will also compute the
* partial derivatives of the model with respect to the parameters using
*
* \f[
* \frac{\delta S_{\rm E}(E | t)}{\delta k_0} =
* \frac{S_{\rm E}(E | t)}{k_0}
* \f]
*
* \f[
* \frac{\delta S_{\rm E}(E | t)}{\delta \gamma_1} =
* S_{\rm E}(E | t) \left[ \ln\left( \frac{E}{E_0} \right) -
* \frac{\left(\frac{E}{E_b}\right)^{\frac{\gamma_1 - \gamma_2}{\beta}}
* \ln\left( \frac{E}{E_b} \right)}
* {\left(\frac{E}{E_b}\right)^{\frac{\gamma_1 - \gamma_2}{\beta}} + 1}\right]
* \f]
*
* \f[
* \frac{\delta S_{\rm E}(E | t)}{\delta \gamma_2} =
* S_{\rm E}(E | t)
* \frac{\left(\frac{E}{E_b}\right)^{\frac{\gamma_1 - \gamma_2}{\beta}}
* \ln\left( \frac{E}{E_b} \right)}
* {\left(\frac{E}{E_b}\right)^{\frac{\gamma_1 - \gamma_2}{\beta}} + 1}
* \f]
*
* \f[
* \frac{\delta S_{\rm E}(E | t)}{\delta E_0} =
* S_{\rm E}(E | t) \frac{\gamma_1}{E_0}
* \f]
*
* \f[
* \frac{\delta S_{\rm E}(E | t)}{\delta E_b} =
* S_{\rm E}(E | t) \frac{\left(\gamma_1 - \gamma_2 \right)
* \left( \frac{E}{E_b} \right)^{\frac{\gamma_1 - \gamma_2}{\beta}}}
{E_b \left(1 + \left(\frac{E}{E_b}\right)^{\frac{\gamma_1 - \gamma_2}{\beta}} \right)}
* \f]
*
* \f[
* \frac{\delta S_{\rm E}(E | t)}{\delta \beta} = S_{\rm E}(E | t)
* \left[
* \frac{ \left( \frac{E}{E_b} \right)^{\frac{\gamma_1 - \gamma_2}{\beta}}
* \ln \left( \left( \frac{E}{E_b}\right)^{ \frac{\gamma_1 - \gamma_2}{\beta}} \right)}
* {1 + \left(\frac{E}{E_b}\right)^{\frac{\gamma_1 - \gamma_2}{\beta}}}
* - \ln \left( 1 + \left(\frac{E}{E_b}\right)^{\frac{\gamma_1 - \gamma_2}{\beta}} \right)
* \right]
* \f]
*
* @todo The method expects that energy!=0. Otherwise Inf or NaN may result.
***************************************************************************/
double GModelSpectralSmoothBrokenPlaw::eval(const GEnergy& srcEng,
const GTime& srcTime,
const bool& gradients) const
{
// Update the evaluation cache
update_eval_cache(srcEng);
// Compute function value
double value = m_norm.value() * m_last_epivot_pow *
std::pow(1.0 + m_last_ebreak_pow, -m_last_beta);
// Optionally compute gradients
if (gradients) {
// Compute normalisation gradient
double g_norm = (m_norm.is_free())
? m_norm.scale() * m_last_epivot_pow *
std::pow(1.0 + m_last_ebreak_pow, -m_last_beta)
: 0.0;
// Compute index1 and index2 value gradients
double g_index1 = (m_index1.is_free())
? value * m_index1.scale() * (m_last_log_epivot_norm -
((m_last_ebreak_pow * m_last_log_ebreak_norm) /
(m_last_ebreak_pow + 1.0)))
: 0.0;
double g_index2 = (m_index2.is_free())
? value * m_index2.scale() * m_last_log_ebreak_norm *
m_last_ebreak_pow / (1.0 + m_last_ebreak_pow)
: 0.0;
// Compute pivot and break energy value gradients
double g_pivot = (m_pivot.is_free())
? -value * m_last_index1 / m_pivot.factor_value()
: 0.0;
double g_break = (m_breakenergy.is_free())
? value * (m_last_index1-m_last_index2) * m_last_ebreak_pow /
((1.0+m_last_ebreak_pow) * m_breakenergy.factor_value())
: 0.0;
// Compute beta gradient
double g_beta = (m_beta.is_free())
? value * m_beta.scale() *
((std::log(m_last_ebreak_pow) * m_last_ebreak_pow) /
(1.0+m_last_ebreak_pow) - std::log(1.0+m_last_ebreak_pow))
: 0.0;
// Store the gradient values
m_norm.factor_gradient(g_norm);
m_index1.factor_gradient(g_index1);
m_index2.factor_gradient(g_index2);
m_pivot.factor_gradient(g_pivot);
m_breakenergy.factor_gradient(g_break);
m_beta.factor_gradient(g_beta);
} // endif: gradient computation was requested
// Compile option: Check for NaN/Inf
#if defined(G_NAN_CHECK)
if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
std::cout << "*** ERROR: GModelSpectralSmoothBrokenPlaw::eval";
std::cout << "(srcEng=" << srcEng;
std::cout << ", srcTime=" << srcTime << "):";
std::cout << " NaN/Inf encountered";
std::cout << " (value=" << value;
std::cout << ", m_norm=" << m_norm.value();
std::cout << ", m_index1=" << m_index1.value();
std::cout << ", m_index2=" << m_index2.value();
std::cout << ", m_pivot=" << m_pivot.value();
std::cout << ", m_breakenergy=" << m_breakenergy.value();
std::cout << ", m_beta=" << m_beta.value();
std::cout << ", m_last_epivot_norm=" << m_last_epivot_norm;
std::cout << ", m_last_ebreak_norm=" << m_last_ebreak_norm;
std::cout << ", m_last_log_epivot_norm=" << m_last_log_epivot_norm;
std::cout << ", m_last_log_ebreak_norm=" << m_last_log_ebreak_norm;
std::cout << ", m_last_epivot_pow=" << m_last_epivot_pow;
std::cout << ", m_last_ebreak_pow=" << m_last_ebreak_pow;
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
*
* \f[
* \int_{\tt emin}^{\tt emax} S_{\rm E}(E | t) dE
* \f]
*
* where
* - [@p emin, @p emax] is an energy interval, and
* - \f$S_{\rm E}(E | t)\f$ is the spectral model (ph/cm2/s/MeV).
* The integration is done numerically.
***************************************************************************/
double GModelSpectralSmoothBrokenPlaw::flux(const GEnergy& emin,
const GEnergy& emax) const
{
// Initialise flux
double flux = 0.0;
// Compute only if integration range is valid
if (emin < emax) {
// Initialise function to integrate
flux_kern kernel(prefactor(), index1(), pivot(),
index2(), breakenergy(), beta());
// 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
*
* \f[
* \int_{\tt emin}^{\tt emax} S_{\rm E}(E | t) E \, dE
* \f]
*
* where
* - [@p emin, @p emax] is an energy interval, and
* - \f$S_{\rm E}(E | t)\f$ is the spectral model (ph/cm2/s/MeV).
* The integration is done numerically.
***************************************************************************/
double GModelSpectralSmoothBrokenPlaw::eflux(const GEnergy& emin,
const GEnergy& emax) const
{
// Initialise flux
double eflux = 0.0;
// Compute only if integration range is valid
if (emin < emax) {
// Initialise function to integrate
eflux_kern kernel(prefactor(), index1(), pivot(),
index2(), breakenergy(), beta());
// 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());
// Convert from MeV/cm2/s to erg/cm2/s
eflux *= gammalib::MeV2erg;
} // 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 smoothly broken
* power law.
***************************************************************************/
GEnergy GModelSpectralSmoothBrokenPlaw::mc(const GEnergy& emin,
const GEnergy& emax,
const GTime& time,
GRan& ran) const
{
// Throw exception if energy range is not valid
if (emin >= emax) {
throw GException::erange_invalid(G_MC, emin.MeV(), emax.MeV(),
"Minimum energy < maximum energy required.");
}
// Allocate energy
GEnergy energy;
// Update Monte Carlo cache
update_mc_cache();
// Initialse acceptance fraction
double acceptance_fraction(0.0);
// Use rejection method to draw a random energy. We first draw
// analytically from a broken power law, and then compare the power law
// at the drawn energy to the curved function. This gives an acceptance
// fraction, and we accept the energy only if a uniform random number
// is <= the acceptance fraction.
do {
// Generate an energy from the broken power law distribution
energy = m_mc_brokenplaw.mc(emin, emax, time, ran);
// Compute acceptance fraction
acceptance_fraction = eval(energy) / m_mc_brokenplaw.eval(energy);
} while (ran.uniform() > acceptance_fraction);
// Return energy
return energy;
}
/***********************************************************************//**
* @brief Read model from XML element
*
* @param[in] xml XML element.
*
* Reads the spectral information from an XML element.
***************************************************************************/
void GModelSpectralSmoothBrokenPlaw::read(const GXmlElement& xml)
{
// Get remaining XML parameters
const GXmlElement* prefactor = gammalib::xml_get_par(G_READ, xml, m_norm.name());
const GXmlElement* index1 = gammalib::xml_get_par(G_READ, xml, m_index1.name());
const GXmlElement* index2 = gammalib::xml_get_par(G_READ, xml, m_index2.name());
const GXmlElement* pivot = gammalib::xml_get_par(G_READ, xml, m_pivot.name());
const GXmlElement* breakenergy = gammalib::xml_get_par(G_READ, xml, m_breakenergy.name());
const GXmlElement* beta = gammalib::xml_get_par(G_READ, xml, m_beta.name());
// Read parameters
m_norm.read(*prefactor);
m_index1.read(*index1);
m_index2.read(*index2);
m_pivot.read(*pivot);
m_breakenergy.read(*breakenergy);
m_beta.read(*beta);
// 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.
***************************************************************************/
void GModelSpectralSmoothBrokenPlaw::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()+"\".");
}
// Get XML parameters
GXmlElement* norm = gammalib::xml_need_par(G_WRITE, xml, m_norm.name());
GXmlElement* index1 = gammalib::xml_need_par(G_WRITE, xml, m_index1.name());
GXmlElement* index2 = gammalib::xml_need_par(G_WRITE, xml, m_index2.name());
GXmlElement* pivot = gammalib::xml_need_par(G_WRITE, xml, m_pivot.name());
GXmlElement* breakenergy = gammalib::xml_need_par(G_WRITE, xml, m_breakenergy.name());
GXmlElement* beta = gammalib::xml_need_par(G_WRITE, xml, m_beta.name());
// Write parameters
m_norm.write(*norm);
m_index1.write(*index1);
m_pivot.write(*pivot);
m_index2.write(*index2);
m_breakenergy.write(*breakenergy);
m_beta.write(*beta);
// Return
return;
}
/***********************************************************************//**
* @brief Print smooth broken powerlaw information
*
* @param[in] chatter Chattiness.
* @return String containing model information.
***************************************************************************/
std::string GModelSpectralSmoothBrokenPlaw::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("=== GModelSpectralSmoothBrokenPlaw ===");
// Append information
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 GModelSpectralSmoothBrokenPlaw::init_members(void)
{
// Initialise model type
m_type = "SmoothBrokenPowerLaw";
// Initialise pre factor
m_norm.clear();
m_norm.name("Prefactor");
m_norm.unit("ph/cm2/s/MeV");
m_norm.scale(1.0);
m_norm.value(1.0); // default: 1.0
m_norm.min(0.0); // min: 0.0
m_norm.free();
m_norm.gradient(0.0);
m_norm.has_grad(true);
// Initialise index1
m_index1.clear();
m_index1.name("Index1");
m_index1.scale(1.0);
m_index1.value(-2.0); // default: -2.0
m_index1.range(-10.0,+10.0); // range: [-10,+10]
m_index1.free();
m_index1.gradient(0.0);
m_index1.has_grad(true);
// Initialise index2
m_index2.clear();
m_index2.name("Index2");
m_index2.scale(1.0);
m_index2.value(-3.0); // default: -2.0
m_index2.range(-10.0,+10.0); // range: [-10,+10]
m_index2.free();
m_index2.gradient(0.0);
m_index2.has_grad(true);
// Initialise pivot energy
m_pivot.clear();
m_pivot.name("PivotEnergy");
m_pivot.unit("MeV");
m_pivot.scale(1.0e5);
m_pivot.value(1.0); // default: 100 GeV
m_pivot.fix();
m_pivot.gradient(0.0);
m_pivot.has_grad(true);
// Initialise break energy
m_breakenergy.clear();
m_breakenergy.name("BreakEnergy");
m_breakenergy.unit("MeV");
m_breakenergy.scale(1.0e5);
m_breakenergy.value(1.0); // default: 100 GeV
m_breakenergy.free();
m_breakenergy.gradient(0.0);
m_breakenergy.has_grad(true);
// Initialize beta (break smoothness parameter)
m_beta.clear();
m_beta.name("BreakSmoothness");
m_beta.scale(1.0);
m_beta.value(1.0);
m_beta.range(0.01,10.0);
m_beta.free();
m_beta.gradient(0.0);
m_beta.has_grad(true);
// Set parameter pointer(s)
m_pars.clear();
m_pars.push_back(&m_norm);
m_pars.push_back(&m_index1);
m_pars.push_back(&m_pivot);
m_pars.push_back(&m_index2);
m_pars.push_back(&m_breakenergy);
m_pars.push_back(&m_beta);
// Initialise eval cache
m_last_energy.clear();
m_last_index1 = 1.0e30;
m_last_index2 = 1.0e30;
m_last_pivot = 1.0e30;
m_last_breakenergy = 1.0e30;
m_last_beta = 1.0e30;
m_last_epivot_norm = 1.0e30;
m_last_ebreak_norm = 1.0e30;
m_last_log_epivot_norm = 1.0e30;
m_last_log_ebreak_norm = 1.0e30;
m_last_epivot_pow = 1.0e30;
m_last_ebreak_pow = 1.0e30;
// Initialise MC cache
m_mc_prefactor = 1.0e30;
m_mc_index1 = 1.0e30;
m_mc_index2 = 1.0e30;
m_mc_pivot = 1.0e30;
m_mc_breakenergy = 1.0e30;
m_mc_brokenplaw = GModelSpectralBrokenPlaw();
// Return
return;
}
/***********************************************************************//**
* @brief Copy class members
*
* @param[in] model Smooth broken power law.
***************************************************************************/
void GModelSpectralSmoothBrokenPlaw::copy_members(
const GModelSpectralSmoothBrokenPlaw& model)
{
// Copy members
m_type = model.m_type;
m_norm = model.m_norm;
m_index1 = model.m_index1;
m_index2 = model.m_index2;
m_pivot = model.m_pivot;
m_breakenergy = model.m_breakenergy;
m_beta = model.m_beta;
// Set parameter pointer(s)
m_pars.clear();
m_pars.push_back(&m_norm);
m_pars.push_back(&m_index1);
m_pars.push_back(&m_index2);
m_pars.push_back(&m_pivot);
m_pars.push_back(&m_breakenergy);
m_pars.push_back(&m_beta);
// Copy eval cache
m_last_energy = model.m_last_energy;
m_last_index1 = model.m_last_index1;
m_last_index2 = model.m_last_index2;
m_last_pivot = model.m_last_pivot;
m_last_breakenergy = model.m_last_breakenergy;
m_last_beta = model.m_last_beta;
m_last_epivot_norm = model.m_last_epivot_norm;
m_last_ebreak_norm = model.m_last_ebreak_norm;
m_last_log_epivot_norm = model.m_last_log_epivot_norm;
m_last_log_ebreak_norm = model.m_last_log_ebreak_norm;
m_last_epivot_pow = model.m_last_epivot_pow;
m_last_ebreak_pow = model.m_last_ebreak_pow;
// Copy MC cache
m_mc_prefactor = model.m_mc_prefactor;
m_mc_index1 = model.m_mc_index1;
m_mc_index2 = model.m_mc_index2;
m_mc_pivot = model.m_mc_pivot;
m_mc_breakenergy = model.m_mc_breakenergy;
m_mc_brokenplaw = model.m_mc_brokenplaw;
// Return
return;
}
/***********************************************************************//**
* @brief Delete class members
***************************************************************************/
void GModelSpectralSmoothBrokenPlaw::free_members(void)
{
// Return
return;
}
/***********************************************************************//**
* @brief Update eval precomputation cache
*
* @param[in] energy Energy.
*
* Updates the precomputation cache for eval() the method.
***************************************************************************/
void GModelSpectralSmoothBrokenPlaw::update_eval_cache(const GEnergy& energy) const
{
// Get parameter values
double index1 = m_index1.value();
double index2 = m_index2.value();
double pivot_eng = pivot().MeV();
double break_eng = breakenergy().MeV();
double beta = m_beta.value();
// If the energy or one of the parameters index1, index2, breakenergy
// energy, or beta has changed then recompute the cache
if ((m_last_energy != energy) ||
(m_last_index1 != index1) ||
(m_last_index2 != index2) ||
(m_last_pivot != pivot_eng) ||
(m_last_breakenergy != break_eng) ||
(m_last_beta != beta)) {
// Store actual energy and parameter values
m_last_energy = energy;
m_last_index1 = index1;
m_last_index2 = index2;
m_last_pivot = pivot_eng;
m_last_breakenergy = break_eng;
m_last_beta = beta;
// Compute and store value
double eng = energy.MeV();
m_last_epivot_norm = eng / m_last_pivot;
m_last_ebreak_norm = eng / m_last_breakenergy;
m_last_log_epivot_norm = std::log(m_last_epivot_norm);
m_last_log_ebreak_norm = std::log(m_last_ebreak_norm);
m_last_epivot_pow = std::pow(m_last_epivot_norm,m_last_index1);
m_last_ebreak_pow = std::pow(m_last_ebreak_norm,
(m_last_index1-m_last_index2)/m_last_beta) ;
} // endif: recomputation was required
// Return
return;
}
/***********************************************************************//**
* @brief Update Monte Carlo pre computation cache
*
* Updates the precomputation cache for Monte Carlo simulations.
***************************************************************************/
void GModelSpectralSmoothBrokenPlaw::update_mc_cache(void) const
{
// Check if we need to update the cache
if (prefactor() != m_mc_prefactor ||
index1() != m_mc_index1 ||
index2() != m_mc_index2 ||
pivot().MeV() != m_mc_pivot ||
breakenergy().MeV() != m_mc_breakenergy) {
// Set parameters for which Monte Carlo cache was computed
m_mc_prefactor = prefactor();
m_mc_index1 = index1();
m_mc_index2 = index2();
m_mc_pivot = pivot().MeV();
m_mc_breakenergy = breakenergy().MeV();
// Compute prefactor at pivot energy
double pre = prefactor() *
std::pow(breakenergy().MeV()/pivot().MeV(), index1());
// Find out which index is harder. This is important since the smoothly
// broken power law follows the hard index below the break energy and
// the softer index above the break energy.
double index1 = (m_mc_index1 > m_mc_index2) ? m_mc_index1 : m_mc_index2;
double index2 = (m_mc_index1 > m_mc_index2) ? m_mc_index2 : m_mc_index1;
// Set broken power law for Monte Carlo simulations
m_mc_brokenplaw = GModelSpectralBrokenPlaw(pre,
index1,
breakenergy(),
index2);
} // endif: Update was required
// Return
return;
}