/***********************************************************************//** * @brief Test CTA Npred computation * * Tests the Npred computation for the diffuse source model. This is done * by loading the model from the XML file and by calling the * GCTAObservation::npred method which in turn calls the * GCTAResponse::npred_diffuse method. The test takes a few seconds. ***************************************************************************/ void TestGCTAResponse::test_response_npred_diffuse(void) { // Set reference value double ref = 11212.26274; // Set parameters double src_ra = 201.3651; double src_dec = -43.0191; double roi_rad = 4.0; // Setup ROI centred on Cen A with a radius of 4 deg GCTARoi roi; GCTAInstDir instDir; instDir.radec_deg(src_ra, src_dec); roi.centre(instDir); roi.radius(roi_rad); // Setup pointing on Cen A GSkyDir skyDir; skyDir.radec_deg(src_ra, src_dec); GCTAPointing pnt; pnt.dir(skyDir); // Setup dummy event list GGti gti; GEbounds ebounds; GTime tstart(0.0); GTime tstop(1800.0); GEnergy emin; GEnergy emax; emin.TeV(0.1); emax.TeV(100.0); gti.append(tstart, tstop); ebounds.append(emin, emax); GCTAEventList events; events.roi(roi); events.gti(gti); events.ebounds(ebounds); // Setup dummy CTA observation GCTAObservation obs; obs.ontime(1800.0); obs.livetime(1600.0); obs.deadc(1600.0/1800.0); obs.response(cta_irf, cta_caldb); obs.events(&events); obs.pointing(pnt); // Load models for Npred computation GModels models(cta_rsp_xml); // Perform Npred computation double npred = obs.npred(models, NULL); // Test Npred test_value(npred, ref, 1.0e-5, "Diffuse Npred computation"); // Return return; }
// Generate an EventCube, rate is the number of event per second. Events have a time between tmin and tmax. virtual GTestEventCube* generateCube(const double &rate, const GTime &tmin, const GTime &tmax, GRan &ran) { // Create an event list GTestEventCube* cube = new GTestEventCube(); // Set min and max energy for ebounds // npred method integrate the model on time and energy. // In order to have a rate which not depend on energy we create an interval of 1 Mev. GEnergy engmin,engmax; engmin.MeV(1.0); engmax.MeV(2.0); // Instrument Direction GTestInstDir dir; // Generate an times list. GTimes times = m_modelTps->mc(rate, tmin, tmax, ran); GTestEventBin bin; bin.time(times[0]); bin.energy(engmin); bin.ewidth(engmax-engmin); bin.dir(dir); bin.ontime(10); // 10 sec per bin for (int i = 0; i < times.size(); ++i) { if ((bin.time().secs() + bin.ontime()) < times[i].secs()) { // Add the event to the cube cube->append(bin); bin.counts(0.0); bin.time(times[i]); bin.energy(engmin); bin.ewidth(engmax-engmin); bin.dir(dir); bin.ontime(10); // 10 sec per bin } bin.counts(bin.counts()+1); } // Create a time interval and add it to the list. GGti gti; gti.append(tmin,tmax); cube->gti(gti); // Create an energy interval and add it to the list GEbounds ebounds; ebounds.append(engmin,engmax); cube->ebounds(ebounds); return cube; };
/***********************************************************************//** * @brief Read energy boundary data sub-space keywords * * @param[in] hdu FITS HDU * * @exception GException::invalid_value * Invalid energy data sub-space encountered * * Reads the energy boundary data sub-space keywords by searching for a * DSTYPx keyword named "ENERGY". The data sub-space information is expected * to be in the format "200:50000", where the 2 arguments are the minimum * and maximum energy. The energy unit is given by the keyword DSUNIx, which * supports keV, MeV, GeV and TeV (case independent). No detailed syntax * checking is performed. ***************************************************************************/ GEbounds gammalib::read_ds_ebounds(const GFitsHDU& hdu) { // Initialise energy boundaries GEbounds ebounds; // 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 sub-space 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 ENERGY if (hdu.has_card(type_key) && hdu.string(type_key) == "ENERGY") { // Extract energy boundaries std::string value = hdu.string(value_key); std::string unit = hdu.string(unit_key); std::vector<std::string> values = gammalib::split(value, ":"); if (values.size() == 2) { double emin = gammalib::todouble(values[0]); double emax = gammalib::todouble(values[1]); GEnergy e_min(emin, unit); GEnergy e_max(emax, unit); ebounds.append(e_min, e_max); } else { std::string msg = "Invalid energy value \""+value+ "\" encountered in data selection key \""+ value_key+"\""; throw GException::invalid_value(G_READ_DS_EBOUNDS, msg); } } // endif: ENERGY type_key found } // endfor: looped over data selection keys // Return return ebounds; }
/***********************************************************************//** * @brief Append energy boundaries * * @param[in] ebds Energy boundaries. * * Append energy boundaries to the container. ***************************************************************************/ void GEbounds::extend(const GEbounds& ebds) { // Do nothing if energy boundaries are empty if (!ebds.is_empty()) { // Allocate new intervals int num = m_num+ebds.size(); GEnergy* min = new GEnergy[num]; GEnergy* max = new GEnergy[num]; // Initialise index int inx = 0; // Copy existing intervals for (; inx < m_num; ++inx) { min[inx] = m_min[inx]; max[inx] = m_max[inx]; } // Append intervals for (int i = 0; i < ebds.size(); ++i, ++inx) { min[inx] = ebds.m_min[i]; max[inx] = ebds.m_max[i]; } // 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(); } // endif: energy boundaries were not empty // Return return; }
// Generate an EventList, rate is the number of event per second. Events have a time between tmin and tmax. virtual GTestEventList* generateList(const double &rate, const GTime &tmin, const GTime &tmax, GRan &ran) { // Create an event list GTestEventList * list = new GTestEventList(); // Set min and max energy for ebounds // npred method integrate the model on time and energy. // In order to have a rate which not depend on energy we create an interval of 1 Mev. GEnergy engmin,engmax; engmin.MeV(1.0); engmax.MeV(2.0); // Instrument Direction GTestInstDir dir; // Generate an times list. GTimes times = m_modelTps->mc(rate,tmin, tmax,ran); for (int i = 0; i < times.size() ; ++i) { GTestEventAtom event; event.dir(dir); event.energy(engmin); event.time(times[i]); // Add the event to the list list->append(event); } // Create a time interval and add it to the list. GGti gti; gti.append(tmin,tmax); list->gti(gti); // Create an energy interval and add it to the list GEbounds ebounds; ebounds.append(engmin,engmax); list->ebounds(ebounds); return list; }
/***********************************************************************//** * @brief Energy boundary constructor * * @param[in] ebds Energy boundaries. ***************************************************************************/ GArf::GArf(const GEbounds& ebds) { // Initialise members init_members(); // Set energy boundaries m_ebounds = ebds; // Initialize spectral response m_specresp.assign(ebds.size(), 0.0); // Return return; }
/***********************************************************************//** * @brief Energy boundary constructor * * @param[in] ebds Energy boundaries. ***************************************************************************/ GPha::GPha(const GEbounds& ebds) { // Initialise members init_members(); // Set energy boundaries m_ebounds = ebds; // Initialize spectrum m_counts.assign(ebds.size(), 0.0); // Return return; }
/***********************************************************************//** * @brief Test GEbounds ***************************************************************************/ void TestGObservation::test_ebounds(void) { // Test void constructor test_try("Void constructor"); try { GEbounds ebds; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Manipulate energy boudaries starting from an empty object GEbounds ebds; test_value(ebds.size(), 0, "GEbounds should have zero size."); test_assert(ebds.is_empty(), "GEbounds should be empty."); test_value(ebds.emin().MeV(), 0.0, 1.0e-10, "Minimum energy should be 0."); test_value(ebds.emax().MeV(), 0.0, 1.0e-10, "Maximum energy should be 0."); // Add empty interval ebds.append(GEnergy(1.0, "MeV"), GEnergy(1.0, "MeV")); test_value(ebds.size(), 0, "GEbounds should have zero size."); test_assert(ebds.is_empty(), "GEbounds should be empty."); test_value(ebds.emin().MeV(), 0.0, 1.0e-10, "Minimum energy should be 0."); test_value(ebds.emax().MeV(), 0.0, 1.0e-10, "Maximum energy should be 0."); // Add one interval ebds.append(GEnergy(1.0, "MeV"), GEnergy(10.0, "MeV")); test_value(ebds.size(), 1, "GEbounds should have 1 element."); test_assert(!ebds.is_empty(), "GEbounds should not be empty."); test_value(ebds.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1."); test_value(ebds.emax().MeV(), 10.0, 1.0e-10, "Maximum energy should be 10."); // Remove interval ebds.remove(0); test_value(ebds.size(), 0, "GEbounds should have zero size."); test_assert(ebds.is_empty(), "GEbounds should be empty."); test_value(ebds.emin().MeV(), 0.0, 1.0e-10, "Minimum energy should be 0."); test_value(ebds.emax().MeV(), 0.0, 1.0e-10, "Maximum energy should be 0."); // Append two overlapping intervals ebds.append(GEnergy(1.0, "MeV"), GEnergy(100.0, "MeV")); ebds.append(GEnergy(10.0, "MeV"), GEnergy(1000.0, "MeV")); test_value(ebds.size(), 2, "GEbounds should have 2 elements."); test_assert(!ebds.is_empty(), "GEbounds should not be empty."); test_value(ebds.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1."); test_value(ebds.emax().MeV(), 1000.0, 1.0e-10, "Maximum energy should be 1000."); // Clear object ebds.clear(); test_value(ebds.size(), 0, "GEbounds should have zero size."); test_assert(ebds.is_empty(), "GEbounds should be empty."); test_value(ebds.emin().MeV(), 0.0, 1.0e-10, "Minimum energy should be 0."); test_value(ebds.emax().MeV(), 0.0, 1.0e-10, "Maximum energy should be 0."); // Append two overlapping intervals in inverse order ebds.clear(); ebds.append(GEnergy(10.0, "MeV"), GEnergy(1000.0, "MeV")); ebds.append(GEnergy(1.0, "MeV"), GEnergy(100.0, "MeV")); test_value(ebds.size(), 2, "GEbounds should have 2 elements."); test_assert(!ebds.is_empty(), "GEbounds should not be empty."); test_value(ebds.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1."); test_value(ebds.emax().MeV(), 1000.0, 1.0e-10, "Maximum energy should be 1000."); // Insert two overlapping intervals ebds.clear(); ebds.insert(GEnergy(1.0, "MeV"), GEnergy(100.0, "MeV")); ebds.insert(GEnergy(10.0, "MeV"), GEnergy(1000.0, "MeV")); test_value(ebds.size(), 2, "GEbounds should have 2 elements."); test_assert(!ebds.is_empty(), "GEbounds should not be empty."); test_value(ebds.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1."); test_value(ebds.emax().MeV(), 1000.0, 1.0e-10, "Maximum energy should be 1000."); // Insert two overlapping intervals in inverse order ebds.clear(); ebds.insert(GEnergy(10.0, "MeV"), GEnergy(1000.0, "MeV")); ebds.insert(GEnergy(1.0, "MeV"), GEnergy(100.0, "MeV")); test_value(ebds.size(), 2, "GEbounds should have 2 elements."); test_assert(!ebds.is_empty(), "GEbounds should not be empty."); test_value(ebds.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1."); test_value(ebds.emax().MeV(), 1000.0, 1.0e-10, "Maximum energy should be 1000."); // Merge two overlapping intervals ebds.clear(); ebds.merge(GEnergy(1.0, "MeV"), GEnergy(100.0, "MeV")); ebds.merge(GEnergy(10.0, "MeV"), GEnergy(1000.0, "MeV")); test_value(ebds.size(), 1, "GEbounds should have 1 element."); test_assert(!ebds.is_empty(), "GEbounds should not be empty."); test_value(ebds.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1."); test_value(ebds.emax().MeV(), 1000.0, 1.0e-10, "Maximum energy should be 1000."); // Merge two overlapping intervals in inverse order ebds.clear(); ebds.merge(GEnergy(10.0, "MeV"), GEnergy(1000.0, "MeV")); ebds.merge(GEnergy(1.0, "MeV"), GEnergy(100.0, "MeV")); test_value(ebds.size(), 1, "GEbounds should have 1 element."); test_assert(!ebds.is_empty(), "GEbounds should not be empty."); test_value(ebds.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1."); test_value(ebds.emax().MeV(), 1000.0, 1.0e-10, "Maximum energy should be 1000."); // Check linear boundaries ebds.clear(); ebds.set_lin(2, GEnergy(1.0, "MeV"), GEnergy(3.0, "MeV")); test_value(ebds.size(), 2, "GEbounds should have 2 elements."); test_assert(!ebds.is_empty(), "GEbounds should not be empty."); test_value(ebds.emin(0).MeV(), 1.0, 1.0e-10, "Bin 0 minimum energy should be 1."); test_value(ebds.emin(1).MeV(), 2.0, 1.0e-10, "Bin 1 minimum energy should be 2."); test_value(ebds.emax(0).MeV(), 2.0, 1.0e-10, "Bin 0 maximum energy should be 2."); test_value(ebds.emax(1).MeV(), 3.0, 1.0e-10, "Bin 1 maximum energy should be 3."); test_value(ebds.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1."); test_value(ebds.emax().MeV(), 3.0, 1.0e-10, "Maximum energy should be 3."); // Check logarithmic boundaries ebds.clear(); ebds.set_log(2, GEnergy(1.0, "MeV"), GEnergy(100.0, "MeV")); test_value(ebds.size(), 2, "GEbounds should have 2 elements."); test_assert(!ebds.is_empty(), "GEbounds should not be empty."); test_value(ebds.emin(0).MeV(), 1.0, 1.0e-10, "Bin 0 minimum energy should be 1."); test_value(ebds.emin(1).MeV(), 10.0, 1.0e-10, "Bin 1 minimum energy should be 10."); test_value(ebds.emax(0).MeV(), 10.0, 1.0e-10, "Bin 0 maximum energy should be 10."); test_value(ebds.emax(1).MeV(), 100.0, 1.0e-10, "Bin 1 maximum energy should be 100."); test_value(ebds.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1."); test_value(ebds.emax().MeV(), 100.0, 1.0e-10, "Maximum energy should be 100."); // Check boundary extension GEbounds ext(1, GEnergy(100.0, "MeV"), GEnergy(1000.0, "MeV")); ebds.extend(ext); test_value(ebds.size(), 3, "GEbounds should have 3 elements."); test_assert(!ebds.is_empty(), "GEbounds should not be empty."); test_value(ebds.emin(0).MeV(), 1.0, 1.0e-10, "Bin 0 minimum energy should be 1."); test_value(ebds.emin(1).MeV(), 10.0, 1.0e-10, "Bin 1 minimum energy should be 10."); test_value(ebds.emin(2).MeV(), 100.0, 1.0e-10, "Bin 2 minimum energy should be 100."); test_value(ebds.emax(0).MeV(), 10.0, 1.0e-10, "Bin 0 maximum energy should be 10."); test_value(ebds.emax(1).MeV(), 100.0, 1.0e-10, "Bin 1 maximum energy should be 100."); test_value(ebds.emax(2).MeV(), 1000.0, 1.0e-10, "Bin 1 maximum energy should be 1000."); test_value(ebds.emin().MeV(), 1.0, 1.0e-10, "Minimum energy should be 1."); test_value(ebds.emax().MeV(), 1000.0, 1.0e-10, "Maximum energy should be 1000."); // Return return; }
/***********************************************************************//** * @brief Test CTA IRF computation for diffuse source model * * Tests the IRF computation for the diffuse source model. This is done * by calling the GCTAObservation::model method which in turn calls the * GCTAResponse::irf_diffuse method. The test is done for a small counts * map to keep the test executing reasonably fast. ***************************************************************************/ void TestGCTAResponse::test_response_irf_diffuse(void) { // Set reference value double ref = 13803.800313356; // Set parameters double src_ra = 201.3651; double src_dec = -43.0191; int nebins = 5; // Setup pointing on Cen A GSkyDir skyDir; skyDir.radec_deg(src_ra, src_dec); GCTAPointing pnt; pnt.dir(skyDir); // Setup skymap (10 energy layers) GSkymap map("CAR", "CEL", src_ra, src_dec, 0.5, 0.5, 10, 10, nebins); // Setup time interval GGti gti; GTime tstart(0.0); GTime tstop(1800.0); gti.append(tstart, tstop); // Setup energy boundaries GEbounds ebounds; GEnergy emin; GEnergy emax; emin.TeV(0.1); emax.TeV(100.0); ebounds.setlog(emin, emax, nebins); // Setup event cube centered on Cen A GCTAEventCube cube(map, ebounds, gti); // Setup dummy CTA observation GCTAObservation obs; obs.ontime(1800.0); obs.livetime(1600.0); obs.deadc(1600.0/1800.0); obs.response(cta_irf, cta_caldb); obs.events(&cube); obs.pointing(pnt); // Load model for IRF computation GModels models(cta_rsp_xml); // Reset sum double sum = 0.0; // Iterate over all bins in event cube for (int i = 0; i < obs.events()->size(); ++i) { // Get event pointer const GEventBin* bin = (*(static_cast<const GEventCube*>(obs.events())))[i]; // Get model and add to sum double model = obs.model(models, *bin, NULL) * bin->size(); sum += model; } // Test sum test_value(sum, ref, 1.0e-5, "Diffuse IRF computation"); // Return return; }
/***********************************************************************//** * @brief Set empty CTA event list * * @param[in] obs CTA observation. * * Attaches an empty event list to CTA observation. The method also sets the * pointing direction using the m_ra and m_dec members, the ROI based on * m_ra, m_dec and m_rad, a single GTI based on m_tmin and m_tmax, and a * single energy boundary based on m_emin and m_emax. The method furthermore * sets the ontime, livetime and deadtime correction factor. ***************************************************************************/ void ctobssim::set_list(GCTAObservation* obs) { // Continue only if observation is valid if (obs != NULL) { // Get CTA observation parameters m_ra = (*this)["ra"].real(); m_dec = (*this)["dec"].real(); m_rad = (*this)["rad"].real(); m_tmin = (*this)["tmin"].real(); m_tmax = (*this)["tmax"].real(); m_emin = (*this)["emin"].real(); m_emax = (*this)["emax"].real(); m_deadc = (*this)["deadc"].real(); // Allocate CTA event list GCTAEventList events; // Set pointing direction GCTAPointing pnt; GSkyDir skydir; skydir.radec_deg(m_ra, m_dec); pnt.dir(skydir); // Set ROI GCTAInstDir instdir(skydir); GCTARoi roi(instdir, m_rad); // Set GTI GGti gti(m_cta_ref); GTime tstart; GTime tstop; tstart.set(m_tmin, m_cta_ref); tstop.set(m_tmax, m_cta_ref); gti.append(tstart, tstop); // Set energy boundaries GEbounds ebounds; GEnergy emin; GEnergy emax; emin.TeV(m_emin); emax.TeV(m_emax); ebounds.append(emin, emax); // Set CTA event list attributes events.roi(roi); events.gti(gti); events.ebounds(ebounds); // Attach event list to CTA observation obs->events(events); // Set observation ontime, livetime and deadtime correction factor obs->ontime(gti.ontime()); obs->livetime(gti.ontime()*m_deadc); obs->deadc(m_deadc); } // endif: oberservation was valid // Return return; }
/***********************************************************************//** * @brief Get observation container * * Get an observation container according to the user parameters. The method * supports loading of a individual FITS file or an observation definition * file in XML format. * * If the input filename is empty, the method checks for the existence of the * "expcube", "psfcube" and "bkgcube" parameters. If file names have been * specified, the method loads the files and creates a dummy events cube that * is appended to the observation container. * * If no file names are specified for the "expcube", "psfcube" or "bkgcube" * parameters, the method reads the necessary parameters to build a CTA * observation from scratch. * * The method sets m_append_cube = true and m_binned = true in case that * a stacked observation is requested (as detected by the presence of the * "expcube", "psfcube", and "bkgcube" parameters). In that case, it appended * a dummy event cube to the observation. ***************************************************************************/ void ctmodel::get_obs(void) { // Get the filename from the input parameters std::string filename = (*this)["inobs"].filename(); // If no observation definition file has been specified then read all // parameters that are necessary to create an observation from scratch if ((filename == "NONE") || (gammalib::strip_whitespace(filename) == "")) { // Get response cube filenames std::string expcube = (*this)["expcube"].filename(); std::string psfcube = (*this)["psfcube"].filename(); std::string bkgcube = (*this)["bkgcube"].filename(); // If the filenames are valid then build an observation from cube // response information if ((expcube != "NONE") && (psfcube != "NONE") && (bkgcube != "NONE") && (gammalib::strip_whitespace(expcube) != "") && (gammalib::strip_whitespace(psfcube) != "") && (gammalib::strip_whitespace(bkgcube) != "")) { // Get exposure, PSF and background cubes GCTACubeExposure exposure(expcube); GCTACubePsf psf(psfcube); GCTACubeBackground background(bkgcube); // Create energy boundaries GEbounds ebounds = create_ebounds(); // Create dummy sky map cube GSkyMap map("CAR","GAL",0.0,0.0,1.0,1.0,1,1,ebounds.size()); // Create event cube GCTAEventCube cube(map, ebounds, exposure.gti()); // Create CTA observation GCTAObservation cta; cta.events(cube); cta.response(exposure, psf, background); // Append observation to container m_obs.append(cta); // Signal that we are in binned mode m_binned = true; // Signal that we appended a cube m_append_cube = true; } // endif: cube response information was available // ... otherwise build an observation from IRF response information else { // Create CTA observation GCTAObservation cta = create_cta_obs(); // Set response set_obs_response(&cta); // Append observation to container m_obs.append(cta); } } // endif: filename was "NONE" or "" // ... otherwise we have a file name else { // If file is a FITS file then create an empty CTA observation // and load file into observation if (gammalib::is_fits(filename)) { // Allocate empty CTA observation GCTAObservation cta; // Load data cta.load(filename); // Set response set_obs_response(&cta); // Append observation to container m_obs.append(cta); // Signal that no XML file should be used for storage m_use_xml = false; } // ... otherwise load file into observation container else { // Load observations from XML file m_obs.load(filename); // For all observations that have no response, set the response // from the task parameters set_response(m_obs); // Set observation boundary parameters (emin, emax, rad) set_obs_bounds(m_obs); // Signal that XML file should be used for storage m_use_xml = true; } // endelse: file was an XML file } // Return return; }
/***********************************************************************//** * @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; }
/***********************************************************************//** * @brief Convolve sky model with the instrument response * * @param[in] model Sky model. * @param[in] event Event. * @param[in] obs Observation. * @param[in] grad Should model gradients be computed? (default: true) * @return Event probability. * * Computes the event probability * * \f[ * P(p',E',t') = \int \int \int * S(p,E,t) \times R(p',E',t'|p,E,t) \, dp \, dE \, dt * \f] * * without taking into account any time dispersion. Energy dispersion is * correctly handled by this method. If time dispersion is indeed needed, * an instrument specific method needs to be provided. ***************************************************************************/ double GResponse::convolve(const GModelSky& model, const GEvent& event, const GObservation& obs, const bool& grad) const { // Set number of iterations for Romberg integration. static const int iter = 6; // Initialise result double prob = 0.0; // Continue only if the model has a spatial component if (model.spatial() != NULL) { // Get source time (no dispersion) GTime srcTime = event.time(); // Case A: Integration if (use_edisp()) { // Retrieve true energy boundaries GEbounds ebounds = this->ebounds(event.energy()); // Loop over all boundaries for (int i = 0; i < ebounds.size(); ++i) { // Get boundaries in MeV double emin = ebounds.emin(i).MeV(); double emax = ebounds.emax(i).MeV(); // Continue only if valid if (emax > emin) { // Setup integration function edisp_kern integrand(this, &obs, &model, &event, srcTime, grad); GIntegral integral(&integrand); // Set number of iterations integral.fixed_iter(iter); // Do Romberg integration emin = std::log(emin); emax = std::log(emax); prob += integral.romberg(emin, emax); } // endif: interval was valid } // endfor: looped over intervals } // Case B: No integration (assume no energy dispersion) else { // Get source energy (no dispersion) GEnergy srcEng = event.energy(); // Evaluate probability prob = eval_prob(model, event, srcEng, srcTime, obs, grad); } // Compile option: Check for NaN/Inf #if defined(G_NAN_CHECK) if (gammalib::is_notanumber(prob) || gammalib::is_infinite(prob)) { std::cout << "*** ERROR: GResponse::convolve:"; std::cout << " NaN/Inf encountered"; std::cout << " (prob=" << prob; std::cout << ", event=" << event; std::cout << ", srcTime=" << srcTime; std::cout << ")" << std::endl; } #endif } // endif: spatial component valid // Return probability return prob; }
/***********************************************************************//** * @brief Select events * * @param[in] obs CTA observation. * @param[in] filename File name. * * Select events from a FITS file by making use of the selection possibility * of the cfitsio library on loading a file. A selection string is created * from the specified criteria that is appended to the filename so that * cfitsio will automatically filter the event data. This selection string * is then applied when opening the FITS file. The opened FITS file is then * saved into a temporary file which is the loaded into the actual CTA * observation, overwriting the old CTA observation. The ROI, GTI and EBounds * of the CTA event list are then set accordingly to the specified selection. * Finally, the temporary file created during this process is removed. * * Good Time Intervals of the observation will be limited to the time * interval [m_tmin, m_tmax]. If m_tmin=m_tmax=0, no time selection is * performed. * * @todo Use INDEF instead of 0.0 for pointing as RA/DEC selection ***************************************************************************/ void ctselect::select_events(GCTAObservation* obs, const std::string& filename) { // Allocate selection string std::string selection; char cmin[80]; char cmax[80]; char cra[80]; char cdec[80]; char crad[80]; // Set requested selections bool select_time = (m_tmin != 0.0 || m_tmax != 0.0); // Set RA/DEC selection double ra = m_ra; double dec = m_dec; if (m_usepnt) { const GCTAPointing *pnt = obs->pointing(); ra = pnt->dir().ra_deg(); dec = pnt->dir().dec_deg(); } // Set time selection interval. We make sure here that the time selection // interval cannot be wider than the GTIs covering the data. This is done // using GGti's reduce() method. if (select_time) { // Reduce GTIs to specified time interval. The complicated cast is // necessary here because the gti() method is declared const, so // we're not officially allowed to modify the GTIs. ((GGti*)(&obs->events()->gti()))->reduce(m_timemin, m_timemax); } // endif: time selection was required // Save GTI for later usage GGti gti = obs->events()->gti(); // Make time selection if (select_time) { // Extract effective time interval in CTA reference time. We need // this reference for filtering. double tmin = gti.tstart().convert(m_cta_ref); double tmax = gti.tstop().convert(m_cta_ref); // Format time with sufficient accuracy and add to selection string sprintf(cmin, "%.8f", tmin); sprintf(cmax, "%.8f", tmax); selection = "TIME >= "+std::string(cmin)+" && TIME <= "+std::string(cmax); if (logTerse()) { log << parformat("Time range"); log << tmin << " - " << tmax << " s" << std::endl; } if (selection.length() > 0) { selection += " && "; } } // Make energy selection sprintf(cmin, "%.8f", m_emin); sprintf(cmax, "%.8f", m_emax); selection += "ENERGY >= "+std::string(cmin)+" && ENERGY <= "+std::string(cmax); if (logTerse()) { log << parformat("Energy range"); log << m_emin << " - " << m_emax << " TeV" << std::endl; } if (selection.length() > 0) { selection += " && "; } // Make ROI selection sprintf(cra, "%.6f", ra); sprintf(cdec, "%.6f", dec); sprintf(crad, "%.6f", m_rad); selection += "ANGSEP("+std::string(cra)+"," + std::string(cdec)+",RA,DEC) <= " + std::string(crad); if (logTerse()) { log << parformat("Acceptance cone centre"); log << "RA=" << ra << ", DEC=" << dec << " deg" << std::endl; log << parformat("Acceptance cone radius"); log << m_rad << " deg" << std::endl; } if (logTerse()) { log << parformat("cfitsio selection"); log << selection << std::endl; } // Add additional expression if (strip_whitespace(m_expr).length() > 0) { if (selection.length() > 0) { selection += " && "; } selection += "("+strip_whitespace(m_expr)+")"; } // Build input filename including selection expression std::string expression = filename; if (selection.length() > 0) expression += "[EVENTS]["+selection+"]"; if (logTerse()) { log << parformat("FITS filename"); log << expression << std::endl; } // Open FITS file GFits file(expression); // Log selected FITS file if (logExplicit()) { log << std::endl; log.header1("FITS file content after selection"); log << file << std::endl; } // Check if we have an EVENTS HDU if (!file.hashdu("EVENTS")) { std::string message = "No \"EVENTS\" extension found in FITS file "+ expression+"."; throw GException::app_error(G_SELECT_EVENTS, message); } // Determine number of events in EVENTS HDU int nevents = file.table("EVENTS")->nrows(); // If the selected event list is empty then append an empty event list // to the observation. Otherwise load the data from the temporary file. if (nevents < 1) { // Create empty event list GCTAEventList eventlist; // Append list to observation obs->events(&eventlist); } else { // Get temporary file name std::string tmpname = std::tmpnam(NULL); // Save FITS file to temporary file file.saveto(tmpname, true); // Load observation from temporary file obs->load_unbinned(tmpname); // Remove temporary file std::remove(tmpname.c_str()); } // Get CTA event list pointer GCTAEventList* list = static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events())); // Set ROI GCTARoi roi; GCTAInstDir instdir; instdir.radec_deg(ra, dec); roi.centre(instdir); roi.radius(m_rad); list->roi(roi); // Set GTI list->gti(gti); // Set energy boundaries GEbounds ebounds; GEnergy emin; GEnergy emax; emin.TeV(m_emin); emax.TeV(m_emax); ebounds.append(emin, emax); list->ebounds(ebounds); // Recompute ontime and livetime. GTime meantime = 0.5 * (gti.tstart() + gti.tstop()); obs->ontime(gti.ontime()); obs->livetime(gti.ontime() * obs->deadc(meantime)); // Return return; }