/***********************************************************************//** * @brief Returns MC 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 constant between * the minimum and maximum photon energy. * * Method Used: Box-Muller transform, outlined here: * http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform * * Code from: http://www.design.caltech.edu/erik/Misc/Gaussian.html ***************************************************************************/ GEnergy GModelSpectralGauss::mc(const GEnergy& emin, const GEnergy& emax, const GTime& time, GRan& ran) const { // Get energy boundaries in MeV double xmax = emax.MeV(); double xmin = emin.MeV(); // Initialize return energy double energy = 0.0; // Throw an exception if energy range is invalid if (xmin >= xmax) { throw GException::erange_invalid(G_MC, xmin, xmax, "Minimum energy < maximum energy required."); } // Sample until we find a value within the requested energy range do { // Compute random value double val = ran.normal(); // Scale to specified width and shift by mean value energy = m_sigma.value() * val + m_mean.value(); } while (energy < xmin || energy > xmax); // Return energy return GEnergy(energy, "MeV"); }
/***********************************************************************//** * @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 GModelSpectralExpPlaw::eflux(const GEnergy& emin, const GEnergy& emax) const { // Initialise flux double eflux = 0.0; // Compute only if integration range is valid if (emin < emax) { // Setup integration kernel eflux_kernel integrand(m_norm.value(), m_index.value(), m_pivot.value(), m_ecut.value()); GIntegral integral(&integrand); // Get integration boundaries in MeV double e_min = emin.MeV(); double e_max = emax.MeV(); // Perform integration eflux = integral.romberg(e_min, e_max); // Convert from MeV/cm2/s to erg/cm2/s eflux *= gammalib::MeV2erg; } // endif: integration range was valid // Return return eflux; }
/***********************************************************************//** * @brief Convert value into flux * * @param[in] energy Energy at which flux is given. * @param[in] flux Flux value. * @param[in] unit Unit of value. * * @exception GMWLException::invalid_unit * Invalid unit string encountered * * Converts a flux value into units of ph/cm2/s/MeV based on the specified * units. The following units are supported (case insensitive): * ph/cm2/s/MeV, ph/s/cm2/MeV, erg/cm2/s and erg/s/cm2. ***************************************************************************/ double GMWLSpectrum::conv_flux(const GEnergy& energy, const double& flux, const std::string& unit) { // Initialise energy double result; // Convert unit string to upper base without any leading/trailing // whitespace std::string str_unit = gammalib::strip_whitespace(gammalib::toupper(unit)); // High-energy units if (str_unit == "PH/CM2/S/MEV" || str_unit == "PH/S/CM2/MEV") { result = flux; } else if (str_unit == "ERG/CM2/S" || str_unit == "ERG/S/CM2") { result = (gammalib::erg2MeV*flux) / (energy.MeV()*energy.MeV()); } // ... otherwise throw exception else { throw GMWLException::invalid_unit(G_CONV_FLUX, unit); } // Return energy return result; }
/***********************************************************************//** * @brief Returns model photon flux between [emin, emax] (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 analytically. ***************************************************************************/ double GModelSpectralGauss::flux(const GEnergy& emin, const GEnergy& emax) const { // Initialise flux double flux = 0.0; // Compute only if integration range is valid if (emin < emax) { // Precomputations double energy_min = emin.MeV(); double energy_max = emax.MeV(); double norm = m_norm.value(); double mean = m_mean.value(); double sigma = m_sigma.value(); double denom = 1.0 / (gammalib::sqrt_two*sigma); double zmin = (energy_min - mean) * denom; double zmax = (energy_max - mean) * denom; // Compute flux for a constant model flux = norm*(gammalib::erfcc(zmin) - gammalib::erfcc(zmax))/2.0; } // endif: integration range was valid // Return 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 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 Set logarithmically spaced energy intervals * * @param[in] num Number of energy intervals. * @param[in] emin Minimum energy of first interval. * @param[in] emax Maximum energy of last interval. * * Creates @p num logarithmically spaced energy boundaries running from * @p emin to @p emax. ***************************************************************************/ void GEbounds::set_log(const int& num, const GEnergy& emin, const GEnergy& emax) { // Initialise members clear(); // Compute bin width double elogmin = std::log10(emin.MeV()); double elogmax = std::log10(emax.MeV()); double elogbin = (elogmax - elogmin)/double(num); // Append boundaries GEnergy min; GEnergy max; for (int i = 0; i < num; ++i) { min.MeV(std::pow(10.0, double(i)*elogbin + elogmin)); max.MeV(std::pow(10.0, double(i+1)*elogbin + elogmin)); append(min, max); } // Set attributes set_attributes(); // Return return; }
/***********************************************************************//** * @brief Returns MC energy between [emin, emax] * * @param[in] emin Minimum photon energy. * @param[in] emax Maximum photon energy. * @param[in] ran Random number generator. * @return Energy. * * @exception GException::erange_invalid * Energy range is invalid (emin < emax required). * * Simulates a random energy in the interval [emin, emax] for a spectral * function. ***************************************************************************/ GEnergy GModelSpectralFunc::mc(const GEnergy& emin, const GEnergy& emax, GRan& ran) const { // Throw an exception if energy range is invalid if (emin >= emax) { throw GException::erange_invalid(G_MC, emin.MeV(), emax.MeV(), "Minimum energy < maximum energy required."); } // Allocate energy GEnergy energy; // Continue only if emax > emin if (emax > emin) { // Update cache mc_update(emin, emax); // Determine in which bin we reside int inx = 0; if (m_mc_cum.size() > 1) { double u = ran.uniform(); for (inx = m_mc_cum.size()-1; inx > 0; --inx) { if (m_mc_cum[inx-1] <= u) { break; } } } // Get random energy for specific bin if (m_mc_exp[inx] != 0.0) { double e_min = m_mc_min[inx]; double e_max = m_mc_max[inx]; double u = ran.uniform(); double eng = (u > 0.0) ? std::exp(std::log(u * (e_max - e_min) + e_min) / m_mc_exp[inx]) : 0.0; energy.MeV(eng); } else { double e_min = m_mc_min[inx]; double e_max = m_mc_max[inx]; double u = ran.uniform(); double eng = std::exp(u * (e_max - e_min) + e_min); energy.MeV(eng); } } // endif: emax > emin // Return energy return energy; }
/***********************************************************************//** * @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. ***************************************************************************/ void GModelSpectralExpPlaw::update_mc_cache(const GEnergy& emin, const GEnergy& emax) const { // Case A: Index is not -1 if (index() != -1.0) { // Change in energy boundaries? if (emin.MeV() != m_mc_emin || emax.MeV() != m_mc_emax) { m_mc_emin = emin.MeV(); m_mc_emax = emax.MeV(); m_mc_exponent = index() + 1.0; m_mc_pow_emin = std::pow(m_mc_emin, m_mc_exponent); m_mc_pow_ewidth = std::pow(m_mc_emax, m_mc_exponent) - m_mc_pow_emin; } } // Case B: Index is -1 else { // Change in energy boundaries? if (emin.MeV() != m_mc_emin || emax.MeV() != m_mc_emax) { m_mc_emin = emin.MeV(); m_mc_emax = emax.MeV(); m_mc_exponent = 0.0; m_mc_pow_emin = std::log(m_mc_emin); m_mc_pow_ewidth = std::log(m_mc_emax) - m_mc_pow_emin; } } // Return return; }
/***********************************************************************//** * @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). * * @exception GException::erange_invalid * Energy range is invalid (emin < emax required). * * Computes * \f[\int_{E_{\rm min}}^{E_{\rm max}} I(E) dE\f] * where * \f$E_{\rm min}\f$ and \f$E_{\rm max}\f$ are the minimum and maximum * energy, respectively, and * \f$I(E)\f$ is the spectral model (units: ph/cm2/s/MeV). * The integration is done analytically. ***************************************************************************/ double GModelSpectralConst::flux(const GEnergy& emin, const GEnergy& emax) const { // Throw an exception if energy range is invalid if (emin >= emax) { throw GException::erange_invalid(G_FLUX, emin.MeV(), emax.MeV(), "Minimum energy < maximum energy required."); } // Compute flux for a constant model double flux = norm() * (emax.MeV() - emin.MeV()); // Return return flux; }
/***********************************************************************//** * @brief Evaluate model value * * @param[in] srcEng True photon energy. * @param[in] srcTime True photon arrival time. * @return Model value (ph/cm2/s/MeV). * * Evaluates * * \f[ * S_{\rm E}(E | t) = \frac{m\_norm}{\sqrt{2\pi}m\_sigma} * \exp(\frac{-(E-m\_mean)^2}{2 m\_sigma^2}) * \f] * * where * - \f${\tt m\_norm}\f$ is the normalization, * - \f${\tt m\_mean}\f$ is the mean energy, and * - \f${\tt m\_sigma}\f$ is the energy width. ***************************************************************************/ double GModelSpectralGauss::eval(const GEnergy& srcEng, const GTime& srcTime) const { // Get parameter values double energy = srcEng.MeV(); double norm = m_norm.value(); double mean = m_mean.value(); double sigma = m_sigma.value(); // Compute function value double delta = energy - mean; double term1 = (norm / sigma) * gammalib::inv_sqrt2pi; double term2 = delta * delta / (2.0 * sigma * sigma); double value = term1 * std::exp(-term2); // Compile option: Check for NaN/Inf #if defined(G_NAN_CHECK) if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { std::cout << "*** ERROR: GModelSpectralGauss::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 Return exponential cut-off energy * * @return Exponential cut-off energy. * * Returns the exponential cut-off energy. ***************************************************************************/ inline GEnergy GModelSpectralSuperExpPlaw::cutoff(void) const { GEnergy energy; energy.MeV(m_ecut.value()); return energy; }
/***********************************************************************//** * @brief Return maximum energy * * @return Maximum energy. * * Returns the maximum energy. ***************************************************************************/ inline GEnergy GModelSpectralPlaw2::emax(void) const { GEnergy energy; energy.MeV(m_emax.value()); return energy; }
/***********************************************************************//** * @brief Return breakenergy energy * * @return breakenergy energy. * * Returns the breakenergy energy. ***************************************************************************/ inline GEnergy GModelSpectralBrokenPlaw::breakenergy(void) const { GEnergy energy; energy.MeV(m_breakenergy.value()); return energy; }
/***********************************************************************//** * @brief Integration kernel for edisp_kern() method * * @param[in] x Function value. * * This method implements the integration kernel needed for the edisp_kern() * method. ***************************************************************************/ double GResponse::edisp_kern::eval(const double& x) { // Set energy GEnergy eng; double expx = std::exp(x); eng.MeV(expx); // Get function value double value = m_parent->eval_prob(*m_model, *m_event, eng, m_srcTime, *m_obs, m_grad); // Save value if needed #if defined(G_NAN_CHECK) double value_out = value; #endif // Correct for variable substitution value *= expx; // Compile option: Check for NaN #if defined(G_NAN_CHECK) if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { std::cout << "*** ERROR: GResponse::edisp_kern::eval"; std::cout << "(x=" << x << "): "; std::cout << " NaN/Inf encountered"; std::cout << " (value=" << value_out; std::cout << " exp(x)=" << expx; std::cout << ")" << std::endl; } #endif // Return value return value; }
/***********************************************************************//** * @brief Return pivot energy * * @return Pivot energy. * * Returns the pivot energy. ***************************************************************************/ inline GEnergy GModelSpectralPlaw::pivot(void) const { GEnergy energy; energy.MeV(m_pivot.value()); return energy; }
/***********************************************************************//** * @brief Update eval precomputation cache * * @param[in] energy Energy. * * Updates the precomputation cache for eval() and eval_gradients() methods. ***************************************************************************/ void GModelSpectralExpPlaw::update_eval_cache(const GEnergy& energy) const { // Get parameter values (takes 3 multiplications which are difficult // to avoid) double index = m_index.value(); double ecut = m_ecut.value(); double pivot = m_pivot.value(); // If the energy or one of the parameters index, cut-off or pivot // energy has changed then recompute the cache if ((m_last_energy != energy) || (m_last_index != index) || (m_last_ecut != ecut) || (m_last_pivot != pivot)) { // Store actual energy and parameter values m_last_energy = energy; m_last_index = index; m_last_ecut = ecut; m_last_pivot = pivot; // Compute and store value double eng = energy.MeV(); m_last_e_norm = eng / m_last_pivot; m_last_e_cut = eng / m_last_ecut; m_last_power = std::pow(m_last_e_norm, m_last_index) * std::exp(-m_last_e_cut); } // endif: recomputation was required // Return return; }
/***********************************************************************//** * @brief Returns model energy flux between [emin, emax] (units: ph/cm2/s) * * @param[in] emin Minimum photon energy. * @param[in] emax Minimum photon energy. * @return Photon flux (ph/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). ***************************************************************************/ double GModelSpectralPlaw2::eflux(const GEnergy& emin, const GEnergy& emax) const { // Initialise flux double eflux = 0.0; // Compute only if integration range is valid if (emin < emax) { // Compute power law normalization double norm; if (index() != -1.0) { double gamma = m_index.value() + 1.0; double pow_ref_emin = std::pow(this->emin().MeV(), gamma); double pow_ref_emax = std::pow(this->emax().MeV(), gamma); norm = m_integral.value() * gamma / (pow_ref_emax - pow_ref_emin); } else { double log_ref_emin = std::log(this->emin().MeV()); double log_ref_emax = std::log(this->emax().MeV()); norm = m_integral.value() / (log_ref_emax - log_ref_emin); } // Compute energy flux if (index() != -2.0) { double gamma = m_index.value() + 2.0; double pow_emin = std::pow(emin.MeV(), gamma); double pow_emax = std::pow(emax.MeV(), gamma); eflux = norm / gamma * (pow_emax - pow_emin); } // Case B: Index is -2 else { double log_emin = std::log(emin.MeV()); double log_emax = std::log(emax.MeV()); eflux = norm * (log_emax - log_emin); } // Convert from MeV/cm2/s to erg/cm2/s eflux *= gammalib::MeV2erg; } // endif: integration range was valid // Return flux return eflux; }
/***********************************************************************//** * @brief Constructor * * @param[in] integral Integral flux (ph/cm2/s). * @param[in] index Power law index. * @param[in] emin Minimum energy. * @param[in] emax Maximum energy. * * Construct a spectral power law from the * - integral flux (in ph/cm2/s), * - spectral index, * - minimum energy and * - maximum energy. ***************************************************************************/ GModelSpectralPlaw2::GModelSpectralPlaw2(const double& integral, const double& index, const GEnergy& emin, const GEnergy& emax) : GModelSpectral() { // Initialise members init_members(); // Set parameters m_integral.value(integral); m_index.value(index); m_emin.value(emin.MeV()); m_emax.value(emax.MeV()); // Return return; }
/***********************************************************************//** * @brief Constructor * * @param[in] norm Total flux under Gaussian (in ph/cm2/s). * @param[in] mean Mean energy. * @param[in] sigma Energy width. ***************************************************************************/ GModelSpectralGauss::GModelSpectralGauss(const double& norm, const GEnergy& mean, const GEnergy& sigma) : GModelSpectral() { // Initialise members init_members(); // Set parameters m_norm.value(norm); m_mean.value(mean.MeV()); m_sigma.value(sigma.MeV()); // Autoscale parameters autoscale(); // Return return; }
/***********************************************************************//** * @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 power law. ***************************************************************************/ GEnergy GModelSpectralPlaw::mc(const GEnergy& emin, const GEnergy& emax, const GTime& time, GRan& ran) const { // Throw an exception if energy range is invalid if (emin >= emax) { throw GException::erange_invalid(G_MC, emin.MeV(), emax.MeV(), "Minimum energy < maximum energy required."); } // Update cache update_mc_cache(emin, emax); // Get uniform random number double u = ran.uniform(); // Initialise energy double eng; // Case A: Index is not -1 if (index() != -1.0) { if (u > 0.0) { eng = std::exp(std::log(u * m_mc_pow_ewidth + m_mc_pow_emin) / m_mc_exponent); } else { eng = 0.0; } } // Case B: Index is -1 else { eng = std::exp(u * m_mc_pow_ewidth + m_mc_pow_emin); } // Set energy GEnergy energy; energy.MeV(eng); // Return energy return energy; }
/***********************************************************************//** * @brief Constructor * * @param[in] prefactor Pre factor normalization (ph/cm2/s/MeV). * @param[in] index Power law index. * @param[in] pivot Pivot energy. * @param[in] cutoff Cut off energy. * * Construct an exponentially cut off power law from * - a prefactor value (in units of ph/cm2/s/MeV) * - a spectral index, * - a pivot energy, and * - a cut off energy. ***************************************************************************/ GModelSpectralExpPlaw::GModelSpectralExpPlaw(const double& prefactor, const double& index, const GEnergy& pivot, const GEnergy& cutoff) : GModelSpectral() { // Initialise members init_members(); // Set parameters m_norm.value(prefactor); m_index.value(index); m_pivot.value(pivot.MeV()); // Internally stored in MeV m_ecut.value(cutoff.MeV()); // Internally stored in MeV // Autoscale parameters autoscale(); // Return return; }
/***********************************************************************//** * @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 analytically. ***************************************************************************/ double GModelSpectralPlaw::flux(const GEnergy& emin, const GEnergy& emax) const { // Initialise flux double flux = 0.0; // Compute only if integration range is valid if (emin < emax) { // Compute photon flux flux = m_norm.value() * gammalib::plaw_photon_flux(emin.MeV(), emax.MeV(), m_pivot.value(), m_index.value()); } // endif: integration range was valid // Return flux return flux; }
/***********************************************************************//** * @brief Evaluate model value and gradient * * @param[in] srcEng True photon energy. * @param[in] srcTime True photon arrival time. * @return Model value (ph/cm2/s/MeV). * * Evaluates * * \f[ * S_{\rm E}(E | t) = \frac{m\_norm}{\sqrt{2\pi}m\_sigma} * \exp(\frac{-(E-m\_mean)^2}{2 m\_sigma^2}) * \f] * * where * - \f${\tt m\_norm}\f$ is the normalization, * - \f${\tt m\_mean}\f$ is the mean energy, and * - \f${\tt m\_sigma}\f$ is the energy width. * * The method also evaluates the partial derivatives of the model with * respect to the parameters using * * \f[ * \frac{\delta S_{\rm E}(E | t)}{\delta {\tt m\_norm}} = * \frac{S_{\rm E}(E | t)}{{\tt m\_norm}} * \f] * * \f[ * \frac{\delta S_{\rm E}(E | t)}{\delta {\tt m\_mean}} = * S_{\rm E}(E | t) \frac{E-m\_mean}{m\_sigma^2} * \f] * * \f[ * \frac{\delta S_{\rm E}(E | t)}{\delta {\tt m\_sigma}} = * \frac{S_{\rm E}(E | t)}{{\tt m\_sigma}} * \left( \frac{(E-m\_mean)^2}{m\_sigma^2} - 1 \right) * \f] * ***************************************************************************/ double GModelSpectralGauss::eval_gradients(const GEnergy& srcEng, const GTime& srcTime) { // Get parameter values double energy = srcEng.MeV(); double norm = m_norm.value(); double mean = m_mean.value(); double sigma = m_sigma.value(); // Compute function terms double delta = energy - mean; double sigma2 = sigma * sigma; double term2 = (1.0 / sigma) * gammalib::inv_sqrt2pi; double term1 = norm * term2; double term3 = delta * delta / (2.0 * sigma2); double term4 = delta / sigma2; double term5 = (norm / sigma2) * gammalib::inv_sqrt2pi; double eterm3 = std::exp(-term3); // Compute function value double value = term1 * eterm3; // Compute partial derivatives with respect to the parameter factor // values (partial differentials were determined analytically). double g_norm = (m_norm.is_free()) ? term2 * eterm3 * m_norm.scale() : 0.0; double g_mean = (m_mean.is_free()) ? value * term4 * m_mean.scale() : 0.0; double g_sigma = (m_sigma.is_free()) ? -term5 * eterm3 * (1.0 - (2.0 * term3)) * m_sigma.scale() : 0.0; // Set gradients m_norm.factor_gradient(g_norm); m_mean.factor_gradient(g_mean); m_sigma.factor_gradient(g_sigma); // Compile option: Check for NaN/Inf #if defined(G_NAN_CHECK) if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { std::cout << "*** ERROR: GModelSpectralGauss::eval_gradients"; 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 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 Returns model photon flux between [emin, emax] (units: ph/cm2/s) * * @param[in] emin Minimum photon energy. * @param[in] emax Minimum 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). ***************************************************************************/ double GModelSpectralPlaw2::flux(const GEnergy& emin, const GEnergy& emax) const { // Initialise flux double flux = 0.0; // Compute only if integration range is valid if (emin < emax) { // Case A: Index is not -1 if (index() != -1.0) { double gamma = m_index.value() + 1.0; double pow_emin = std::pow(emin.MeV(), gamma); double pow_emax = std::pow(emax.MeV(), gamma); double pow_ref_emin = std::pow(this->emin().MeV(), gamma); double pow_ref_emax = std::pow(this->emax().MeV(), gamma); double factor = (pow_emax - pow_emin) / (pow_ref_emax - pow_ref_emin); flux = m_integral.value() * factor; } // Case B: Index is -1 else { double log_emin = std::log(emin.MeV()); double log_emax = std::log(emax.MeV()); double log_ref_emin = std::log(this->emin().MeV()); double log_ref_emax = std::log(this->emax().MeV()); double factor = (log_emax - log_emin) / (log_ref_emax - log_ref_emin); flux = m_integral.value() * factor; } } // 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 analytically. ***************************************************************************/ double GModelSpectralPlaw::eflux(const GEnergy& emin, const GEnergy& emax) const { // Initialise flux double eflux = 0.0; // Compute only if integration range is valid if (emin < emax) { // Compute photon flux eflux = m_norm.value() * gammalib::plaw_energy_flux(emin.MeV(), emax.MeV(), m_pivot.value(), m_index.value()); // Convert from MeV/cm2/s to erg/cm2/s eflux *= gammalib::MeV2erg; } // endif: integration range was valid // Return flux return eflux; }
/***********************************************************************//** * @brief Returns logarithmic mean energy for a given energy interval * * @param[in] index Energy interval index (0,...,size()-1). * @return Logarithmic mean energy of interval. * * @exception GException::out_of_range * Specified index is out of range. * * Computes the logarithmic mean energy * \f$10^{0.5 * (\log E_{\rm min} + \log E_{\rm max})}\f$ * for the energy interval @p index. ***************************************************************************/ GEnergy GEbounds::elogmean(const int& index) const { #if defined(G_RANGE_CHECK) // If index is outside boundary then throw an error if (index < 0 || index >= m_num) { throw GException::out_of_range(G_ELOGMEAN, index, 0, m_num-1); } #endif // Compute logarithmic mean energy GEnergy elogmean; elogmean.MeV(std::sqrt(m_min[index].MeV() * m_max[index].MeV())); // Return return elogmean; }
/***********************************************************************//** * @brief Parameter constructor * * @param[in] prefactor Power law pre factor (ph/cm2/s/MeV). * @param[in] index Power law index. * @param[in] pivot Pivot energy. * * Constructs a spectral power law using the model parameters * - power law @p prefactor (ph/cm2/s/MeV) * - spectral @p index * - @p pivot energy. ***************************************************************************/ GModelSpectralPlaw::GModelSpectralPlaw(const double& prefactor, const double& index, const GEnergy& pivot) : GModelSpectral() { // Initialise members init_members(); // Set parameters m_norm.value(prefactor); m_index.value(index); m_pivot.value(pivot.MeV()); // Internally stored in MeV // Perform autoscaling of parameter autoscale(); // 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; }