/***********************************************************************//** * @brief Return point spread function (in units of sr^-1) * * @param[in] delta Angular separation between true and measured photon * directions (rad). * @param[in] logE Log10 of the true photon energy (TeV). * @param[in] theta Offset angle in camera system (rad). * @param[in] phi Azimuth angle in camera system (rad). Not used. * @param[in] zenith Zenith angle in Earth system (rad). Not used. * @param[in] azimuth Azimuth angle in Earth system (rad). Not used. * @param[in] etrue Use true energy (true/false). Not used. * * Returns the point spread function for a given angular separation in units * of sr^-1 for a given energy and offset angle. The PSF value will be * determined by trilinear interpolation (and extrapolation) in the PSF * histograms. If the interpolation or extrapolation would lead to negative * PSF values, the value of zero is returned. A zero value is also returned * if the @p delta angle is larger than the largest angle in the response * table. ***************************************************************************/ double GCTAPsfTable::operator()(const double& delta, const double& logE, const double& theta, const double& phi, const double& zenith, const double& azimuth, const bool& etrue) const { // Initialise PSF value double psf = 0.0; // Continue only if delta is not larger than delta_max if (delta <= m_delta_max) { // Setup argument vector double arg[3]; arg[m_inx_energy] = logE; arg[m_inx_theta] = theta; arg[m_inx_delta] = delta; // Compute point spread function by trilinear interpolation psf = m_psf(m_inx_rpsf, arg[0], arg[1], arg[2]); // Constrain PSF value to non-negative values if (psf < 0.0) { psf = 0.0; } } // endif: delta was within validity range // Return PSF return psf; }
/***********************************************************************//** * @brief Update PSF parameter cache * * @param[in] logE Log10 of the true photon energy (TeV). * @param[in] theta Offset angle in camera system (rad). * * This method updates the PSF parameter cache. ***************************************************************************/ void GCTAPsf2D::update(const double& logE, const double& theta) const { // Only compute PSF parameters if arguments have changed if (logE != m_par_logE || theta != m_par_theta) { // Save parameters m_par_logE = logE; m_par_theta = theta; // Interpolate response parameters std::vector<double> pars = m_psf(logE, theta); // Set Gaussian sigmas m_sigma1 = pars[1]; m_sigma2 = pars[3]; m_sigma3 = pars[5]; // Set width parameters double sigma1 = m_sigma1 * m_sigma1; double sigma2 = m_sigma2 * m_sigma2; double sigma3 = m_sigma3 * m_sigma3; // Compute Gaussian 1 if (sigma1 > 0.0) { m_width1 = -0.5 / sigma1; } else { m_width1 = 0.0; } // Compute Gaussian 2 if (sigma2 > 0.0) { m_width2 = -0.5 / sigma2; m_norm2 = pars[2]; } else { m_width2 = 0.0; m_norm2 = 0.0; } // Compute Gaussian 3 if (sigma3 > 0.0) { m_width3 = -0.5 / sigma3; m_norm3 = pars[4]; } else { m_width3 = 0.0; m_norm3 = 0.0; } // Compute global normalization parameter double integral = gammalib::twopi * (sigma1 + sigma2*m_norm2 + sigma3*m_norm3); m_norm = (integral > 0.0) ? 1.0 / integral : 0.0; } // Return return; }
/***********************************************************************//** * @brief Performs precomputations for point spread function * * Replaces any invalid PSF histogram by zeroes, normalises the PSF * histograms to unity, and computes maximum PSF value and maximum delta * value. ***************************************************************************/ void GCTAPsfTable::precompute(void) { // Determine PSF dimensions int neng = m_psf.axis_bins(m_inx_energy); int ntheta = m_psf.axis_bins(m_inx_theta); int ndelta = m_psf.axis_bins(m_inx_delta); // Determine delta element increment int inx_inc = element(0,0,1) - element(0,0,0); // Replace any negative or invalid histogram values by zero values for (int i = 0; i < m_psf.elements(); ++i) { double element = m_psf(m_inx_rpsf, i); if (element < 0.0 || gammalib::is_notanumber(element) || gammalib::is_infinite(element)) { m_psf(m_inx_rpsf, i) = 0.0; } } // Normalise PSF to unity for (int ieng = 0; ieng < neng; ++ieng) { for (int itheta = 0; itheta < ntheta; ++itheta) { // Initialise PSF integral double sum = 0.0; // Set start element int inx = element(ieng, itheta, 0); // Integrate delta array by computing the sum over the pixels // times the pixel size time the delta angle for (int idelta = 0; idelta < ndelta; ++idelta, inx += inx_inc) { // Compute delta value (radians) double delta = m_psf.axis_nodes(m_inx_delta)[idelta]; // Compute delta bin width (radians) double width = (m_psf.axis_hi(m_inx_delta, idelta) - m_psf.axis_lo(m_inx_delta, idelta)) * gammalib::deg2rad; // Integrate PSF sum += m_psf(m_inx_rpsf, inx) * std::sin(delta) * width * gammalib::twopi; } // If integral is positive then divide PSF by integral so that it // normalises to unity if (sum > 0.0) { // Set start element int inx = element(ieng, itheta, 0); // Normalise PSF for (int idelta = 0; idelta < ndelta; ++idelta, inx += inx_inc) { m_psf(m_inx_rpsf, inx) /= sum; } } // endif: PSF integral was positive } // endfor: looped over all theta angles } // endfor: looped over all PSF values // Compute maximum PSF value m_psf_max = 0.0; for (int i = 0; i < m_psf.elements(); ++i) { if (m_psf(m_inx_rpsf, i) > m_psf_max) { m_psf_max = m_psf(m_inx_rpsf, i); } } // Set maximum PSF radius (radians) m_delta_max = m_psf.axis_hi(m_inx_delta, m_psf.axis_bins(m_inx_delta)-1) * gammalib::deg2rad; // Return return; }