Updated about 10 years ago by Knödlseder Jürgen

Fermi/LAT interface

Pass 7 IRF

Pass 7 IRF have been implement in GammaLib version 00-06-00. Please check Validation of Pass 7 IRFs for more information.

Livetime cubes

The Fermi/LAT Science Tools implement the livetime cube information as map_tools::Exposure objects. map_tools::Exposure derives from SkyExposure which is defined as

typedef BasicExposure<SkyBinner, healpix::CosineBinner> SkyExposure;

The BasicExposure class is a template class which has the following access operator that is used to retrieve exposure values:
    template<class F>
        double operator()(const astro::SkyDir& dir, const F& fun)const
    {
        const C& binner = m_sky[dir];
        return binner(fun);
    }

Here C is the healpix::CosineBinner class. The class F is typically the effective area class. The binner(fun) operator is implemented in healpix/healpix/CosineBinner by
    template<class F>
    double operator()(const F& f)const
    {   
        double sum=0;
        int n(0);
        for(const_iterator it=begin(); it!=end_costh(); ++it, ++n){
            sum += (*it)*f(costheta(it));
        }
        return sum; 

    }

The energy does not appear explicitly in these code fragments as it is stored as a member of the effective area helper class:
   class Aeff : public AeffBase {
   public:
      Aeff(double energy, int evtType, const Observation & observation); 
      virtual ~Aeff() {}
   protected:
      double m_energy;
      int m_evtType;
      const Observation & m_observation;
      virtual double value(double cosTheta, double phi=0) const;
   };

Exposure computation using efficiency factors

The Fermi/LAT Science Tools compute the exposure with trigger rate- and energy-dependent efficiency corrections in Likelihood/Likelihood/ExposureCube.h where the following template class is implemented:

   template<class T>
   double value(const astro::SkyDir & dir, const T & aeff, double energy) const {
      double factor1(1), factor2(0);
      if (m_efficiencyFactor) {
         double met((m_tstart + m_tstop)/2.);
         m_efficiencyFactor->getLivetimeFactors(energy, factor1, factor2, met);
      }
      double value1(value(dir, aeff));
      double value2(0);
      double exposure(factor1*value1);
      if (factor2 != 0) {
         value2 = value(dir, aeff, true);
         exposure += factor2*value2;
      }
      if (exposure < 0) {
         throw std::runtime_error("ExposureCube::value: exposure < 0");
      }
      return exposure;
   }

An approximation is made in the computation of the efficiency factors which are the same for the front and the back section (see irfs/latResponse/src/EfficiencyFactor.cxx):
void EfficiencyFactor::getLivetimeFactors(double energy, double & factor1, double & factor2, double met) const {
   (void)(met);
   if (!m_havePars) {
      factor1 = 1;
      factor2 = 0;
      return;
   }
   double logE(std::log10(energy));

   // Since we do not have access to the front and back effective
   // areas separately, just return the averages.  We would really
   // want to return the averages weighted by effective area (as a function
   // of energy and theta).
   factor1 = (m_p1_front(logE) + m_p1_back(logE))/2.;
   factor2 = (m_p0_front(logE) + m_p0_back(logE))/2.;
}

In GammaLib we do not make this approximation as the response is fully separated in front and back response functions. The efficiency factors are returned for front and back separately:

double GLATLtCube::operator()(const GSkyDir& dir, const GEnergy& energy, const GLATAeff& aeff) const
{
    // Compute exposure
    double exposure = m_exposure(dir, energy, aeff);

    // Optionally compute livetime factors for trigger rate- and
    // energy-dependent efficiency corrections
    if (aeff.has_efficiency()) {

        // Compute correction factors
        double f1 = aeff.efficiency_factor1(energy);
        double f2 = aeff.efficiency_factor2(energy);

        // Compute correction
        double correction = m_weighted_exposure(dir, energy, aeff);

        // Set exposure
        exposure = f1 * exposure + f2 * correction;

    } // endif: corrections requested

    // Return exposure
    return exposure;
}

Below an example for the efficiency factors for P7REP_SOURCE_V15 at 200 MeV:

Parameter Science Tools GammaLib
factor1::front -1.06527 -1.05736
factor2::front 2.28282 2.27408
factor1::back -1.06527 -1.07317
factor2::back 2.28282 2.29157

Effective area

The effective area is accessed in the Fermi/LAT Science Tools using the ExposureCube::Aeff::value method (recall that the energy is stored in the member m_energy):

double ExposureCube::Aeff::value(double cosTheta, double phi) const {
   double inclination = acos(cosTheta)*180./M_PI;
   double epoch((m_observation.expCube().tstart() + m_observation.expCube().tstop())/2.);
   std::map<unsigned int, irfInterface::Irfs *>::const_iterator respIt = m_observation.respFuncs().begin();
   for ( ; respIt != m_observation.respFuncs().end(); ++respIt) {
      if (respIt->second->irfID() == m_evtType) {
         irfInterface::IAeff * aeff = respIt->second->aeff();
         double aeff_val = aeff->value(m_energy, inclination, phi, epoch);
         return aeff_val;
      }
   }
   return 0;
}
The inclination is given in degrees.

The effective area is implemented by the irfs/latResponse/Aeff.cxx class. The value method is used to access the effective area values:

double Aeff::value(double energy, double theta, double phi, double time) const {
   (void)(time);
   double costheta(std::cos(theta*M_PI/180.));
   if (costheta < m_aeffTable.minCosTheta()) {
      return 0;
   }
   if (costheta > 0.99999) {
      costheta = 0.99999;  // blech.
   }
   bool interpolate;
   double logE(std::log10(energy));
   double aeff_value(m_aeffTable.value(logE, costheta, interpolate=true)*1e4);
   double phi_mod(phi_modulation(logE, costheta, phi, interpolate=false));
   return aeff_value*phi_mod;
}
Note the unnecessary conversion of inclination to costheta (why is not simply costheta passed in this interface?). Effective area values are determined by bilinear interpolation in logE and costheta. The code also multiplies-in a Phi modulation:
double Aeff::phi_modulation(double logE, double costheta, double phi, bool interpolate) const {
   double par[2];
   if (!m_phiDepPars || !m_usePhiDependence) {
      return 1.;
   }
   m_phiDepPars->getPars(logE, costheta, par, interpolate);
   return phi_modulation(par[0], par[1], phi);
}
double Aeff::phi_modulation(double par0, double par1, double phi) const {
   if (phi < 0) {
      phi += 360.;
   }
   double norm(1./(1. + par0/(1. + par1)));
   double phi_pv(std::fmod(phi*M_PI/180., M_PI) - M_PI/2.);
   double xx(2.*std::fabs(2./M_PI*std::fabs(phi_pv) - 0.5));
   return norm*(1. + par0*std::pow(xx, par1));
}

The equivalent GammaLib code is

double GLATAeff::operator()(const double& logE, const double& ctheta)
{
    // Get effective area value
    double aeff = (ctheta >= m_min_ctheta) 
                  ? m_aeff_bins.interpolate(logE, ctheta, m_aeff) : 0.0;

    // Make sure that effective area is not negative
    if (aeff < 0.0) {
        aeff = 0.0;
    }

    // Return effective area value
    return aeff;
}
A variant of this operator takes also a phi value, yet the effective area phi dependence is so far not implemented in GammaLib.

Here a number of effective area values (in cm2) determined using the ScienceTools and GammaLib for the P7REP_SOURCE_V15 response for 200 MeV as function of costheta (no phi dependence was used in the Fermi/LAT Science Tools):
logE costheta ScienceTools GammaLib
2.30103 0.999844 2334.4 2426.55
2.30103 0.998594 2334.4 2417.21
2.30103 0.996094 2334.4 2398.55
2.30103 0.992344 2334.4 2370.56
2.30103 0.987344 2333.23 2333.23
2.30103 0.981094 2286.58 2286.58
2.30103 0.418594 119.825 119.825
2.30103 0.379844 60.7843 60.7843
2.30103 0.339844 26.3115 26.3115
2.30103 0.298594 8.99221 8.99221
2.30103 0.256094 2.41828 2.41828
2.30103 0.212344 0.571005 0.566958

For costheta > 0.992344 (about 7°) the effective area in the Fermi/LAT Science Tools saturates while for GammaLib it still increases (maybe the ScienceTools bi-linear interpolator does not extrapolate?). The Fermi/LAT Science Tools determine the minimum costheta angle from the lower boundary of the front response (note that formerly this was fixed to 70°, but I now dropped this since Fermi/LAT Science Tools do no limit the response).

Mean PSF

The Fermi/LAT Science Tools and GammaLib use a mean PSF for point sources in binned analysis. The mean PSF is implemented by Likelihood/src/MeanPsf.cxx:

void MeanPsf::init() {
   computeExposure();
   if (s_separations.size() == 0) {
      createLogArray(1e-4, 70., 200, s_separations);
   }
   m_psfValues.reserve(m_energies.size()*s_separations.size());
   for (unsigned int k = 0; k < m_energies.size(); k++) {
      for (unsigned int j = 0; j < s_separations.size(); j++) {
         double value(0);
         std::map<unsigned int, irfInterface::Irfs *>::const_iterator 
            resp = m_observation.respFuncs().begin();
         for (; resp != m_observation.respFuncs().end(); ++resp) {
            int evtType = resp->second->irfID();
            Psf psf(s_separations[j], m_energies[k], evtType, m_observation);
            value += m_observation.expCube().value(m_srcDir, psf,
                                                   m_energies[k]);
         }
         if (m_exposure[k] > 0) {
            value /= m_exposure[k];
         } else {
            value = 0;
         }
         m_psfValues.push_back(value);
       }
   }
}
void MeanPsf::computeExposure() {
   m_exposure.reserve(m_energies.size());
   for (size_t k(0); k < m_energies.size(); k++) {
      double value(0);
      std::map<unsigned int, irfInterface::Irfs *>::const_iterator
         resp = m_observation.respFuncs().begin();
      for (; resp != m_observation.respFuncs().end(); ++resp) {
         int evtType = resp->second->irfID();
         ExposureCube::Aeff aeff(m_energies[k], evtType, m_observation);
         value += m_observation.expCube().value(m_srcDir, aeff, m_energies[k]);
      }
      m_exposure.push_back(value);
   }
}