Exemple #1
0
/***********************************************************************//**
 * @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 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;
}
Exemple #4
0
/***********************************************************************//**
 * @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 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 Set energy boundaries
 *
 * Computes energy boundaries from the energy nodes. The boundaries will be
 * computed as the centre in log10(energy) between the nodes. If only a
 * single map exists, no boundaries will be computed.
 ***************************************************************************/
void GModelSpatialDiffuseCube::set_energy_boundaries(void)
{
    // Initialise energy boundaries
    m_ebounds.clear();

    // Determine number of energy bins
    int num = m_logE.size();

    // Continue only if there are at least two energy nodes
    if (num > 1) {

        // Loop over all nodes
        for (int i = 0; i < num; ++i) {

            // Calculate minimum energy
            double e_min = (i == 0) ? m_logE[i] - 0.5 * (m_logE[i+1] - m_logE[i])
                                    : m_logE[i] - 0.5 * (m_logE[i] - m_logE[i-1]);

            // Calculate maximum energy
            double e_max = (i < num-1) ? m_logE[i] + 0.5 * (m_logE[i+1] - m_logE[i])
                                       : m_logE[i] + 0.5 * (m_logE[i] - m_logE[i-1]);

            // Set energy boundaries
            GEnergy emin;
            GEnergy emax;
            emin.log10MeV(e_min);
            emax.log10MeV(e_max);

            // Append energy bin to energy boundary arra
            m_ebounds.append(emin, emax);

        } // endfor: looped over energy nodes

    } // endif: there were at least two energy nodes

    // Return
    return;
}
Exemple #7
0
/***********************************************************************//**
 * @brief Returns efficiency factor 2
 *
 * @param[in] srcEng True energy of photon.
 *
 * Returns the efficiency factor 2 as function of the true photon energy.
 *
 * If no efficiency factors are present returns 2.
 *
 * @todo Implement cache to save computation time if called with same energy
 *       value (happens for binned analysis for example)
 ***************************************************************************/
double GLATAeff::efficiency_factor2(const GEnergy& srcEng) const
{
    // Initialise factor
    double factor = 0.0;

    // Compute efficiency factor. Note that the factor 2 uses the functor 1,
    // following the philosophy implemented in the ScienceTools method
    // EfficiencyFactor::getLivetimeFactors
    if (m_eff_func1 != NULL) {
        factor = (*m_eff_func1)(srcEng.log10MeV());
    }

    // Return factor
    return factor;
}
/***********************************************************************//**
 * @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).
 *
 * Computes
 *
 * \f[
 *    S_{\rm E}(E | t) = {\tt m\_integral}
 *    \frac{{\tt m\_index}+1}
 *         {{\tt e\_max}^{{\tt m\_index}+1} -
 *          {\tt e\_min}^{{\tt m\_index}+1}}
 *    E^{\tt m\_index}
 * \f]
 *
 * for \f${\tt m\_index} \ne -1\f$ and
 *
 * \f[
 *    S_{\rm E}(E | t) = 
 *    \frac{{\tt m\_integral}}
 *         {\log {\tt e\_max} - \log {\tt e\_min}}
 *    E^{\tt m\_index}
 * \f]
 *
 * for \f${\tt m\_index} = -1\f$, where
 * - \f${\tt e\_min}\f$ is the minimum energy of an interval,
 * - \f${\tt e\_max}\f$ is the maximum energy of an interval,
 * - \f${\tt m\_integral}\f$ is the integral flux between 
 *   \f${\tt e\_min}\f$ and \f${\tt e\_max}\f$, and
 * - \f${\tt m\_index}\f$ is the spectral index.
 *
 * 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\_integral}} =
 *      \frac{S_{\rm E}(E | t)}{{\tt m\_integral}}
 * \f]
 *
 * \f[
 *    \frac{\delta S_{\rm E}(E | t)}{\delta {\tt m\_index}} =
 *      S_{\rm E}(E | t) \,
 *      \left( \frac{1}{{\tt m\_index}+1} -
 *             \frac{\log({\tt e\_max}) {\tt e\_max}^{{\tt m\_index}+1} -
 *                   \log({\tt e\_min}) {\tt e\_min}^{{\tt m\_index}+1}}
 *                        {{\tt e\_max}^{{\tt m\_index}+1} -
 *                         {\tt e\_min}^{{\tt m\_index}+1}}
 *                 + \ln(E) \right)
 * \f]
 *
 * for \f${\tt m\_index} \ne -1\f$ and
 *
 * \f[
 *    \frac{\delta S_{\rm E}(E | t)}{\delta {\tt m\_index}} =
 *      S_{\rm E}(E | t) \, \ln(E)
 * \f]
 *
 * for \f${\tt m\_index} = -1\f$.
 *
 * No partial derivatives are supported for the energy boundaries.
 ***************************************************************************/
double GModelSpectralPlaw2::eval_gradients(const GEnergy& srcEng,
                                           const GTime&   srcTime)
{
    // Initialise gradients
    double g_integral = 0.0;
    double g_index    = 0.0;
    
    // Update precomputed values
    update(srcEng);

    // Compute function value
    double value = m_integral.value() * m_norm * m_power;

    // Integral flux gradient
    if (m_integral.is_free()) {
         g_integral = value / m_integral.factor_value();
    }

    // Index gradient
    if (m_index.is_free()) {
        g_index = value * (m_g_norm + gammalib::ln10 * srcEng.log10MeV()) *
                  m_index.scale();
    }

    // Set gradients
    m_integral.factor_gradient(g_integral);
    m_index.factor_gradient(g_index);

    // Compile option: Check for NaN/Inf
    #if defined(G_NAN_CHECK)
    if (gammalib::is_notanumber(value) || gammalib::is_infinite(value)) {
        std::cout << "*** ERROR: GModelSpectralPlaw2::eval_gradients";
        std::cout << "(srcEng=" << srcEng;
        std::cout << ", srcTime=" << srcTime << "):";
        std::cout << " NaN/Inf encountered";
        std::cout << " (value=" << value;
        std::cout << ", integral=" << integral();
        std::cout << ", m_norm=" << m_norm;
        std::cout << ", m_power=" << m_power;
        std::cout << ", g_integral=" << g_integral;
        std::cout << ", g_index=" << g_index;
        std::cout << ")" << std::endl;
    }
    #endif

    // Return
    return value;
}
/***********************************************************************//**
 * @brief Set Monte Carlo simulation cone
 *
 * @param[in] centre Simulation cone centre.
 * @param[in] radius Simulation cone radius (degrees).
 *
 * Sets the simulation cone centre and radius that defines the directions
 * that will be simulated using the mc() method.
 ***************************************************************************/
void GModelSpatialDiffuseCube::set_mc_cone(const GSkyDir& centre,
                                           const double&  radius)
{
    // Initialise cache
    m_mc_cache.clear();
    m_mc_spectrum.clear();

    // Fetch cube
    fetch_cube();

    // Determine number of cube pixels and maps
    int npix  = pixels();
    int nmaps = maps();

    // Continue only if there are pixels and maps
    if (npix > 0 && nmaps > 0) {

        // Reserve space for all pixels in cache
        m_mc_cache.reserve((npix+1)*nmaps);

        // Loop over all maps
        for (int i = 0; i < nmaps; ++i) {

            // Compute pixel offset
            int offset = i * (npix+1);

            // Set first cache value to 0
            m_mc_cache.push_back(0.0);

            // Initialise cache with cumulative pixel fluxes and compute
            // total flux in skymap for normalization. Negative pixels are
            // excluded from the cumulative map.
            double total_flux = 0.0;
        	for (int k = 0; k < npix; ++k) {

                // Derive effective pixel radius from half opening angle
                // that corresponds to the pixel's solid angle. For security,
                // the radius is enhanced by 50%.
                double pixel_radius =
                       std::acos(1.0 - m_cube.solidangle(k)/gammalib::twopi) *
                       gammalib::rad2deg * 1.5;

                // Add up flux with simulation cone radius + effective pixel
                // radius. The effective pixel radius is added to make sure
                // that all pixels that overlap with the simulation cone are
                // taken into account. There is no problem of having even
                // pixels outside the simulation cone taken into account as
                // long as the mc() method has an explicit test of whether a
                // simulated event is contained in the simulation cone.
                double distance = centre.dist_deg(m_cube.pix2dir(k));
                if (distance <= radius+pixel_radius) {
                    double flux = m_cube(k,i) * m_cube.solidangle(k);
                    if (flux > 0.0) {
                        total_flux += flux;
                    }
                }

                // Push back flux
        		m_mc_cache.push_back(total_flux); // units: ph/cm2/s/MeV
        	}

            // Normalize cumulative pixel fluxes so that the values in the
            // cache run from 0 to 1
            if (total_flux > 0.0) {
        		for (int k = 0; k < npix; ++k) {
        			m_mc_cache[k+offset] /= total_flux;
        		}
        	}

            // Make sure that last pixel in the cache is >1
            m_mc_cache[npix+offset] = 1.0001;

            // Store centre flux in node array
            if (m_logE.size() == nmaps) {
                GEnergy energy;
                energy.log10MeV(m_logE[i]);
                
                // Only append node if flux > 0
                if (total_flux > 0.0) {
                	m_mc_spectrum.append(energy, total_flux);
                }

            }

        } // endfor: looped over all maps

        // Dump cache values for debugging
        #if defined(G_DEBUG_CACHE)
        for (int i = 0; i < m_mc_cache.size(); ++i) {
            std::cout << "i=" << i;
            std::cout << " c=" << m_mc_cache[i] << std::endl;
        }
        #endif

    } // endif: there were cube pixels and maps

    // Return
    return;
}