/***********************************************************************//** * @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; }
/***********************************************************************//** * @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 Test CTA npsf computation ***************************************************************************/ void TestGCTAResponse::test_response_npsf(void) { // Setup CTA response GCTAResponse rsp; rsp.caldb(cta_caldb); rsp.load(cta_irf); // Setup npsf computation GSkyDir srcDir; GEnergy srcEng; GTime srcTime; GCTAPointing pnt; GCTARoi roi; GCTAInstDir instDir; instDir.radec_deg(0.0, 0.0); roi.centre(instDir); roi.radius(2.0); srcEng.TeV(0.1); // Test PSF centred on ROI srcDir.radec_deg(0.0, 0.0); double npsf = rsp.npsf(srcDir, srcEng.log10TeV(), srcTime, pnt, roi); test_value(npsf, 1.0, 1.0e-3, "PSF(0,0) integration"); // Test PSF offset but inside ROI srcDir.radec_deg(1.0, 1.0); npsf = rsp.npsf(srcDir, srcEng.log10TeV(), srcTime, pnt, roi); test_value(npsf, 1.0, 1.0e-3, "PSF(1,1) integration"); // Test PSF outside and overlapping ROI srcDir.radec_deg(0.0, 2.0); npsf = rsp.npsf(srcDir, srcEng.log10TeV(), srcTime, pnt, roi); test_value(npsf, 0.492373, 1.0e-3, "PSF(0,2) integration"); // Test PSF outside ROI srcDir.radec_deg(2.0, 2.0); npsf = rsp.npsf(srcDir, srcEng.log10TeV(), srcTime, pnt, roi); test_value(npsf, 0.0, 1.0e-3, "PSF(2,2) integration"); // Return return; }
/***********************************************************************//** * @brief Test CTA Aeff computation ***************************************************************************/ void TestGCTAResponse::test_response_aeff(void) { // Load response GCTAResponse rsp; rsp.caldb(cta_caldb); rsp.load(cta_irf); // Sum over effective area for control GEnergy eng; double sum = 0.0; double ref = 154124059000.00006; //!< Adjust to actual value for (int i = 0; i < 30; ++i) { eng.TeV(pow(10.0, -1.7 + 0.1*double(i))); double aeff = rsp.aeff(0.0, 0.0, 0.0, 0.0, eng.log10TeV()); //std::cout << eng << " " << eng.log10TeV() << " " << aeff << std::endl; sum += aeff; } test_value(sum, ref, 0.1, "Effective area verification"); // 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 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 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; }