/***********************************************************************//** * @brief Sum effective area multiplied by livetime over zenith and * (optionally) azimuth angles * * @param[in] dir True sky direction. * @param[in] energy True photon energy. * @param[in] aeff Effective area. * * Computes * \f[\sum_{\cos \theta, \phi} T_{\rm live}(\cos \theta, \phi) * A_{\rm eff}(\log E, \cos \theta, \phi)\f] * where * \f$T_{\rm live}(\cos \theta, \phi)\f$ is the livetime as a function of * the cosine of the zenith and the azimuth angle, and * \f$A_{\rm eff}(\log E, \cos \theta, \phi)\f$ is the effective area that * depends on * the log10 of the energy (in MeV), * the cosine of the zenith angle, and * the azimuth angle. * This method assumes that \f$T_{\rm live}(\cos \theta, \phi)\f$ is * stored as a set of maps in a 2D array with \f$\cos \theta\f$ being the * most rapidely varying parameter and with the first map starting at * index m_num_ctheta (the first m_num_ctheta maps are the livetime cube * maps without any \f$\phi\f$ dependence). ***************************************************************************/ double GLATLtCubeMap::operator()(const GSkyDir& dir, const GEnergy& energy, const GLATAeff& aeff) const { // Get map index int pixel = m_map.dir2pix(dir); // Initialise sum double sum = 0.0; // Circumvent const correctness GLATAeff* fct = ((GLATAeff*)&aeff); // If livetime cube and response have phi dependence then sum over // zenith and azimuth. Note that the map index starts with m_num_ctheta // as the first m_num_ctheta maps correspond to an evaluation without // any phi-dependence. if (has_phi() && aeff.has_phi()) { for (int iphi = 0, i = m_num_ctheta; iphi < m_num_phi; ++iphi) { double p = phi(iphi); for (int itheta = 0; itheta < m_num_ctheta; ++itheta, ++i) { sum += m_map(pixel, i) * (*fct)(energy.log10MeV(), costheta(i), p); } } } // ... otherwise sum only over zenith angle else { for (int i = 0; i < m_num_ctheta; ++i) { sum += m_map(pixel, i) * (*fct)(energy.log10MeV(), costheta(i)); } } // Return sum return sum; }
/***********************************************************************//** * @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 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 map cube energies * * @return Map cube energies. * * Returns the energies for the map cube in a vector. ***************************************************************************/ GEnergies GModelSpatialDiffuseCube::energies(void) { // Initialise energies container GEnergies energies; // Fetch cube fetch_cube(); // Get number of map energies int num = m_logE.size(); // Continue only if there are maps in the cube if (num > 0) { // Reserve space for all energies energies.reserve(num); // Set log10(energy) nodes, where energy is in units of MeV for (int i = 0; i < num; ++i) { GEnergy energy; energy.log10MeV(m_logE[i]); energies.append(energy); } } // endif: there were maps in the cube // Return energies return energies; }
/***********************************************************************//** * @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 Test CTA Npred computation * * Tests the Npred computation for the diffuse source model. This is done * by loading the model from the XML file and by calling the * GCTAObservation::npred method which in turn calls the * GCTAResponse::npred_diffuse method. The test takes a few seconds. ***************************************************************************/ void TestGCTAResponse::test_response_npred_diffuse(void) { // Set reference value double ref = 11212.26274; // Set parameters double src_ra = 201.3651; double src_dec = -43.0191; double roi_rad = 4.0; // Setup ROI centred on Cen A with a radius of 4 deg GCTARoi roi; GCTAInstDir instDir; instDir.radec_deg(src_ra, src_dec); roi.centre(instDir); roi.radius(roi_rad); // Setup pointing on Cen A GSkyDir skyDir; skyDir.radec_deg(src_ra, src_dec); GCTAPointing pnt; pnt.dir(skyDir); // Setup dummy event list GGti gti; GEbounds ebounds; GTime tstart(0.0); GTime tstop(1800.0); GEnergy emin; GEnergy emax; emin.TeV(0.1); emax.TeV(100.0); gti.append(tstart, tstop); ebounds.append(emin, emax); GCTAEventList events; events.roi(roi); events.gti(gti); events.ebounds(ebounds); // Setup dummy CTA observation GCTAObservation obs; obs.ontime(1800.0); obs.livetime(1600.0); obs.deadc(1600.0/1800.0); obs.response(cta_irf, cta_caldb); obs.events(&events); obs.pointing(pnt); // Load models for Npred computation GModels models(cta_rsp_xml); // Perform Npred computation double npred = obs.npred(models, NULL); // Test Npred test_value(npred, ref, 1.0e-5, "Diffuse Npred computation"); // Return return; }
/***********************************************************************//** * @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 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 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 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 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 Initialise Monte Carlo cache * * @todo Verify assumption made about the solid angles of the response table * elements. * @todo Add optional sampling on a finer spatial grid. ***************************************************************************/ void GCTABackgroundPerfTable::init_mc_cache(void) const { // Initialise cache m_mc_spectrum.clear(); // Compute solid angle of model double solidangle = this->solidangle(); // Loop over nodes for (int i = 0; i < size(); ++i) { // Set energy GEnergy energy; energy.log10TeV(m_logE[i]); // Compute total rate #if defined(G_LOG_INTERPOLATION) double total_rate = std::pow(10.0, m_background[i]) * solidangle; #else double total_rate = m_background[i] * solidangle; #endif // Set node if (total_rate > 0.0) { m_mc_spectrum.append(energy, total_rate); } } // Return return; }
/***********************************************************************//** * @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 Test CTA psf computation * * The Psf computation is tested by integrating numerically the Psf * function. Integration is done in a rather simplistic way, by stepping * radially away from the centre. The integration is done for a set of * energies from 0.1-10 TeV. ***************************************************************************/ void TestGCTAResponse::test_response_psf(void) { // Load response GCTAResponse rsp; rsp.caldb(cta_caldb); rsp.load(cta_irf); // Integrate Psf GEnergy eng; for (double e = 0.1; e < 10.0; e *= 2.0) { eng.TeV(e); double r = 0.0; double dr = 0.001; int steps = int(1.0/dr); double sum = 0.0; for (int i = 0; i < steps; ++i) { r += dr; sum += rsp.psf(r*deg2rad, 0.0, 0.0, 0.0, 0.0, eng.log10TeV()) * twopi * std::sin(r*deg2rad) * dr*deg2rad; } test_value(sum, 1.0, 0.001, "PSF integration for "+eng.print()); } // 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 Insert energy interval * * @param[in] index Index after with interval is inserted. * @param[in] emin Minimum energy of interval. * @param[in] emax Maximum energy of interval. * * @exception GException::invalid_argument * Minimum energy larger than maximum energy * * Inserts an energy interval after the specified @p index in the energy * boundaries. The method does not reorder the intervals by energy, instead * the client needs to determine the approriate @p index. * * Invalid parameters do not produce any exception, but are handled * transparently. If the interval is invalid (i.e. @p emin > @p emax) an * exception is thrown. If the @p index is out of the valid range, the * index will be adjusted to either the first or the last element. ***************************************************************************/ void GEbounds::insert_eng(const int& index, const GEnergy& emin, const GEnergy& emax) { // Throw an exception if energy interval is invalid if (emin > emax) { std::string msg = "Invalid energy interval specified. Minimum" " energy "+emin.print(NORMAL)+" can not be" " larger than maximum energy "+ emax.print(NORMAL)+"."; throw GException::invalid_argument(G_INSERT_ENG, msg); } // Set index int inx = index; // If inx is out of range then adjust it if (inx < 0) inx = 0; if (inx > m_num) inx = m_num; // Allocate new intervals int num = m_num+1; GEnergy* min = new GEnergy[num]; GEnergy* max = new GEnergy[num]; // Copy intervals before index to be inserted for (int i = 0; i < inx; ++i) { min[i] = m_min[i]; max[i] = m_max[i]; } // Insert interval min[inx] = emin; max[inx] = emax; // Copy intervals after index to be inserted for (int i = inx+1; i < num; ++i) { min[i] = m_min[i-1]; max[i] = m_max[i-1]; } // Free memory if (m_min != NULL) delete [] m_min; if (m_max != NULL) delete [] m_max; // Set new memory m_min = min; m_max = max; // Set number of elements m_num = num; // 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 function and gradients * * @param[in] srcEng True energy of photon. * * The spectral model is defined as * \f[I(E)=norm f(E)\f] * where * \f$norm=n_s n_v\f$ is the normalization of the function. * Note that the normalization is factorised into a scaling factor and a * value and that the method is expected to return the gradient with respect * to the parameter value \f$n_v\f$. * * The partial derivative of the normalization value is given by * \f[dI/dn_v=n_s f(E)\f] ***************************************************************************/ double GModelSpectralFunc::eval_gradients(const GEnergy& srcEng) const { // Interpolate function. This is done in log10-log10 space, but the // linear value is returned. double arg = m_log_nodes.interpolate(srcEng.log10MeV(), m_log_values); double func = std::pow(10.0, arg); // Compute function value double value = norm() * func; // Compute partial derivatives of the parameter values double g_norm = (m_norm.isfree()) ? m_norm.scale() * func : 0.0; // Set gradients (circumvent const correctness) const_cast<GModelSpectralFunc*>(this)->m_norm.gradient(g_norm); // Compile option: Check for NaN/Inf #if defined(G_NAN_CHECK) if (isnotanumber(value) || isinfinite(value)) { std::cout << "*** ERROR: GModelSpectralFunc::eval_gradients"; std::cout << "(srcEng=" << srcEng << "):"; std::cout << " NaN/Inf encountered"; std::cout << " (value=" << value; std::cout << ", norm=" << norm(); std::cout << ", func=" << func; std::cout << ", g_norm=" << g_norm; std::cout << ")" << std::endl; } #endif // Return return value; }
/***********************************************************************//** * @brief Evaluate function * * @param[in] srcEng True energy of photon. * * The spectral model is defined as * \f[I(E)=norm f(E)\f] * where * \f$norm\f$ is the normalization of the function. * Note that the node energies are stored as log10 of energy in units of * MeV. ***************************************************************************/ double GModelSpectralFunc::eval(const GEnergy& srcEng) const { // Interpolate function. This is done in log10-log10 space, but the // linear value is returned. double arg = m_log_nodes.interpolate(srcEng.log10MeV(), m_log_values); double func = std::pow(10.0, arg); // Compute function value double value = norm() * func; // Compile option: Check for NaN/Inf #if defined(G_NAN_CHECK) if (isnotanumber(value) || isinfinite(value)) { std::cout << "*** ERROR: GModelSpectralFunc::eval"; std::cout << "(srcEng=" << srcEng << "):"; std::cout << " NaN/Inf encountered"; std::cout << " (value=" << value; std::cout << ", norm=" << norm(); std::cout << ", func=" << func; std::cout << ")" << std::endl; } #endif // Return return value; }
/***********************************************************************//** * @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 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 Evaluate function and gradients * * @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) = {\tt m\_norm} * \f] * * where * - \f${\tt m\_norm}\f$ is the normalization factor. * * The method also evaluates the partial derivatives of the model with * respect to the normalization parameter using * * \f[ * \frac{\delta S_{\rm E}(E | t)}{\delta {\tt m\_norm}} = * \frac{S_{\rm E}(E | t)}{{\tt m\_norm}} * \f] ***************************************************************************/ double GModelSpectralFunc::eval_gradients(const GEnergy& srcEng, const GTime& srcTime) { // Interpolate function. This is done in log10-log10 space, but the // linear value is returned. double arg = m_log_nodes.interpolate(srcEng.log10MeV(), m_log_values); double func = std::pow(10.0, arg); // Compute function value double value = m_norm.value() * func; // Compute partial derivatives of the parameter values double g_norm = (m_norm.is_free()) ? m_norm.scale() * func : 0.0; // Set gradients m_norm.factor_gradient(g_norm); // Compile option: Check for NaN/Inf #if defined(G_NAN_CHECK) if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) { std::cout << "*** ERROR: GModelSpectralFunc::eval_gradients"; std::cout << "(srcEng=" << srcEng; std::cout << ", srcTime=" << srcTime << "):"; std::cout << " NaN/Inf encountered"; std::cout << " (value=" << value; std::cout << ", norm=" << norm(); std::cout << ", func=" << func; std::cout << ", g_norm=" << g_norm; std::cout << ")" << std::endl; } #endif // Return return value; }
/***********************************************************************//** * @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 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; }