/***********************************************************************//** * @brief Extract ROI from data sub-space keywords * * @param[in] hdu FITS HDU * * @exception GException::invalid_value * Invalid ROI data sub-space encountered * * Reads the ROI data sub-space keywords by searching for a DSTYPx keyword * named "POS(RA,DEC)". The data sub-space information is expected to be in * the format "CIRCLE(267.0208,-24.78,4.5)", where the 3 arguments are Right * Ascension, Declination and radius in units of degrees. No detailed syntax * checking is performed. * * If no ROI information has been found, an GCTARoi object with initial * values will be returned. ***************************************************************************/ GCTARoi gammalib::read_ds_roi(const GFitsHDU& hdu) { // Initialise ROI GCTARoi roi; // Get number of data sub-space keywords (default to 0 if keyword is // not found) int ndskeys = (hdu.has_card("NDSKEYS")) ? hdu.integer("NDSKEYS") : 0; // Loop over all data selection keys for (int i = 1; i <= ndskeys; ++i) { // Set data sub-space key strings std::string type_key = "DSTYP"+gammalib::str(i); //std::string unit_key = "DSUNI"+gammalib::str(i); std::string value_key = "DSVAL"+gammalib::str(i); // Continue only if type_key is found and if this key is POS(RA,DEC) if (hdu.has_card(type_key) && hdu.string(type_key) == "POS(RA,DEC)") { // ... //std::string unit = gammalib::toupper(hdu.string(unit_key)); std::string value = hdu.string(value_key); std::string value_proc = gammalib::strip_chars(value, "CIRCLE("); value_proc = gammalib::strip_chars(value_proc, ")"); std::vector<std::string> args = gammalib::split(value_proc, ","); if (args.size() == 3) { double ra = gammalib::todouble(args[0]); double dec = gammalib::todouble(args[1]); double rad = gammalib::todouble(args[2]); GCTAInstDir dir; dir.dir().radec_deg(ra, dec); roi.centre(dir); roi.radius(rad); } else { std::string msg = "Invalid acceptance cone value \""+value+ "\" encountered in data sub-space " "key \""+value_key+"\"."; throw GException::invalid_value(G_READ_DS_ROI, msg); } } // endif: POS(RA,DEC) type found } // endfor: looped over data sub-space keys // Return roi return roi; }
/***********************************************************************//** * @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 Return simulated list of events * * @param[in] obs Observation. * @param[in] ran Random number generator. * @return Pointer to list of simulated events (needs to be de-allocated by * client) * * @exception GException::invalid_argument * Specified observation is not a CTA observation. * * Draws a sample of events from the background model using a Monte * Carlo simulation. The region of interest, the energy boundaries and the * good time interval for the sampling will be extracted from the observation * argument that is passed to the method. The method also requires a random * number generator of type GRan which is passed by reference, hence the * state of the random number generator will be changed by the method. * * The method also applies a deadtime correction using a Monte Carlo process, * taking into account temporal deadtime variations. For this purpose, the * method makes use of the time dependent GObservation::deadc method. * * For each event in the returned event list, the sky direction, the nominal * coordinates (DETX and DETY), the energy and the time will be set. ***************************************************************************/ GCTAEventList* GCTAModelIrfBackground::mc(const GObservation& obs, GRan& ran) const { // Initialise new event list GCTAEventList* list = new GCTAEventList; // Continue only if model is valid) if (valid_model()) { // Retrieve CTA observation const GCTAObservation* cta = dynamic_cast<const GCTAObservation*>(&obs); if (cta == NULL) { std::string msg = "Specified observation is not a CTA observation.\n" + obs.print(); throw GException::invalid_argument(G_MC, msg); } // Get pointer on CTA IRF response const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(cta->response()); if (rsp == NULL) { std::string msg = "Specified observation does not contain" " an IRF response.\n" + obs.print(); throw GException::invalid_argument(G_MC, msg); } // Retrieve CTA response and pointing const GCTAPointing& pnt = cta->pointing(); // Get pointer to CTA background const GCTABackground* bgd = rsp->background(); if (bgd == NULL) { std::string msg = "Specified observation contains no background" " information.\n" + obs.print(); throw GException::invalid_argument(G_MC, msg); } // Retrieve event list to access the ROI, energy boundaries and GTIs const GCTAEventList* events = dynamic_cast<const GCTAEventList*>(obs.events()); if (events == NULL) { std::string msg = "No CTA event list found in observation.\n" + obs.print(); throw GException::invalid_argument(G_MC, msg); } // Get simulation region const GCTARoi& roi = events->roi(); const GEbounds& ebounds = events->ebounds(); const GGti& gti = events->gti(); // Set simulation region for result event list list->roi(roi); list->ebounds(ebounds); list->gti(gti); // Create a spectral model that combines the information from the // background information and the spectrum provided by the model GModelSpectralNodes spectral(bgd->spectrum()); for (int i = 0; i < spectral.nodes(); ++i) { GEnergy energy = spectral.energy(i); double intensity = spectral.intensity(i); double norm = m_spectral->eval(energy, events->tstart()); spectral.intensity(i, norm*intensity); } // Loop over all energy boundaries for (int ieng = 0; ieng < ebounds.size(); ++ieng) { // Compute the background rate in model within the energy // boundaries from spectral component (units: cts/s). // Note that the time here is ontime. Deadtime correction will // be done later. double rate = spectral.flux(ebounds.emin(ieng), ebounds.emax(ieng)); // Debug option: dump rate #if defined(G_DUMP_MC) std::cout << "GCTAModelIrfBackground::mc(\"" << name() << "\": "; std::cout << "rate=" << rate << " cts/s)" << std::endl; #endif // Loop over all good time intervals for (int itime = 0; itime < gti.size(); ++itime) { // Get Monte Carlo event arrival times from temporal model GTimes times = m_temporal->mc(rate, gti.tstart(itime), gti.tstop(itime), ran); // Get number of events int n_events = times.size(); // Reserve space for events if (n_events > 0) { list->reserve(n_events); } // Debug option: provide number of times and initialize // statisics #if defined(G_DUMP_MC) std::cout << " Interval " << itime; std::cout << " times=" << n_events << std::endl; int n_killed_by_deadtime = 0; int n_killed_by_roi = 0; #endif // Loop over events for (int i = 0; i < n_events; ++i) { // Apply deadtime correction double deadc = obs.deadc(times[i]); if (deadc < 1.0) { if (ran.uniform() > deadc) { #if defined(G_DUMP_MC) n_killed_by_deadtime++; #endif continue; } } // Get Monte Carlo event energy from spectral model GEnergy energy = spectral.mc(ebounds.emin(ieng), ebounds.emax(ieng), times[i], ran); // Get Monte Carlo event direction from spatial model. // This only will set the DETX and DETY coordinates. GCTAInstDir instdir = bgd->mc(energy, times[i], ran); // Derive sky direction from instrument coordinates GSkyDir skydir = pnt.skydir(instdir); // Set sky direction in GCTAInstDir object instdir.dir(skydir); // Allocate event GCTAEventAtom event; // Set event attributes event.dir(instdir); event.energy(energy); event.time(times[i]); // Append event to list if it falls in ROI if (events->roi().contains(event)) { list->append(event); } #if defined(G_DUMP_MC) else { n_killed_by_roi++; } #endif } // endfor: looped over all events // Debug option: provide statisics #if defined(G_DUMP_MC) std::cout << " Killed by deadtime="; std::cout << n_killed_by_deadtime << std::endl; std::cout << " Killed by ROI="; std::cout << n_killed_by_roi << std::endl; #endif } // endfor: looped over all GTIs } // endfor: looped over all energy boundaries } // endif: model was valid // Return return list; }
/***********************************************************************//** * @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; }