/***********************************************************************//** * @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 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 Returns MC sky direction * * @param[in] energy Photon energy. * @param[in] time Photon arrival time. * @param[in,out] ran Random number generator. * @return Sky direction. * * @exception GException::invalid_value * No energy boundaries specified, or energy boundaries do not * cover the specified @p energy. * * Returns a random sky direction according to the intensity distribution of * the model sky map and the specified energy. The method makes use of a * cache array that contains the normalised cumulative flux values for each * of the sky maps in the cube. The specified energy is used to select the * appropriate cache array from the cube. Using a uniform random number, the * selected cache array is scanned using a bi-section method to determine * the skymap pixel for which the position should be returned. To avoid * binning problems, the exact position within the pixel is set by a uniform * random number generator (neglecting thus pixel distortions). The * fractional skymap pixel is then converted into a sky direction. ***************************************************************************/ GSkyDir GModelSpatialDiffuseCube::mc(const GEnergy& energy, const GTime& time, GRan& ran) const { // Allocate sky direction GSkyDir dir; // Fetch cube fetch_cube(); // Determine number of skymap pixels int npix = pixels(); // Continue only if there are skymap pixels if (npix > 0) { // If no energy boundaries are defined, throw an exception if (m_ebounds.size() < 1) { std::string msg = "The energy boundaries of the maps in the cube" " have not been defined. Maybe the map cube file" " is missing the \"ENERGIES\" extension which" " defines the energy of each map in the cube.\n" "Please provide the energy information."; throw GException::invalid_value(G_MC, msg); } // Determine the map that corresponds best to the specified energy. // This is not 100% clean, as ideally some map interpolation should // be done to the exact energy specified. However, as long as the map // does not change drastically with energy, taking the closest map // seems to be fine. int i = m_ebounds.index(energy); if (i < 0) { if (energy <= m_ebounds.emin()) { i = 0; } else if (energy >= m_ebounds.emax()) { i = m_ebounds.size()-1; } else { std::string msg = "The specified energy "+energy.print()+" does" " not fall in any of the energy boundaries of" " the map cube.\n" "Please make sure that the map cube energies" " are properly defined."; throw GException::invalid_value(G_MC, msg); } } // Get uniform random number double u = ran.uniform(); // Get pixel index according to random number. We use a bi-section // method to find the corresponding skymap pixel int offset = i * (npix+1); int low = offset; int high = offset + npix; while ((high - low) > 1) { int mid = (low+high) / 2; if (u < m_mc_cache[mid]) { high = mid; } else if (m_mc_cache[mid] <= u) { low = mid; } } // Convert sky map index to sky map pixel GSkyPixel pixel = m_cube.inx2pix(low-offset); // Randomize pixel pixel.x(pixel.x() + ran.uniform() - 0.5); pixel.y(pixel.y() + ran.uniform() - 0.5); // Get sky direction dir = m_cube.pix2dir(pixel); } // endif: there were pixels in sky map // Return sky direction return dir; }