/*************************************************************************** * test_CTA.cpp - Test CTA classes * * ----------------------------------------------------------------------- * * copyright (C) 2010-2018 by Juergen Knoedlseder * * ----------------------------------------------------------------------- * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * * * ***************************************************************************/ /** * @file test_CTA.cpp * @brief Implementation of CTA classes unit tests * @author Juergen Knoedlseder */ /* __ Includes ___________________________________________________________ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include // getenv #include "GCTALib.hpp" #include "GTools.hpp" #include "GNodeArray.hpp" #include "test_CTA.hpp" #include "GCTAEdisp2D.hpp" #include "GCTAEdispPerfTable.hpp" #include "GCTAResponseTable.hpp" #include "GMath.hpp" /* __ Namespaces _________________________________________________________ */ /* __ Globals ____________________________________________________________ */ /* __ Constants __________________________________________________________ */ const std::string datadir = std::getenv("TEST_CTA_DATA"); const std::string caldbdir = datadir + "/../caldb"; const std::string cta_caldb = datadir + "/../../caldb"; const std::string cta_irf = "cta_dummy_irf"; const std::string cta_events = datadir+"/crab_events.fits"; const std::string cta_events_gti = datadir+"/crab_events_gti.fits[EVENTS2]"; const std::string cta_cntmap = datadir+"/crab_cntmap.fits"; const std::string cta_bin_xml = datadir+"/obs_binned.xml"; const std::string cta_unbin_xml = datadir+"/obs_unbinned.xml"; const std::string cta_model_xml = datadir+"/crab.xml"; const std::string cta_rsp_xml = datadir+"/rsp_models.xml"; const std::string cta_cube_bgd_xml = datadir+"/cta_model_cube_bgd.xml"; const std::string cta_irf_bgd_xml = datadir+"/cta_model_irf_bgd.xml"; const std::string cta_aeff_bgd_xml = datadir+"/cta_model_aeff_bgd.xml"; const std::string cta_caldb_king = cta_caldb+"/data/cta/e/bcf/IFAE20120510_50h_King"; const std::string cta_irf_king = "irf_file.fits"; const std::string cta_psf_table = caldbdir+"/psf_table.fits[PSF_2D_TABLE]"; const std::string cta_perf_table = caldbdir+"/cta_dummy_irf.dat"; const std::string cta_edisp_rmf = caldbdir+"/dc1/rmf.fits"; const std::string cta_edisp_2D = caldbdir+"/edisp_matrix.fits"; const std::string cta_bgd_3D = caldbdir+"/edisp_matrix.fits"; const std::string cta_modbck_fit = datadir+"/bg_test.fits"; const std::string cta_point_table = datadir+"/crab_pointing.fits"; /* __ Test files for stacked analysis (based on Prod2::South_0.5h) _______ */ const std::string cta_stacked_xml = datadir+"/stacked_obs.xml"; const std::string cta_stacked_model = datadir+"/stacked_model.xml"; const std::string cta_stacked_cntcube = datadir+"/stacked_cntcube.fits"; const std::string cta_stacked_expcube = datadir+"/stacked_expcube.fits"; const std::string cta_stacked_psfcube = datadir+"/stacked_psfcube.fits"; const std::string cta_stacked_edispcube = datadir+"/stacked_edispcube.fits"; const std::string cta_stacked_bkgcube = datadir+"/stacked_bkgcube.fits"; /* __ Test files for On/Off analysis _____________________________________ */ const std::string cta_onoff_obs = datadir+"/onoff_obs.xml"; const std::string cta_onoff_model = datadir+"/onoff_model.xml"; const std::string cta_onoff_onreg = datadir+"/onoff_region_on.reg"; /* __ Debug definitions __________________________________________________ */ //#define G_COMPUTE_REF_RATE_EBIN //!< Compute reference rate /***********************************************************************//** * @brief Set miscellaneous CTA classes test methods ***************************************************************************/ void TestGCTA::set(void) { // Set test name name("Miscellaneous CTA classes"); // Append GCTAInstDir tests to test suite append(static_cast(&TestGCTA::test_instdir), "Test GCTAInstDir class"); // Append GCTAPointing tests to test suite append(static_cast(&TestGCTA::test_pointing_load_table), "Test loading of table into GCTAPointing instance"); append(static_cast(&TestGCTA::test_pointing_interpolate_altaz), "Test alt/az interpolation given a time"); // Return return; } /***********************************************************************//** * @brief Set CTA response test methods ***************************************************************************/ void TestGCTAResponse::set(void) { // Set test name name("GCTAResponse"); // Append tests to test suite append(static_cast(&TestGCTAResponse::test_response), "Test response"); append(static_cast(&TestGCTAResponse::test_response_aeff), "Test effective area"); append(static_cast(&TestGCTAResponse::test_response_psf), "Test Gaussian point spread function"); append(static_cast(&TestGCTAResponse::test_response_psf_king), "Test King point spread function"); append(static_cast(&TestGCTAResponse::test_response_psf_table), "Test Table point spread function"); append(static_cast(&TestGCTAResponse::test_response_npsf), "Test integrated point spread function"); append(static_cast(&TestGCTAResponse::test_response_irf_diffuse), "Test diffuse IRF"); append(static_cast(&TestGCTAResponse::test_response_npred_diffuse), "Test diffuse IRF integration"); append(static_cast(&TestGCTAResponse::test_response_edisp), "Test GCTAEdisp class"); append(static_cast(&TestGCTAResponse::test_response_edisp_PerfTable), "Test GCTAEdispPerfTable class"); append(static_cast(&TestGCTAResponse::test_response_edisp_RMF), "Test GCTAEdispRmf class"); append(static_cast(&TestGCTAResponse::test_response_edisp_2D), "Test GCTAEdisp2D class"); append(static_cast(&TestGCTAResponse::test_response_bgd_PerfTable), "Test GCTABackgroundPerfTable class"); append(static_cast(&TestGCTAResponse::test_response_bgd_3D), "Test GCTABackground3D class"); append(static_cast(&TestGCTAResponse::test_response_expcube), "Test GCTACubeExposure class"); append(static_cast(&TestGCTAResponse::test_response_psfcube), "Test GCTACubePsf class"); append(static_cast(&TestGCTAResponse::test_response_edispcube), "Test GCTACubeEdisp class"); append(static_cast(&TestGCTAResponse::test_response_bkgcube), "Test GCTACubeBackground class"); // Return return; } /***********************************************************************//** * @brief Set CTA model test methods ***************************************************************************/ void TestGCTAModel::set(void) { // Set test name name("Test CTA models"); // Append tests to test suite append(static_cast(&TestGCTAModel::test_model_cube_bgd), "Test CTA cube background model"); append(static_cast(&TestGCTAModel::test_model_irf_bgd), "Test CTA IRF background model"); append(static_cast(&TestGCTAModel::test_model_aeff_bgd), "Test CTA Aeff background model"); // Return return; } /***********************************************************************//** * @brief Set CTA observation test methods ***************************************************************************/ void TestGCTAObservation::set(void) { // Set test name name("GCTAObservation"); // Append tests to test suite append(static_cast(&TestGCTAObservation::test_event_bin), "Test event bin"); append(static_cast(&TestGCTAObservation::test_event_cube), "Test event cube"); append(static_cast(&TestGCTAObservation::test_unbinned_obs), "Test unbinned observations"); append(static_cast(&TestGCTAObservation::test_binned_obs), "Test binned observation"); append(static_cast(&TestGCTAObservation::test_stacked_obs), "Test stacked observation"); append(static_cast(&TestGCTAObservation::test_onoff_obs), "Test On/Off observation"); // Return return; } /***********************************************************************//** * @brief Set CTA optimizer test methods ***************************************************************************/ void TestGCTAOptimize::set(void) { // Set test name name("CTA optimizers"); // Append tests to test suite append(static_cast(&TestGCTAOptimize::test_unbinned_optimizer), "Test unbinned optimizer"); append(static_cast(&TestGCTAOptimize::test_binned_optimizer), "Test binned optimizer"); append(static_cast(&TestGCTAOptimize::test_stacked_optimizer), "Test stacked optimizer"); append(static_cast(&TestGCTAOptimize::test_onoff_optimizer_cstat), "Test On/Off optimizer using CSTAT statistic"); append(static_cast(&TestGCTAOptimize::test_onoff_optimizer_wstat), "Test On/Off optimizer using WSTAT statistic"); // Return return; } /***********************************************************************//** * @brief Test GCTAInstDir class ***************************************************************************/ void TestGCTA::test_instdir(void) { // Check content of empty instance GCTAInstDir instdir1; test_value(instdir1.dir().ra(), 0.0, "Right Ascension of empty instance"); test_value(instdir1.dir().dec(), 0.0, "Declination of empty instance"); test_value(instdir1.detx(), 0.0, "DETX of empty instance"); test_value(instdir1.dety(), 0.0, "DETY of empty instance"); test_value(instdir1.theta(), 0.0, "Theta of empty instance"); test_value(instdir1.phi(), 0.0, "Phi of empty instance"); // Set content double ra = 83.6331 * gammalib::deg2rad; double dec = 22.0145 * gammalib::deg2rad; double detx = 0.5 * gammalib::deg2rad; double dety = 1.5 * gammalib::deg2rad; GSkyDir dir; dir.radec(ra, dec); instdir1.dir(dir); instdir1.detx(detx); instdir1.dety(dety); // Check content of filled instance test_value(instdir1.dir().ra(), ra, "Right Ascension of filled instance"); test_value(instdir1.dir().dec(), dec, "Declination of filled instance"); test_value(instdir1.detx(), detx, "DETX of filled instance"); test_value(instdir1.dety(), dety, "DETY of filled instance"); test_value(instdir1.theta(), 0.02759607852, "Theta of filled instance"); test_value(instdir1.phi(), 1.24904577, "Phi of filled instance"); // Check copying of instance GCTAInstDir instdir2(instdir1); test_value(instdir2.dir().ra(), ra, "Right Ascension of copied instance"); test_value(instdir2.dir().dec(), dec, "Declination of copied instance"); test_value(instdir2.detx(), detx, "DETX of copied instance"); test_value(instdir2.dety(), dety, "DETY of copied instance"); test_value(instdir2.theta(), 0.02759607852, "Theta of copied instance"); test_value(instdir2.phi(), 1.24904577, "Phi of copied instance"); // Return return; } /***********************************************************************//** * @brief Test ability to load a CTA pointing table ***************************************************************************/ void TestGCTA::test_pointing_load_table(void) { // Allocate classes GCTAPointing pnt; // Load pointing table pnt.load(cta_point_table); // Return return; } /***********************************************************************//** * @brief Test interpolation of alt/az pointing dir as a function of time ***************************************************************************/ void TestGCTA::test_pointing_interpolate_altaz(void) { // Allocate classes GCTAObservation run; GCTAPointing pnt; // Load pointing table pnt.load(cta_point_table); // Test an out-of bounds time test_try("Test an out-of bounds time"); try { GTime time(155470378.7, "sec"); GHorizDir dir = pnt.dir_horiz(time); test_try_failure(); } catch (GException::out_of_range &exc) { test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Get a time that is somewhere in the run: GTime time(-128526000.0, "sec"); GHorizDir dir = pnt.dir_horiz( time ); // Return return; } /***********************************************************************//** * @brief Test CTA response handling ***************************************************************************/ void TestGCTAResponse::test_response(void) { // Test CTA IRF response constructor test_try("Test CTA IRF response constructor"); try { GCTAResponseIrf rsp; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test CTA IRF response loading test_try("Test CTA IRF response loading"); try { // Load response GCTAResponseIrf rsp; rsp.caldb(GCaldb(cta_caldb)); rsp.load(cta_irf); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test CTA cube response constructors test_try("Test CTA cube response void constructor"); try { GCTAResponseCube rsp; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } test_try("Test CTA cube response constructor"); try { GCTACubeExposure exposure; GCTACubePsf psf; GCTACubeBackground background; GCTAResponseCube rsp(exposure, psf, background); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Return return; } /***********************************************************************//** * @brief Test CTA Aeff computation ***************************************************************************/ void TestGCTAResponse::test_response_aeff(void) { // Load response GCTAResponseIrf rsp; rsp.caldb(GCaldb(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()); sum += aeff; } test_value(sum, ref, 0.1, "Effective area verification"); // 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 GCTAResponseIrf rsp; rsp.caldb(GCaldb(cta_caldb)); rsp.load(cta_irf); // Integrate Psf GEnergy eng; double sum_check = 0.1; for (double e = 0.1; e < 10.0; e *= 2.0) { eng.TeV(e); double r = 0.0; double dr = 0.00001; int steps = int(1.0/dr); double sum = 0.0; double rcont = 0.0; for (int i = 0; i < steps; ++i) { r += dr; sum += rsp.psf(r * gammalib::deg2rad, 0.0, 0.0, 0.0, 0.0, eng.log10TeV()) * gammalib::twopi * std::sin(r * gammalib::deg2rad) * dr * gammalib::deg2rad; // since 'sum' already totals to 1.0, its also a 'fraction', // which we can plug back into containment_radius(), to compare // with the origial radius 'r' if (sum > sum_check && sum < 1.0) { rcont = rsp.psf()->containment_radius(sum, eng.log10TeV()); sum_check += 0.1; std::string msg = "PSF containment radius for "+eng.print()+ " and "+gammalib::str(sum*100.0)+ "% containment"; test_value(rcont, r*gammalib::deg2rad, 0.1, msg); } } test_value(sum, 1.0, 0.001, "PSF integration for "+eng.print()); } // Return return; } /***********************************************************************//** * @brief Test CTA King profile 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_king(void) { // Load response GCTAResponseIrf rsp; rsp.caldb(GCaldb(cta_caldb_king)); rsp.load(cta_irf_king); // Integrate Psf GEnergy eng; double sum_check = 0.1; for (double e = 0.1; e < 10.0; e *= 2.0) { eng.TeV(e); double r_max = rsp.psf()->delta_max(eng.log10TeV()) * gammalib::rad2deg; double r = 0.0; double dr = 0.00001; int steps = int(r_max / dr); double sum = 0.0; double rcont = 0.0 ; for (int i = 0; i < steps; ++i) { r += dr; sum += rsp.psf(r * gammalib::deg2rad, 0.0, 0.0, 0.0, 0.0, eng.log10TeV()) * gammalib::twopi * std::sin(r * gammalib::deg2rad) * dr * gammalib::deg2rad; // since 'sum' already totals to 1.0, its also a 'fraction', // which we can plug back into containment_radius(), to compare // with the origial radius 'r' if (sum > sum_check && sum < 1.0) { rcont = rsp.psf()->containment_radius(sum, eng.log10TeV()); sum_check += 0.1; std::string msg = "King PSF containment radius for "+eng.print()+ " and "+gammalib::str(sum*100.0)+ "% containment"; test_value(rcont, r*gammalib::deg2rad, 0.1, msg); } } test_value(sum, 1.0, 0.005, "PSF integration for "+eng.print()); } // Return return; } /***********************************************************************//** * @brief Test table point spread function * * 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_table(void) { // Load point spread function GCTAPsfTable psf(cta_psf_table); // Integrate PSF GEnergy eng; double sum_check = 0.1; for (double e = 0.1; e < 10.0; e *= 2.0) { eng.TeV(e); double r_max = psf.delta_max(eng.log10TeV()) * gammalib::rad2deg; double r = 0.0; double dr = 0.00001; int steps = int(r_max / dr); double sum = 0.0; double rcont = 0.0 ; for (int i = 0; i < steps; ++i) { r += dr; sum += psf(r * gammalib::deg2rad, eng.log10TeV()) * gammalib::twopi * std::sin(r * gammalib::deg2rad) * dr * gammalib::deg2rad; // since 'sum' already totals to 1.0, its also a 'fraction', // which we can plug back into containment_radius(), to compare // with the origial radius 'r' if (sum > sum_check && sum < 1.0) { rcont = psf.containment_radius(sum, eng.log10TeV()); sum_check += 0.1; std::string msg = "Table PSF containment radius for "+eng.print()+ " and "+gammalib::str(sum*100.0)+ "% containment"; test_value(rcont, r*gammalib::deg2rad, 0.1, msg); } } test_value(sum, 1.0, 0.01, "PSF integration for "+eng.print()); } // Return return; } /***********************************************************************//** * @brief Test CTA npsf computation ***************************************************************************/ void TestGCTAResponse::test_response_npsf(void) { // Setup CTA response GCTAResponseIrf rsp; rsp.caldb(GCaldb(cta_caldb)); rsp.load(cta_irf); // Setup npsf computation GSkyDir srcDir; GEnergy srcEng; GTime srcTime; GCTAPointing pnt; GCTARoi roi; GCTAInstDir instDir; instDir.dir().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 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; //const double ref = 13803.5186374; //const double ref = 13803.6932774; // after GWcs::solidangle improvement //const double ref = 13803.3453994; // after Irf computation improvements //const double ref = 13803.3461768; // using skymap flux() method const double ref = 13803.5251338; // after correcting contains() method // 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 GEnergy emin(0.1, "TeV"); GEnergy emax(100.0, "TeV"); GEbounds ebounds(nebins, emin, emax); // Setup event cube centreed 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, GCaldb(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(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, 0.2, "Diffuse IRF computation"); // Return return; } /***********************************************************************//** * @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 //const double ref = 11212.26274; // npred_spec precision of 1e-6 //const double ref = 11212.437464; // npred_spec precision of 1e-5 //const double ref = 11212.4370702; // After GWcs::solidangle improvement //const double ref = 12644.3391902; // After correcting for deadtime bug //const double ref = 12643.6159142; // npred_spec precision of 1e-6 //const double ref = 10997.0851335; // After response computation change //const double ref = 11212.7161901; // npred_diffuse precision to iter=8 //const double ref = 11238.6928076; // npred_diffuse precision to iter=9 //const double ref = 11238.6933328; // using skymap flux() method const double ref = 11238.8390288; // after correcting contains() method // 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.dir().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(0.1, "TeV"); GEnergy emax(100.0, "TeV"); 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, GCaldb(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, 0.2, "Diffuse Npred computation"); // Return return; } /***********************************************************************//** * @brief Test CTA Energy Dispersion computation * * The Energy Dispersion computation is tested by integrating numerically * the edisp function. Integration is done in a rather simplistic way, by * stepping through the energy range. The integration is done for a set of * true energies from 0.1-10 TeV. ***************************************************************************/ void TestGCTAResponse::test_response_edisp(void) { // Load response GCTAResponseIrf rsp; // Set performance table response rsp.caldb(GCaldb(cta_caldb)); rsp.load(cta_irf); // Test energy dispersion integration test_response_edisp_integration(rsp); // Return return; } /***********************************************************************//** * @brief Test CTA performance table energy dispersion class ***************************************************************************/ void TestGCTAResponse::test_response_edisp_PerfTable(void) { // Test GCTAEdispPerfTable constructor test_try("GCTAEdispPerfTable constructor"); try { GCTAEdispPerfTable test(cta_perf_table); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Load energy dispersion GCTAEdispPerfTable edisp(cta_perf_table); // Test ebounds_obs and ebounds_src methods test_try("GCTAEdispPerfTable ebounds_obs and ebounds_src methods"); try { //std::cout << "TEST EBOUNDS PERFTABLE" << std::endl; // ebounds_obs (for a given logEsrc) //std::cout << edisp.ebounds_obs(-1.0).print(EXPLICIT) << std::endl; // ebounds_src (for a given logEobs) //std::cout << edisp.ebounds_src(2.0).print(EXPLICIT) << std::endl; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test mc method test_try("GCTAEdispPerfTable mc method"); try { GRan ran; //std::cout << "TEST MC PERFTABLE" << std::endl; for (int i = 0; i < 20; ++i) { //std::cout << edisp.mc(ran, -1.0).GeV() << std::endl; } test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test normalisation test_edisp_integration(edisp); // Return return; } /***********************************************************************//** * @brief Test CTA RMF energy dispersion class ***************************************************************************/ void TestGCTAResponse::test_response_edisp_RMF(void) { // Test void constructor test_try("GRmf void constructor"); try { GRmf rmf; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test source energy boundary constructor test_try("GRmf energy boundary constructor"); try { GEbounds ebounds_src(10, GEnergy(0.1, "TeV"), GEnergy(10.0, "TeV")); GRmf rmf(ebounds_src, ebounds_src); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test observed energy boundary constructor test_try("GRmf energy boundary constructor"); try { GEbounds ebounds_obs(10, GEnergy(0.1, "TeV"), GEnergy(10.0, "TeV")); GRmf rmf(ebounds_obs, ebounds_obs); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Load RMF GCTAEdispRmf edisp(cta_edisp_rmf); // Test if non-diagonal element (below diagonal) is zero test_value(edisp(std::log10(30.0), std::log10(1.0)), 0.0); // Test that diagonal element is non-zero //test_value(edisp(std::log10(30.0),std::log10(30.0)), 0.354952753 ,1e-9); // Test if non-diagonal element (above diagonal) is zero test_value(edisp(std::log10(1.0), std::log10(30.0)), 0.0); // Test normalisation test_edisp_integration(edisp, 0.5, 10.0); // Return return; } /***********************************************************************//** * @brief Test CTA 2D energy dispersion class ***************************************************************************/ void TestGCTAResponse::test_response_edisp_2D(void) { // Test void constructor test_try("GCTAEdisp2D void constructor"); try { GCTAEdisp2D edisp; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test GCTAEdisp2D file constructor test_try("GCTAEdisp2D file constructor"); try { GCTAEdisp2D edisp(cta_edisp_2D); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Load 2D energy dispersion GCTAEdisp2D edisp(cta_edisp_2D); // Test normalisation test_edisp_integration(edisp); // Return return; } /***********************************************************************//** * @brief Test GCTABackgroundPerfTable class ***************************************************************************/ void TestGCTAResponse::test_response_bgd_PerfTable(void) { // Set reference values const double ref_rate_below = 0.000612295557409636; const double ref_rate_above = 5.74072516267095e-11; const double ref_rate_onaxis = 3.62782669235078e-05; const double ref_rate_offaxis = 3.12350491143832e-05; const double ref_rate_ebin_within = 44.9181299290927; const double ref_rate_ebin_below = 3676.68195289697; const double ref_rate_ebin_above = 76.2328310391366; // Set energies for energy range for rate integration const GEnergy ebelow(10.0, "GeV"); const GEnergy emin(1.0, "TeV"); const GEnergy emax(10.0, "TeV"); const GEnergy eabove(200.0, "TeV"); // Test GCTABackgroundPerfTable constructor test_try("GCTABackgroundPerfTable constructor"); try { GCTABackgroundPerfTable bgd1(cta_perf_table); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Check content of empty instance GCTABackgroundPerfTable bgd1; test_value(bgd1.size(), 0, "Nodes of empty instance"); test_value(bgd1.sigma(), 3.0, "Sigma value of empty instance"); test_value(bgd1.filename(), "", "Filename of empty instance"); test_value(bgd1(0.0, 0.0, 0.0), 0.0, "Onaxis operator value of empty instance"); test_value(bgd1(0.0, 0.01, 0.02), 0.0, "Offaxis operator value of empty instance"); test_value(bgd1.rate_ebin(GCTAInstDir(), emin, emax), 0.0, "Integrated rate value of empty instance"); // Check content of filled instance GCTABackgroundPerfTable bgd2(cta_perf_table); test_value(bgd2.size(), 20, "Nodes of filled instance"); test_value(bgd2.sigma(), 3.0, "Sigma value of filled instance"); test_value(bgd2.filename(), cta_perf_table, "Filename of filled instance"); test_value(bgd2(-2.0, 0.0, 0.0), ref_rate_below, "Operator value of filled instance extrapolated below lowest energy"); test_value(bgd2(3.0, 0.0, 0.0), ref_rate_above, "Operator value of filled instance extrapolated above highest energy"); test_value(bgd2(0.0, 0.0, 0.0), ref_rate_onaxis, "Onaxis operator value of filled instance"); test_value(bgd2(0.0, 0.01, 0.02), ref_rate_offaxis, "Offaxis operator value of filled instance"); test_value(bgd2.rate_ebin(GCTAInstDir(), emin, emax), ref_rate_ebin_within, "Integrated rate value of filled instance (within covered energies)"); test_value(bgd2.rate_ebin(GCTAInstDir(), ebelow, emax), ref_rate_ebin_below, "Integrated rate value of filled instance (extrapolate below lowest energy)"); test_value(bgd2.rate_ebin(GCTAInstDir(), emin, eabove), ref_rate_ebin_above, "Integrated rate value of filled instance (extrapolate above highest energy)"); test_value(bgd2.rate_ebin(GCTAInstDir(), emin, emin), 0.0, "Integrated rate value of filled instance for zero interval"); // Check content of copied instance GCTABackgroundPerfTable bgd3(bgd2); test_value(bgd3.size(), 20, "Nodes of copied instance"); test_value(bgd3.sigma(), 3.0, "Sigma value of copied instance"); test_value(bgd3.filename(), cta_perf_table, "Filename of copied instance"); test_value(bgd3(-2.0, 0.0, 0.0), ref_rate_below, "Operator value of copied instance extrapolated below lowest energy"); test_value(bgd3(3.0, 0.0, 0.0), ref_rate_above, "Operator value of copied instance extrapolated above highest energy"); test_value(bgd3(0.0, 0.0, 0.0), ref_rate_onaxis, "Onaxis operator value of copied instance"); test_value(bgd3(0.0, 0.01, 0.02), ref_rate_offaxis, "Offaxis operator value of copied instance"); test_value(bgd3.rate_ebin(GCTAInstDir(), emin, emax), ref_rate_ebin_within, "Integrated rate value of copied instance"); test_value(bgd3.rate_ebin(GCTAInstDir(), emin, emin), 0.0, "Integrated rate value of copied instance for zero interval"); // Compute reference rate #if defined(G_COMPUTE_REF_RATE_EBIN) double rate = 0.0; int nbins = 10000; GEnergy E = emin; GEnergy dE = (eabove-emin)/nbins; for (int i = 0; i < nbins; ++i) { rate += bgd2(E.log10TeV(), 0.0, 0.0) * dE.MeV(); E += dE; } std::cout << rate << std::endl; #endif // Return return; } /***********************************************************************//** * @brief Test GCTABackground3D class ***************************************************************************/ void TestGCTAResponse::test_response_bgd_3D(void) { // Set reference values const double ref_rate_below = 0.0; const double ref_rate_above = 0.0; const double ref_rate_onaxis = 9.7632195934345e-05; const double ref_rate_offaxis = 13.5162501643753e-05; const double ref_rate_ebin_within = 65.6069433285784; const double ref_rate_ebin_below = 4647.37614735018; const double ref_rate_ebin_above = 69.5331125123672; // Set energies for energy range for rate integration const GEnergy ebelow(10.0, "GeV"); const GEnergy emin(1.0, "TeV"); const GEnergy emax(10.0, "TeV"); const GEnergy eabove(200.0, "TeV"); // Test GCTABackground3D constructor test_try("GCTABackground3D constructor"); try { GCTABackground3D bgd1(cta_bgd_3D); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Check content of empty instance GCTABackground3D bgd1; test_value(bgd1.table().axes(), 0, "Response table axes of empty instance"); test_value(bgd1.filename(), "", "Filename of empty instance"); test_value(bgd1(0.0, 0.0, 0.0), 0.0, "Onaxis operator value of empty instance"); test_value(bgd1(0.0, 0.01, 0.02), 0.0, "Offaxis operator value of empty instance"); test_value(bgd1.rate_ebin(GCTAInstDir(), emin, emax), 0.0, "Integrated rate value of empty instance"); // Check content of filled instance GCTABackground3D bgd2(cta_bgd_3D); test_value(bgd2.table().axes(), 3, "Response table axes of filled instance"); test_value(bgd2.filename(), cta_bgd_3D, "Filename of filled instance"); test_value(bgd2(-2.0, 0.0, 0.0), ref_rate_below, "Operator value of filled instance extrapolated below lowest energy"); test_value(bgd2(3.0, 0.0, 0.0), ref_rate_above, "Operator value of filled instance extrapolated above highest energy"); test_value(bgd2(0.0, 0.0, 0.0), ref_rate_onaxis, "Onaxis operator value of filled instance"); test_value(bgd2(0.0, 0.01, 0.02), ref_rate_offaxis, "Offaxis operator value of filled instance"); test_value(bgd2.rate_ebin(GCTAInstDir(), emin, emax), ref_rate_ebin_within, "Integrated rate value of filled instance"); test_value(bgd2.rate_ebin(GCTAInstDir(), ebelow, emax), ref_rate_ebin_below, "Integrated rate value of filled instance (extrapolate below lowest energy)"); test_value(bgd2.rate_ebin(GCTAInstDir(), emin, eabove), ref_rate_ebin_above, "Integrated rate value of filled instance (extrapolate above highest energy)"); test_value(bgd2.rate_ebin(GCTAInstDir(), emin, emin), 0.0, "Integrated rate value of filled instance for zero interval"); // Check content of copied instance GCTABackground3D bgd3(bgd2); test_value(bgd3.table().axes(), 3, "Response table axes of copied instance"); test_value(bgd3.filename(), cta_bgd_3D, "Filename of copied instance"); test_value(bgd3(-2.0, 0.0, 0.0), ref_rate_below, "Operator value of copied instance extrapolated below lowest energy"); test_value(bgd3(3.0, 0.0, 0.0), ref_rate_above, "Operator value of copied instance extrapolated above highest energy"); test_value(bgd3(0.0, 0.0, 0.0), ref_rate_onaxis, "Onaxis operator value of copied instance"); test_value(bgd3(0.0, 0.01, 0.02), ref_rate_offaxis, "Offaxis operator value of copied instance"); test_value(bgd3.rate_ebin(GCTAInstDir(), emin, emax), ref_rate_ebin_within, "Integrated rate value of copied instance"); test_value(bgd3.rate_ebin(GCTAInstDir(), emin, emin), 0.0, "Integrated rate value of copied instance for zero interval"); // Compute reference rate #if defined(G_COMPUTE_REF_RATE_EBIN) double rate = 0.0; int nbins = 10000; GEnergy E = emin; GEnergy dE = (eabove-E)/nbins; for (int i = 0; i < nbins; ++i) { rate += bgd2(E.log10TeV(), 0.0, 0.0) * dE.MeV(); E += dE; } std::cout << rate << std::endl; #endif // Return return; } /***********************************************************************//** * @brief Test exposure cube handling ***************************************************************************/ void TestGCTAResponse::test_response_expcube(void) { // Test exposure cube constructors test_try("CTA exposure cube void constructor"); try { GCTACubeExposure cube; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } test_try("CTA exposure cube map constructor"); try { GEnergies energies(20, GEnergy(0.1, "TeV"), GEnergy(100.0, "TeV")); GCTACubeExposure cube("CAR", "CEL", 83.63, 22.01, 0.02, 0.02, 200, 200, energies); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test set method GCTAObservation obs_cta; obs_cta.load(cta_events); obs_cta.response(cta_irf, GCaldb(cta_caldb)); GEnergies energies(20, GEnergy(0.1, "TeV"), GEnergy(100.0, "TeV")); GCTACubeExposure cube("CAR", "CEL", 83.63, 22.01, 0.02, 0.02, 200, 200, energies); cube.set(obs_cta); cube.save("test_cta_expcube_one.fits", true); // Test fill method GObservations obs; obs_cta.id("000001"); obs.append(obs_cta); obs_cta.id("000002"); obs.append(obs_cta); cube.fill(obs); cube.save("test_cta_expcube_two.fits", true); // Return return; } /***********************************************************************//** * @brief Test PSF cube handling ***************************************************************************/ void TestGCTAResponse::test_response_psfcube(void) { // Test PSF cube constructors test_try("CTA PSF cube void constructor"); try { GCTACubePsf cube; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } test_try("CTA PSF cube map constructor"); try { GEnergies energies(20, GEnergy(0.1, "TeV"), GEnergy(100.0, "TeV")); GCTACubePsf cube("CAR", "CEL", 83.63, 22.01, 0.4, 0.4, 10, 10, energies, 0.1, 20); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test set method GCTAObservation obs_cta; obs_cta.load(cta_events); obs_cta.response(cta_irf, GCaldb(cta_caldb)); GEnergies energies(20, GEnergy(0.1, "TeV"), GEnergy(100.0, "TeV")); GCTACubePsf cube("CAR", "CEL", 83.63, 22.01, 0.4, 0.4, 10, 10, energies, 0.1, 20); cube.set(obs_cta); cube.save("test_cta_psfcube_one.fits", true); // Test fill method GObservations obs; obs_cta.id("000001"); obs.append(obs_cta); obs_cta.id("000002"); obs.append(obs_cta); cube.fill(obs); cube.save("test_cta_psfcube_two.fits", true); // Return return; } /***********************************************************************//** * @brief Test background cube handling ***************************************************************************/ void TestGCTAResponse::test_response_bkgcube(void) { // Test background cube constructors test_try("CTA background cube void constructor"); try { GCTACubeBackground cube; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } GCTACubeBackground cube; cube.load(cta_stacked_bkgcube); cube.save("test_cta_bkgcube.fits", true); // Return return; } /***********************************************************************//** * @brief Test energy dispersion cube handling ***************************************************************************/ void TestGCTAResponse::test_response_edispcube(void) { // Test PSF cube constructors test_try("CTA Edisp cube void constructor"); try { GCTACubeEdisp cube; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } test_try("CTA Edisp cube map constructor"); try { GEnergies energies(20, GEnergy(0.1, "TeV"), GEnergy(100.0, "TeV")); GCTACubeEdisp cube("CAR", "CEL", 83.63, 22.01, 0.4, 0.4, 10, 10, energies, 2.0, 20); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test set method GCTAObservation obs_cta; obs_cta.load(cta_events); obs_cta.response(cta_irf, GCaldb(cta_caldb)); GEnergies energies(20, GEnergy(0.1, "TeV"), GEnergy(100.0, "TeV")); GCTACubeEdisp cube("CAR", "CEL", 83.63, 22.01, 0.4, 0.4, 10, 10, energies, 2.0, 20); cube.set(obs_cta); cube.save("test_cta_edispcube_one.fits", true); // Test fill method GObservations obs; obs_cta.id("000001"); obs.append(obs_cta); obs_cta.id("000002"); obs.append(obs_cta); cube.fill(obs); cube.save("test_cta_edispcube_two.fits", true); // Return return; } /***********************************************************************//** * @brief Utility function for energy dispersion tests * * @param[in] rsp Response * @param[in] e_src_min Minimum true energy (TeV) (default: 0.1) * @param[in] e_src_max Maximum true energy (TeV) (default: 10.0) ***************************************************************************/ void TestGCTAResponse::test_response_edisp_integration(const GCTAResponseIrf& rsp, const double& e_src_min, const double& e_src_max) { // Continue only if energy dispersion is available if (rsp.edisp() != NULL) { // Loop over source energies for (double e_src = e_src_min; e_src < e_src_max; e_src *= 2.0) { // Compute log10 of true energy double log10_e_src = std::log10(e_src); // Retrieve boundaries in observed energy GEbounds ebounds = rsp.edisp()->ebounds_obs(log10_e_src); GEnergy emin = ebounds.emin(); GEnergy emax = ebounds.emax(); double logE_min = std::log10(emin.TeV()); double logE_max = std::log10(emax.TeV()); // Compute step size for numerical integration const int steps = 1000; double dlogE = (logE_max-logE_min)/steps; // Perform numerical integration by summing double sum = 0.0; double logE_obs = logE_min; for (int i = 0; i < steps; ++i) { double dp_dlogE = (*rsp.edisp())(logE_obs, log10_e_src); sum += dp_dlogE * dlogE; logE_obs += dlogE; } GEnergy eng(e_src, "TeV"); test_value(sum, 1.0, 0.001, "Energy Dispersion integration for "+eng.print()); // And now in linear energies, test GCTAResponse::edisp() method const int nE = 10000; double E_min = emin.MeV(); double E_max = emax.MeV(); double dE = (E_max-E_min)/nE; double sum2 = 0.0; double E_obs = E_min; for (int i = 0; i < nE; ++i) { double dp_dE = rsp.edisp(GEnergy(E_obs,"MeV"), 0.0, 0.0, 0.0, 0.0, log10_e_src); sum2 += dp_dE * dE; E_obs += dE; } test_value(sum2, 1.0, 0.001, "Energy Dispersion integration for "+eng.print()); } } // endif: energy dispersion was available // Return return; } /***********************************************************************//** * @brief Utility function for energy dispersion integration tests * * @param[in] edisp Energy dispersion * @param[in] e_src_min Minimum true energy (TeV) (default: 0.1) * @param[in] e_src_max Maximum true energy (TeV) (default: 10.0) ***************************************************************************/ void TestGCTAResponse::test_edisp_integration(const GCTAEdisp& edisp, const double& e_src_min, const double& e_src_max) { // Loop over source energies for (double e_src = e_src_min; e_src <= e_src_max; e_src *= 2.0) { // Compute log10 of true energy double log10_e_src = std::log10(e_src); // Retrieve boundaries in observed energy GEbounds ebounds = edisp.ebounds_obs(log10_e_src); // Skip this energy if the boundaries are empty if (ebounds.is_empty()) { continue; } // Set measured energy range GEnergy emin = ebounds.emin(); GEnergy emax = ebounds.emax(); double logE_min = std::log10(emin.TeV()); double logE_max = std::log10(emax.TeV()); // Compute step size for numerical integration const int steps = 1000; double dlogE = (logE_max-logE_min)/steps; // Perform numerical integration by summing double sum = 0.0; double logE_obs = logE_min; for (int i = 0; i <= steps; ++i) { double dp_dlogE = edisp(logE_obs, log10_e_src); sum += dp_dlogE * dlogE; logE_obs += dlogE; } // Set message string GEnergy eng(e_src, "TeV"); std::string msg = edisp.classname()+" integration for "+eng.print(); test_value(sum, 1.0, 0.03, msg); } // Return return; } /***********************************************************************//** * @brief Test CTA cube background ***************************************************************************/ void TestGCTAModel::test_model_cube_bgd(void) { // Test void constuctor test_try("Test void constuctor"); try { GCTAModelCubeBackground model; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test XML constuctor test_try("Test XML constuctor"); try { GXml xml(cta_cube_bgd_xml); const GXmlElement& lib = *xml.element("source_library", 0); const GXmlElement& src = *lib.element("source", 0); GCTAModelCubeBackground model(src); test_value(model["Prefactor"].value(), 1.0); test_value(model["Index"].value(), 0.0); test_value(model["PivotEnergy"].value(), 1.0e6); test_assert(model.is_constant(), "Model is expected to be constant."); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test spectral constuctor test_try("Test spectral constuctor"); try { GModelSpectralPlaw plaw(1.0, 0.0, GEnergy(1.0, "TeV")); GCTAModelCubeBackground model(plaw); test_value(model["Prefactor"].value(), 1.0); test_value(model["Index"].value(), 0.0); test_value(model["PivotEnergy"].value(), 1.0e6); test_assert(model.is_constant(), "Model is expected to be constant."); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test XML loading of cube background GModels models(cta_cube_bgd_xml); GModel* model = models["CTABackgroundModel"]; test_value((*model)["Prefactor"].value(), 1.0); test_value((*model)["Index"].value(), 0.0); test_value((*model)["PivotEnergy"].value(), 1.0e6); test_assert(model->is_constant(), "Model is expected to be constant."); // Test XML saving and reloading of cube background models.save("test.xml"); models.load("test.xml"); model = models["CTABackgroundModel"]; test_value((*model)["Prefactor"].value(), 1.0); test_value((*model)["Index"].value(), 0.0); test_value((*model)["PivotEnergy"].value(), 1.0e6); test_assert(model->is_constant(), "Model is expected to be constant."); // Return return; } /***********************************************************************//** * @brief Test CTA IRF background model ***************************************************************************/ void TestGCTAModel::test_model_irf_bgd(void) { // Test void constuctor test_try("Test void constuctor"); try { GCTAModelIrfBackground model; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test XML constuctor test_try("Test XML constuctor"); try { GXml xml(cta_irf_bgd_xml); const GXmlElement& lib = *xml.element("source_library", 0); const GXmlElement& src = *lib.element("source", 0); GCTAModelIrfBackground model(src); test_value(model["Prefactor"].value(), 1.0); test_value(model["Index"].value(), 0.0); test_value(model["PivotEnergy"].value(), 1.0e6); test_assert(model.is_constant(), "Model is expected to be constant."); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test spectral constuctor test_try("Test spectral constuctor"); try { GModelSpectralPlaw plaw(1.0, 0.0, GEnergy(1.0, "TeV")); GCTAModelIrfBackground model(plaw); test_value(model["Prefactor"].value(), 1.0); test_value(model["Index"].value(), 0.0); test_value(model["PivotEnergy"].value(), 1.0e6); test_assert(model.is_constant(), "Model is expected to be constant."); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test XML loading of instrumental background GModels models(cta_irf_bgd_xml); GModel* model = models["My model"]; test_value((*model)["Prefactor"].value(), 1.0); test_value((*model)["Index"].value(), 0.0); test_value((*model)["PivotEnergy"].value(), 1.0e6); test_assert(model->is_constant(), "Model is expected to be constant."); // Test XML saving and reloading of instrumental background models.save("test.xml"); models.load("test.xml"); model = models["My model"]; test_value((*model)["Prefactor"].value(), 1.0); test_value((*model)["Index"].value(), 0.0); test_value((*model)["PivotEnergy"].value(), 1.0e6); test_assert(model->is_constant(), "Model is expected to be constant."); // Return return; } /***********************************************************************//** * @brief Test CTA IRF background model ***************************************************************************/ void TestGCTAModel::test_model_aeff_bgd(void) { // Test void constuctor test_try("Test void constuctor"); try { GCTAModelAeffBackground model; test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test XML constuctor test_try("Test XML constuctor"); try { GXml xml(cta_aeff_bgd_xml); const GXmlElement& lib = *xml.element("source_library", 0); const GXmlElement& src = *lib.element("source", 0); GCTAModelIrfBackground model(src); test_value(model["Prefactor"].value(), 1.0e-14); test_value(model["Index"].value(), -2.4); test_value(model["PivotEnergy"].value(), 1.0e6); test_assert(model.is_constant(), "Model is expected to be constant."); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test spectral constuctor test_try("Test spectral constuctor"); try { GModelSpectralPlaw plaw(1.0e-14, -2.4, GEnergy(1.0, "TeV")); GCTAModelAeffBackground model(plaw); test_value(model["Prefactor"].value(), 1.0e-14); test_value(model["Index"].value(), -2.4); test_value(model["PivotEnergy"].value(), 1.0e6); test_assert(model.is_constant(), "Model is expected to be constant."); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test XML loading of instrumental background GModels models(cta_aeff_bgd_xml); GModel* model = models["My model"]; test_value((*model)["Prefactor"].value(), 1e-14); test_value((*model)["Index"].value(), -2.4); test_value((*model)["PivotEnergy"].value(), 1.0e6); test_assert(model->is_constant(), "Model is expected to be constant."); // Test XML saving and reloading of instrumental background models.save("test.xml"); models.load("test.xml"); model = models["My model"]; test_value((*model)["Prefactor"].value(), 1.0e-14); test_value((*model)["Index"].value(), -2.4); test_value((*model)["PivotEnergy"].value(), 1.0e6); test_assert(model->is_constant(), "Model is expected to be constant."); // Return return; } /***********************************************************************//** * @brief Checks handling of CTA event bin ***************************************************************************/ void TestGCTAObservation::test_event_bin(void) { // Test event bin void constructor GCTAEventBin bin; test_value(bin.classname(), "GCTAEventBin", "Check classname() for empty bin"); test_value(bin.size(), 0.0, 1.0e-10, "Check size() for empty bin"); test_value(bin.dir().dir().ra_deg(), 0.0, 1.0e-10, "Check dir().dir().ra_deg() for empty bin"); test_value(bin.dir().dir().dec_deg(), 0.0, 1.0e-10, "Check dir().dir().dec_deg() for empty bin"); test_value(bin.energy().MeV(), 0.0, 1.0e-10, "Check energy() for empty bin"); test_value(bin.time().secs(), 0.0, 1.0e-10, "Check time() for empty bin"); test_value(bin.counts(), 0.0, 1.0e-10, "Check counts() for empty bin"); test_value(bin.error(), 0.0, 1.0e-10, "Check error() for empty bin"); test_value(bin.ipix(), -1, "Check ipix() for empty bin"); test_value(bin.ieng(), -1, "Check ieng() for empty bin"); test_value(bin.solidangle(), 0.0, 1.0e-10, "Check solidangle() for empty bin"); test_value(bin.ewidth().MeV(), 0.0, 1.0e-10, "Check ewidth() for empty bin"); test_value(bin.ontime(), 0.0, 1.0e-10, "Check ontime() for empty bin"); test_value(bin.weight(), 0.0, 1.0e-10, "Check weight() for empty bin"); test_value(bin.print(), "0", "Check print() for empty bin"); // Set bin attributes GSkyDir skydir; skydir.radec_deg(37.2, -67.3); GCTAInstDir instdir(skydir); instdir.detx(-0.78); instdir.dety(+0.35); bin.dir(instdir); bin.energy(GEnergy(2.0, "TeV")); bin.time(GTime(87.3)); bin.counts(4.0); bin.ipix(5); bin.ieng(7); bin.solidangle(3.14); bin.ewidth(GEnergy(7.0, "GeV")); bin.ontime(101.0); bin.weight(0.71); // Set size reference double ref_size = 3.14 * 7000.0 * 101.0 * 0.71; // Test bin attributes test_value(bin.size(), ref_size, 1.0e-10, "Check size() for filled bin"); test_value(bin.dir().dir().ra_deg(), 37.2, 1.0e-10, "Check dir().dir().ra_deg() for filled bin"); test_value(bin.dir().dir().dec_deg(), -67.3, 1.0e-10, "Check dir().dir().dec_deg() for filled bin"); test_value(bin.dir().detx(), -0.78, 1.0e-10, "Check dir().detx() for filled bin"); test_value(bin.dir().dety(), +0.35, 1.0e-10, "Check dir().detx() for filled bin"); test_value(bin.energy().TeV(), 2.0, 1.0e-10, "Check energy() for filled bin"); test_value(bin.time().secs(), 87.3, 1.0e-10, "Check time() for filled bin"); test_value(bin.counts(), 4.0, 1.0e-10, "Check counts() for filled bin"); test_value(bin.error(), 2.0, 1.0e-10, "Check error() for filled bin"); test_value(bin.ipix(), 5, "Check ipix() for filled bin"); test_value(bin.ieng(), 7, "Check ieng() for filled bin"); test_value(bin.solidangle(), 3.14, 1.0e-10, "Check solidangle() for filled bin"); test_value(bin.ewidth().GeV(), 7.0, 1.0e-10, "Check ewidth() for filled bin"); test_value(bin.ontime(), 101.0, 1.0e-10, "Check ontime() for filled bin"); test_value(bin.weight(), 0.71, 1.0e-10, "Check weight() for filled bin"); test_value(bin.print(), "4", "Check print() for filled bin"); // Test copy constructor GCTAEventBin bin2(bin); test_value(bin2.size(), ref_size, 1.0e-10, "Check size() for copied bin"); test_value(bin2.dir().dir().ra_deg(), 37.2, 1.0e-10, "Check dir().dir().ra_deg() for filled bin"); test_value(bin2.dir().dir().dec_deg(), -67.3, 1.0e-10, "Check dir().dir().dec_deg() for filled bin"); test_value(bin2.dir().detx(), -0.78, 1.0e-10, "Check dir().detx() for filled bin"); test_value(bin2.dir().dety(), +0.35, 1.0e-10, "Check dir().detx() for filled bin"); test_value(bin2.energy().TeV(), 2.0, 1.0e-10, "Check energy() for copied bin"); test_value(bin2.time().secs(), 87.3, 1.0e-10, "Check time() for copied bin"); test_value(bin2.counts(), 4.0, 1.0e-10, "Check counts() for copied bin"); test_value(bin2.error(), 2.0, 1.0e-10, "Check error() for copied bin"); test_value(bin2.ipix(), 5, "Check ipix() for copied bin"); test_value(bin2.ieng(), 7, "Check ieng() for copied bin"); test_value(bin2.solidangle(), 3.14, 1.0e-10, "Check solidangle() for copied bin"); test_value(bin2.ewidth().GeV(), 7.0, 1.0e-10, "Check ewidth() for copied bin"); test_value(bin2.ontime(), 101.0, 1.0e-10, "Check ontime() for copied bin"); test_value(bin2.weight(), 0.71, 1.0e-10, "Check weight() for copied bin"); test_value(bin2.print(), "4", "Check print() for copied bin"); // Assignment operator GCTAEventBin bin3 = bin; test_value(bin3.size(), ref_size, 1.0e-10, "Check size() for assigned bin"); test_value(bin3.dir().dir().ra_deg(), 37.2, 1.0e-10, "Check dir().dir().ra_deg() for filled bin"); test_value(bin3.dir().dir().dec_deg(), -67.3, 1.0e-10, "Check dir().dir().dec_deg() for filled bin"); test_value(bin3.dir().detx(), -0.78, 1.0e-10, "Check dir().detx() for filled bin"); test_value(bin3.dir().dety(), +0.35, 1.0e-10, "Check dir().detx() for filled bin"); test_value(bin3.energy().TeV(), 2.0, 1.0e-10, "Check energy() for assigned bin"); test_value(bin3.time().secs(), 87.3, 1.0e-10, "Check time() for assigned bin"); test_value(bin3.counts(), 4.0, 1.0e-10, "Check counts() for assigned bin"); test_value(bin3.error(), 2.0, 1.0e-10, "Check error() for assigned bin"); test_value(bin3.ipix(), 5, "Check ipix() for assigned bin"); test_value(bin3.ieng(), 7, "Check ieng() for assigned bin"); test_value(bin3.solidangle(), 3.14, 1.0e-10, "Check solidangle() for assigned bin"); test_value(bin3.ewidth().GeV(), 7.0, 1.0e-10, "Check ewidth() for assigned bin"); test_value(bin3.ontime(), 101.0, 1.0e-10, "Check ontime() for assigned bin"); test_value(bin3.weight(), 0.71, 1.0e-10, "Check weight() for assigned bin"); test_value(bin3.print(), "4", "Check print() for assigned bin"); // clone method GCTAEventBin* bin4 = bin.clone(); test_value(bin4->size(), ref_size, 1.0e-10, "Check size() for cloned bin"); test_value(bin4->dir().dir().ra_deg(), 37.2, 1.0e-10, "Check dir().dir().ra_deg() for filled bin"); test_value(bin4->dir().dir().dec_deg(), -67.3, 1.0e-10, "Check dir().dir().dec_deg() for filled bin"); test_value(bin4->dir().detx(), -0.78, 1.0e-10, "Check dir().detx() for filled bin"); test_value(bin4->dir().dety(), +0.35, 1.0e-10, "Check dir().detx() for filled bin"); test_value(bin4->energy().TeV(), 2.0, 1.0e-10, "Check energy() for cloned bin"); test_value(bin4->time().secs(), 87.3, 1.0e-10, "Check time() for cloned bin"); test_value(bin4->counts(), 4.0, 1.0e-10, "Check counts() for cloned bin"); test_value(bin4->error(), 2.0, 1.0e-10, "Check error() for cloned bin"); test_value(bin4->ipix(), 5, "Check ipix() for cloned bin"); test_value(bin4->ieng(), 7, "Check ieng() for cloned bin"); test_value(bin4->solidangle(), 3.14, 1.0e-10, "Check solidangle() for cloned bin"); test_value(bin4->ewidth().GeV(), 7.0, 1.0e-10, "Check ewidth() for cloned bin"); test_value(bin4->ontime(), 101.0, 1.0e-10, "Check ontime() for cloned bin"); test_value(bin4->weight(), 0.71, 1.0e-10, "Check weight() for cloned bin"); test_value(bin4->print(), "4", "Check print() for cloned bin"); // clear method bin.clear(); test_value(bin.classname(), "GCTAEventBin", "Check classname() for cleared bin"); test_value(bin.size(), 0.0, 1.0e-10, "Check size() for cleared bin"); test_value(bin.dir().dir().ra_deg(), 0.0, 1.0e-10, "Check dir().dir().ra_deg() for cleared bin"); test_value(bin.dir().dir().dec_deg(), 0.0, 1.0e-10, "Check dir().dir().dec_deg() for cleared bin"); test_value(bin.energy().MeV(), 0.0, 1.0e-10, "Check energy() for cleared bin"); test_value(bin.time().secs(), 0.0, 1.0e-10, "Check time() for cleared bin"); test_value(bin.counts(), 0.0, 1.0e-10, "Check counts() for cleared bin"); test_value(bin.error(), 0.0, 1.0e-10, "Check error() for cleared bin"); test_value(bin.ipix(), -1, "Check ipix() for cleared bin"); test_value(bin.ieng(), -1, "Check ieng() for cleared bin"); test_value(bin.solidangle(), 0.0, 1.0e-10, "Check solidangle() for cleared bin"); test_value(bin.ewidth().MeV(), 0.0, 1.0e-10, "Check ewidth() for cleared bin"); test_value(bin.ontime(), 0.0, 1.0e-10, "Check ontime() for cleared bin"); test_value(bin.weight(), 0.0, 1.0e-10, "Check weight() for cleared bin"); test_value(bin.print(), "0", "Check print() for cleared bin"); // Return return; } /***********************************************************************//** * @brief Test event cube ***************************************************************************/ void TestGCTAObservation::test_event_cube(void) { // Construct empty event cube GCTAEventCube cube1; // Test empty event cube test_value(cube1.size(), 0, "Check that empty event cube has size()=0"); test_value(cube1.dim(), 0, "Check that empty event cube has dim()=0"); test_value(cube1.number(), 0, "Check that empty event cube has number()=0"); test_value(cube1.nx(), 0, "Check that empty event cube has nx()=0"); test_value(cube1.ny(), 0, "Check that empty event cube has ny()=0"); test_value(cube1.npix(), 0, "Check that empty event cube has npix()=0"); test_value(cube1.ebins(), 0, "Check that empty event cube has ebins()=0"); // Construct 3x3x5 event cube GSkyMap map("CAR", "CEL", 0.0, 0.0, 0.5, 0.5, 3, 4, 5); GEbounds ebounds(5, GEnergy(0.1, "TeV"), GEnergy(10.0, "TeV")); GGti gti(GTime(0.0, "sec"), GTime(1800.0, "sec")); GCTAEventCube cube2(map, ebounds, gti); // Test 3x3x5 event cube test_value(cube2.size(), 60, "Check that event cube has size()=60"); test_value(cube2.dim(), 3, "Check that event cube has dim()=3"); test_value(cube2.number(), 0, "Check that event cube has number()=0"); test_value(cube2.nx(), 3, "Check that event cube has nx()=3"); test_value(cube2.ny(), 4, "Check that event cube has ny()=4"); test_value(cube2.npix(), 12, "Check that event cube has npix()=12"); test_value(cube2.ebins(), 5, "Check that event cube has ebins()=5"); // Fill counts cube and test result double ref(0.0); double value(0.0); for (int ix = 0; ix < cube2.nx(); ++ix) { for (int iy = 0; iy < cube2.ny(); ++iy) { for (int iebin = 0; iebin < cube2.ebins(); ++iebin) { value += 0.376; ref += value; map(GSkyPixel(ix,iy),iebin) = value; } } } cube2.counts(map); test_value(cube2.number(), int(ref+0.5), "Check event cube number() for filled cube"); // Checks weights double total(0.0); for (int ix = 0; ix < cube2.nx(); ++ix) { for (int iy = 0; iy < cube2.ny(); ++iy) { for (int iebin = 0; iebin < cube2.ebins(); ++iebin) { total += cube2.weights()(GSkyPixel(ix,iy),iebin); } } } test_value(total, 60.0, 1.0e-6, "Check event cube weights for cube with unity weights"); // Save event cube cube2.save("test_cta_event_cube.fits", true); // Construct counts cube from FITS file GCTAEventCube cube3("test_cta_event_cube.fits"); // Test loaded event cube test_value(cube3.size(), 60, "Check that loaded event cube has size()=60"); test_value(cube3.dim(), 3, "Check that loaded event cube has dim()=3"); test_value(cube3.nx(), 3, "Check that loaded event cube has nx()=3"); test_value(cube3.ny(), 4, "Check that loaded event cube has ny()=4"); test_value(cube3.npix(), 12, "Check that loaded event cube has npix()=12"); test_value(cube3.ebins(), 5, "Check that loaded event cube has ebins()=5"); test_value(cube3.number(), int(ref+0.5), "Check event cube number() for loaded cube"); // Checks weights for loaded event cube total = 0.0; for (int ix = 0; ix < cube2.nx(); ++ix) { for (int iy = 0; iy < cube2.ny(); ++iy) { for (int iebin = 0; iebin < cube2.ebins(); ++iebin) { total += cube3.weights()(GSkyPixel(ix,iy),iebin); } } } test_value(total, 60.0, 1.0e-6, "Check event cube weights for loaded cube with unity weights"); // Set weights cube3.weights(map); // Checks weights total = 0.0; for (int ix = 0; ix < cube2.nx(); ++ix) { for (int iy = 0; iy < cube2.ny(); ++iy) { for (int iebin = 0; iebin < cube2.ebins(); ++iebin) { total += cube3.weights()(GSkyPixel(ix,iy),iebin); } } } test_value(total, ref, 1.0e-6, "Check event cube weights after setting them"); // Save event cube cube3.save("test_cta_event_cube.fits", true); // Construct counts cube from FITS file GCTAEventCube cube4("test_cta_event_cube.fits"); // Checks weights total = 0.0; for (int ix = 0; ix < cube2.nx(); ++ix) { for (int iy = 0; iy < cube2.ny(); ++iy) { for (int iebin = 0; iebin < cube2.ebins(); ++iebin) { total += cube3.weights()(GSkyPixel(ix,iy),iebin); } } } test_value(total, ref, 1.0e-6, "Check event cube weights after loading them again"); // Return return; } /***********************************************************************//** * @brief Test unbinned observation handling ***************************************************************************/ void TestGCTAObservation::test_unbinned_obs(void) { // Declare observations GObservations obs; GCTAObservation run; // Load unbinned CTA observation test_try("Load unbinned CTA observation"); try { run.load(cta_events); run.response(cta_irf, GCaldb(cta_caldb)); test_value(run.roi().centre().dir().ra_deg(), 83.63); test_value(run.roi().centre().dir().dec_deg(), 22.01); test_value(run.roi().radius(), 5.0); test_value(run.ebounds().emin().TeV(), 0.1); test_value(run.ebounds().emax().TeV(), 100.0); test_value(run.gti().tstart().convert(run.gti().reference()), 0.0); test_value(run.gti().tstop().convert(run.gti().reference()), 1800.0); test_value(run.ontime(), 1800.0); test_value(run.livetime(), 1710.0); test_value(run.deadc(), 0.95); test_value(run.ra_obj(), 0.0); test_value(run.dec_obj(), 0.0); test_value(run.id(), "0"); test_value(run.pointing().dir().ra_deg(), 83.63); test_value(run.pointing().dir().dec_deg(), 22.01); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Load unbinned CTA observation with non-default extension names test_try("Load unbinned CTA observation with non-default extension names"); try { run.load(cta_events_gti); run.response(cta_irf, GCaldb(cta_caldb)); test_value(run.roi().centre().dir().ra_deg(), 83.65); test_value(run.roi().centre().dir().dec_deg(), 23.01); test_value(run.roi().radius(), 4.0); test_value(run.ebounds().emin().TeV(), 0.2); test_value(run.ebounds().emax().TeV(), 120.0); test_value(run.gti().tstart().convert(run.gti().reference()), 1.0); test_value(run.gti().tstop().convert(run.gti().reference()), 2000.0); test_value(run.ontime(), 1800.0); test_value(run.livetime(), 1710.0); test_value(run.deadc(), 0.95); test_value(run.ra_obj(), 0.0); test_value(run.dec_obj(), 0.0); test_value(run.id(), "0"); test_value(run.pointing().dir().ra_deg(), 83.63); test_value(run.pointing().dir().dec_deg(), 22.01); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Add observation (twice) to data test_try("Append observation twice"); try { run.id("0001"); obs.append(run); run.id("0002"); obs.append(run); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Loop over all events const GEvents* events = run.events(); int num = 0; for (int i = 0; i < events->size(); ++i) { num++; } test_value(num, 4134, 1.0e-20, "Test event iterator"); // Test XML loading and saving test_try("Test XML loading and saving"); try { obs = GObservations(cta_unbin_xml); obs.save("test_cta_obs_unbinned.xml"); GCTAObservation* run = dynamic_cast(obs[0]); test_value(run->roi().centre().dir().ra_deg(), 83.63); test_value(run->roi().centre().dir().dec_deg(), 22.01); test_value(run->roi().radius(), 5.0); test_value(run->ebounds().emin().TeV(), 0.1); test_value(run->ebounds().emax().TeV(), 100.0); test_value(run->gti().tstart().convert(run->gti().reference()), 0.0); test_value(run->gti().tstop().convert(run->gti().reference()), 1800.0); test_value(run->ontime(), 1800.0); test_value(run->livetime(), 1710.0); test_value(run->deadc(), 0.95); test_value(run->ra_obj(), 0.0); test_value(run->dec_obj(), 0.0); test_value(run->id(), "00001"); test_value(run->pointing().dir().ra_deg(), 83.63); test_value(run->pointing().dir().dec_deg(), 22.01); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Remove test files std::remove("test_cta_events1.fits"); std::remove("test_cta_events2.fits"); // Test loading of the event file and saving into a different extension run.load(cta_events); run.save("test_cta_events1.fits[EVENTS2;GTI2]", true); GFits fits("test_cta_events1.fits"); test_value(fits.size(), 3, "FITS file should contain 3 HDUs"); test_assert(fits.contains("EVENTS2"), "FITS should contain \"EVENTS2\" HDU"); test_assert(fits.contains("GTI2"), "FITS should contain \"GTI2\" HDU"); fits.close(); run.save("test_cta_events1.fits[EVENTS3;GTI3]", true); fits.open("test_cta_events1.fits"); test_value(fits.size(), 5, "FITS file should contain 5 HDUs"); test_assert(fits.contains("EVENTS2"), "FITS should contain \"EVENTS2\" HDU"); test_assert(fits.contains("GTI2"), "FITS should contain \"GTI2\" HDU"); test_assert(fits.contains("EVENTS3"), "FITS should contain \"EVENTS3\" HDU"); test_assert(fits.contains("GTI3"), "FITS should contain \"GTI3\" HDU"); fits.close(); // Test writing of multiple event files into FITS file run.load(cta_events); GFits fits2; run.write(fits2, "EVENTS1", "GTI1"); run.write(fits2, "EVENTS2", "GTI2"); run.write(fits2, "EVENTS3", "GTI3"); fits2.saveto("test_cta_events2.fits", true); fits2.close(); fits.open("test_cta_events2.fits"); test_value(fits.size(), 7, "FITS file should contain 7 HDUs"); test_assert(fits.contains("EVENTS1"), "FITS should contain \"EVENTS1\" HDU"); test_assert(fits.contains("GTI1"), "FITS should contain \"GTI1\" HDU"); test_assert(fits.contains("EVENTS2"), "FITS should contain \"EVENTS2\" HDU"); test_assert(fits.contains("GTI2"), "FITS should contain \"GTI2\" HDU"); test_assert(fits.contains("EVENTS3"), "FITS should contain \"EVENTS3\" HDU"); test_assert(fits.contains("GTI3"), "FITS should contain \"GTI3\" HDU"); fits.close(); // Return return; } /***********************************************************************//** * @brief Test binned observation handling ***************************************************************************/ void TestGCTAObservation::test_binned_obs(void) { // Set filenames const std::string file1 = "test_cta_obs_binned.xml"; // Declare observations GObservations obs; GCTAObservation run; // Load binned CTA observation test_try("Load binned CTA observation"); try { run.load(cta_cntmap); run.response(cta_irf, GCaldb(cta_caldb)); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Test XML loading and saving test_try("Test XML loading and saving"); try { obs = GObservations(cta_bin_xml); obs.save(file1); test_try_success(); } catch (std::exception &e) { test_try_failure(e); } // Return return; } /***********************************************************************//** * @brief Test stacked observation handling ***************************************************************************/ void TestGCTAObservation::test_stacked_obs(void) { // Construct stacked observation without energy dispersion GCTAObservation cta1(cta_stacked_cntcube, cta_stacked_expcube, cta_stacked_psfcube, cta_stacked_bkgcube); // Test for presence of response const GCTAResponseCube* rsp = dynamic_cast(cta1.response()); test_assert((rsp != NULL), "Observation contains cube response"); if (rsp != NULL) { rsp->apply_edisp(true); // Try to use energy dispersion test_assert((!rsp->use_edisp()), "Response has no energy dispersion"); } // Construct stacked observation with energy dispersion GCTAObservation cta2(cta_stacked_cntcube, cta_stacked_expcube, cta_stacked_psfcube, cta_stacked_edispcube, cta_stacked_bkgcube); // Test for presence of response rsp = dynamic_cast(cta2.response()); test_assert((rsp != NULL), "Observation contains cube response"); if (rsp != NULL) { rsp->apply_edisp(true); // Try to use energy dispersion test_assert(rsp->use_edisp(), "Response has energy dispersion"); } // Construct stacked observation from XML file without energy dispersion GObservations obs(cta_stacked_xml); test_value(obs.size(), 1, "One observation in container"); // Test for presence of response rsp = dynamic_cast(obs[0]->response()); test_assert((rsp != NULL), "Observation contains cube response"); if (rsp != NULL) { rsp->apply_edisp(true); // Try to use energy dispersion test_assert((!rsp->use_edisp()), "Response has no energy dispersion"); } // Save observation container into XML file obs.save("test_cta_obs_cube.xml"); // Return return; } /***********************************************************************//** * @brief Test On/Off observation handling ***************************************************************************/ void TestGCTAObservation::test_onoff_obs(void) { // Load On/Off observation into container GObservations obs(cta_onoff_obs); // Save observation container into XML file obs.save("test_cta_onoff_obs.xml"); // Load CTA observations obs.load(cta_unbin_xml); // Setup sky regions GSkyRegions regions(cta_onoff_onreg); // Set and check regions for (int i = 0; i < obs.size(); ++i) { static_cast(obs[i])->off_regions(regions); test_value(static_cast(obs[i])->off_regions().size(), 1, "Check number of sky regions"); } // Save observation container into XML file obs.save("test_cta_onoff_obs_regions.xml"); // Re-load observation container and check regions obs.load("test_cta_onoff_obs_regions.xml"); // Check regions for (int i = 0; i < obs.size(); ++i) { test_value(static_cast(obs[i])->off_regions().size(), 1, "Check number of sky regions"); } // Return return; } /***********************************************************************//** * @brief Test unbinned optimizer ***************************************************************************/ void TestGCTAOptimize::test_unbinned_optimizer(void) { // Set reference result double fit_results[] = {83.6331, 0, 22.0145, 0, 6.03743e-16, 2.01634e-17, -2.49595, 0.0249533, 300000, 0, 1, 0, 2.94185, 0.0379307, 6.43398e-05, 1.80715e-06, -1.82866, 0.0155452, 1000000, 0, 1, 0}; // Declare observations GObservations obs; GCTAObservation run; // Load unbinned CTA observation and set response run.load(cta_events); run.response(cta_irf, GCaldb(cta_caldb)); obs.append(run); // Load models from XML file obs.models(cta_model_xml); // Perform LM optimization GOptimizerLM opt; opt.max_iter(100); obs.optimize(opt); obs.errors(opt); // Verify fit results check_results(obs, fit_results); // Return return; } /***********************************************************************//** * @brief Test binned optimizer ***************************************************************************/ void TestGCTAOptimize::test_binned_optimizer(void) { // Set reference result double fit_results[] = {83.6331, 0, 22.0145, 0, 5.99255e-16, 2.00626e-17, -2.49175, 0.0250639, 300000, 0, 1, 0, 2.95662, 0.0704027, 6.42985e-05, 1.96478e-06, -1.82084, 0.0163749, 1000000, 0, 1, 0}; // Declare observations GObservations obs; GCTAObservation run; // Load binned CTA observation and set response run.load(cta_cntmap); run.response(cta_irf, GCaldb(cta_caldb)); obs.append(run); // Load models from XML file obs.models(cta_model_xml); // Perform LM optimization GOptimizerLM opt; opt.max_iter(100); obs.optimize(opt); obs.errors(opt); // Verify fit results check_results(obs, fit_results); // Return return; } /***********************************************************************//** * @brief Test stacked optimizer * * The stacked response cubes have been computed using the Prod2::South_0.5h * response. ***************************************************************************/ void TestGCTAOptimize::test_stacked_optimizer(void) { // Set reference result double fit_results[] = {83.6331, 0, 22.0145, 0, 5.985317e-16, 1.09268e-17, -2.49903, 0.0145909, 300000, 0, 1, 0, 1.150542, 0.0234168, -0.000264949, 0.0145699, 1.0e6, 0, 1, 0}; // Load stacked CTA observation GObservations obs(cta_stacked_xml); // Load models from XML file obs.models(cta_stacked_model); // Perform LM optimization GOptimizerLM opt; opt.max_iter(100); obs.optimize(opt); obs.errors(opt); // Verify fit results check_results(obs, fit_results); // Return return; } /***********************************************************************//** * @brief Test On/Off optimizer ***************************************************************************/ void TestGCTAOptimize::test_onoff_optimizer_cstat(void) { // Reference result double fit_results[] = {83.6331, 0, 22.0145, 0, 5.750910e-16, 7.604206e-18, -2.472779, 0.012290, 300000, 0, 1, 0, 0.994676, 0.050424, -0.015947, 0.029617, 1.0e6, 0, 1, 0}; // Load On/Off CTA observation GObservations obs(cta_onoff_obs); // Set fit statistic for (int i = 0; i < obs.size(); ++i){ obs[i]->statistic("CSTAT"); } // Load models from XML file obs.models(cta_onoff_model); // Perform LM optimization GOptimizerLM opt; opt.max_iter(100); obs.optimize(opt); obs.errors(opt); // Verify fit results check_results(obs, fit_results); // Return return; } /***********************************************************************//** * @brief Test On/Off optimizer ***************************************************************************/ void TestGCTAOptimize::test_onoff_optimizer_wstat(void) { // Reference result double fit_results[] = {83.6331, 0, 22.0145, 0, 5.74512365e-16, 7.61094369e-18, -2.472668, 0.0123336, 300000, 0, 1, 0, 1.0, 0., 0., 0., 1.0e6, 0, 1, 0}; // Load On/Off CTA observation GObservations obs(cta_onoff_obs); // Set fit statistic for (int i = 0; i < obs.size(); ++i){ obs[i]->statistic("WSTAT"); } // Load models from XML file obs.models(cta_onoff_model); // Perform LM optimization GOptimizerLM opt; opt.max_iter(100); obs.optimize(opt); obs.errors(opt); // Verify fit results check_results(obs, fit_results); // Return return; } /***********************************************************************//** * @brief Check fit results * * @param[in] obs Observation container * @param[in] results Pointer to array of expected fit results ***************************************************************************/ void TestGCTAOptimize::check_results(const GObservations& obs, const double* results) { // Verify fit results for (int i = 0, j = 0; i < obs.models().size(); ++i) { const GModel* model = obs.models()[i]; for (int k = 0; k < model->size(); ++k) { GModelPar par = (*model)[k]; std::string msg = "Verify optimization result for " + par.print(); test_value(par.value(), results[j], std::abs(1.0e-4*results[j]), msg); j++; test_value(par.error(), results[j], std::abs(1.0e-4*results[j]), msg); j++; } } // Return return; } /*************************************************************************** * @brief Main entry point for test executable ***************************************************************************/ int main(void) { // Allocate test suite container GTestSuites testsuites("CTA instrument specific class testing"); // Check if data directory exists bool has_data = (access(datadir.c_str(), R_OK) == 0); // Set CALDB environment variable setenv("CALDB", cta_caldb.c_str(), 1); // Initially assume that we pass all tests bool success = true; // Create test suites and append them to the container TestGCTA test; TestGCTAResponse rsp; TestGCTAModel model; TestGCTAOptimize opt; TestGCTAObservation obs; testsuites.append(rsp); if (has_data) { testsuites.append(test); testsuites.append(model); testsuites.append(opt); testsuites.append(obs); } // Run the testsuites success = testsuites.run(); // Save test report testsuites.save("reports/GCTA.xml"); // Return success status return (success ? 0 : 1); }