/***********************************************************************//** * @brief Returns MC sky direction * * @param[in] ran Random number generator. * * @exception GException::feature_not_implemented * Method not yet implemented * * This method returns a random sky direction according to the intensity * distribution of the model sky map. It makes use of a cache array that * contains the normalized cumulative flux values of the skymap. Using a * uniform random number, this 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 GModelSpatialMap::mc(GRan& ran) const { // Allocate sky direction GSkyDir dir; // Determine number of skymap pixels int npix = m_map.npix(); // Continue only if there are skymap pixels if (npix > 0) { // 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 low = 0; int high = 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 1D pixel index to 2D pixel index GSkyPixel pixel = m_map.pix2xy(low); // Randomize pixel pixel.x(pixel.x() + ran.uniform() - 0.5); pixel.y(pixel.y() + ran.uniform() - 0.5); // Get sky direction dir = m_map.xy2dir(pixel); } // endif: there were pixels in sky map // Return sky direction return dir; }
/***********************************************************************//** * @brief Test GSkyPixel class * * Test GSkyPixel class. ***************************************************************************/ void TestGSky::test_GSkyPixel(void) { // Test void constructor test_try("Test void constructor"); try { GSkyPixel pixel; test_value(pixel.size(), 0); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test 1D constructor test_try("Test 1D constructor (int version)"); try { GSkyPixel pixel(41); test_assert(pixel.is_1D(), "Pixel is not 1D"); test_assert(!pixel.is_2D(), "Pixel is 2D but it should be 1D"); test_value(pixel.size(), 1); test_value(pixel.index(), 41.0); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test 1D constructor test_try("Test 1D constructor (double version)"); try { GSkyPixel pixel(41.001); test_assert(pixel.is_1D(), "Pixel is not 1D"); test_assert(!pixel.is_2D(), "Pixel is 2D but it should be 1D"); test_value(pixel.size(), 1); test_value(pixel.index(), 41.001); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test 2D constructor test_try("Test 2D constructor (int version)"); try { GSkyPixel pixel(41,14); test_assert(!pixel.is_1D(), "Pixel is 1D but it should be 2D"); test_assert(pixel.is_2D(), "Pixel is not 2D"); test_value(pixel.size(), 2); test_value(pixel.x(), 41.0); test_value(pixel.y(), 14.0); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test 2D constructor test_try("Test 2D constructor (double version)"); try { GSkyPixel pixel(41.001,14.003); test_assert(!pixel.is_1D(), "Pixel is 1D but it should be 2D"); test_assert(pixel.is_2D(), "Pixel is not 2D"); test_value(pixel.size(), 2); test_value(pixel.x(), 41.001); test_value(pixel.y(), 14.003); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test 1D indexing test_try("Test 1D indexing"); try { GSkyPixel pixel; test_value(pixel.size(), 0); pixel = 41; int index = pixel; test_value(index, 41); pixel = 41.001; index = pixel; test_value(index, 41); double dindex = pixel; test_value(dindex, 41.001); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test cloning test_try("Test cloning"); try { GSkyPixel pixel; test_value(pixel.size(), 0); GSkyPixel* clone = pixel.clone(); test_value(clone->size(), 0); delete clone; pixel = 41; clone = pixel.clone(); test_value(clone->size(), 1); int index = pixel; test_value(index, 41); delete clone; pixel.x(41.001); pixel.y(14.003); clone = pixel.clone(); test_value(clone->size(), 2); test_value(clone->x(), 41.001); test_value(clone->y(), 14.003); delete clone; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Return return; }
/***********************************************************************//** * @brief Fill events into counts cube * * @param[in] obs CTA observation. * * @exception GException::invalid_value * No event list found in observation. * * Fills the events from an event list in the counts cube setup by init_cube. ***************************************************************************/ void ctbin::fill_cube(GCTAObservation* obs) { // Continue only if observation pointer is valid if (obs != NULL) { // Make sure that the observation holds a CTA event list. If this // is not the case then throw an exception. const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs->events()); if (events == NULL) { std::string msg = "CTA Observation does not contain an event " "list. Event list information is needed to " "fill the counts map."; throw GException::invalid_value(G_FILL_CUBE, msg); } // Get the RoI const GCTARoi& roi = events->roi(); // Initialise binning statistics int num_outside_roi = 0; int num_outside_map = 0; int num_outside_ebds = 0; int num_in_map = 0; // Fill sky map for (int i = 0; i < events->size(); ++i) { // Get event const GCTAEventAtom* event = (*events)[i]; // Determine sky pixel GCTAInstDir* inst = (GCTAInstDir*)&(event->dir()); GSkyDir dir = inst->dir(); GSkyPixel pixel = m_cube.dir2pix(dir); // Skip if pixel is outside RoI if (roi.centre().dir().dist_deg(dir) > roi.radius()) { num_outside_roi++; continue; } // Skip if pixel is out of range if (pixel.x() < -0.5 || pixel.x() > (m_cube.nx()-0.5) || pixel.y() < -0.5 || pixel.y() > (m_cube.ny()-0.5)) { num_outside_map++; continue; } // Determine energy bin. Skip if we are outside the energy range int index = m_ebounds.index(event->energy()); if (index == -1) { num_outside_ebds++; continue; } // Fill event in skymap m_cube(pixel, index) += 1.0; num_in_map++; } // endfor: looped over all events // Append GTIs m_gti.extend(events->gti()); // Update ontime and livetime m_ontime += obs->ontime(); m_livetime += obs->livetime(); // Log filling results if (logTerse()) { log << gammalib::parformat("Events in list"); log << obs->events()->size() << std::endl; log << gammalib::parformat("Events in cube"); log << num_in_map << std::endl; log << gammalib::parformat("Event bins outside RoI"); log << num_outside_roi << std::endl; log << gammalib::parformat("Events outside cube area"); log << num_outside_map << std::endl; log << gammalib::parformat("Events outside energy bins"); log << num_outside_ebds << std::endl; } // Log cube if (logExplicit()) { log.header1("Counts cube"); log << m_cube << std::endl; } } // endif: observation was valid // 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; }
/***********************************************************************//** * @brief Bin events into a counts map * * @param[in] obs CTA observation. * * @exception GException::no_list * No event list found in observation. * @exception GCTAException::no_pointing * No valid CTA pointing found. * * This method bins the events found in a CTA events list into a counts map * and replaces the event list by the counts map in the observation. The * energy boundaries of the counts map are also stored in the observation's * energy boundary member. * * If the reference values for the map centre (m_xref, m_yref) are 9999.0, * the pointing direction of the observation is taken as the map centre. * Otherwise, the specified reference value is used. ***************************************************************************/ void ctbin::bin_events(GCTAObservation* obs) { // Continue only if observation pointer is valid if (obs != NULL) { // Make sure that the observation holds a CTA event list. If this // is not the case then throw an exception. if (dynamic_cast<const GCTAEventList*>(obs->events()) == NULL) { throw GException::no_list(G_BIN_EVENTS); } // Setup energy range covered by data GEnergy emin; GEnergy emax; GEbounds ebds; emin.TeV(m_emin); emax.TeV(m_emax); ebds.setlog(emin, emax, m_enumbins); // Get Good Time intervals GGti gti = obs->events()->gti(); // Get map centre double xref; double yref; if (m_xref != 9999.0 && m_yref != 9999.0) { xref = m_xref; yref = m_yref; } else { // Get pointer on CTA pointing const GCTAPointing *pnt = obs->pointing(); if (pnt == NULL) { throw GCTAException::no_pointing(G_BIN_EVENTS); } // Set reference point to pointing if (toupper(m_coordsys) == "GAL") { xref = pnt->dir().l_deg(); yref = pnt->dir().b_deg(); } else { xref = pnt->dir().ra_deg(); yref = pnt->dir().dec_deg(); } } // endelse: map centre set to pointing // Create skymap GSkymap map = GSkymap(m_proj, m_coordsys, xref, yref, m_binsz, m_binsz, m_nxpix, m_nypix, m_enumbins); // Initialise binning statistics int num_outside_map = 0; int num_outside_ebds = 0; int num_in_map = 0; // Fill sky map GCTAEventList* events = static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events())); for (GCTAEventList::iterator event = events->begin(); event != events->end(); ++event) { // Determine sky pixel GCTAInstDir* inst = (GCTAInstDir*)&(event->dir()); GSkyDir dir = inst->dir(); GSkyPixel pixel = map.dir2xy(dir); // Skip if pixel is out of range if (pixel.x() < -0.5 || pixel.x() > (m_nxpix-0.5) || pixel.y() < -0.5 || pixel.y() > (m_nypix-0.5)) { num_outside_map++; continue; } // Determine energy bin. Skip if we are outside the energy range int index = ebds.index(event->energy()); if (index == -1) { num_outside_ebds++; continue; } // Fill event in skymap map(pixel, index) += 1.0; num_in_map++; } // endfor: looped over all events // Log binning results if (logTerse()) { log << std::endl; log.header1("Binning"); log << parformat("Events in list"); log << obs->events()->size() << std::endl; log << parformat("Events in map"); log << num_in_map << std::endl; log << parformat("Events outside map area"); log << num_outside_map << std::endl; log << parformat("Events outside energy bins"); log << num_outside_ebds << std::endl; } // Log map if (logTerse()) { log << std::endl; log.header1("Counts map"); log << map << std::endl; } // Create events cube from sky map GCTAEventCube cube(map, ebds, gti); // Replace event list by event cube in observation obs->events(&cube); } // endif: observation was valid // Return return; }