Commit ad1eef9b740f43214c89821ede9761995eb9b7f8

Authored by Jürgen Knödlseder
1 parent e00d5dd4

Add ctool::setup_observations() method to setup an observation container by comp…

…lementing missing information from user parameters.
pyext/ctool.i
@@ -87,6 +87,7 @@ public: @@ -87,6 +87,7 @@ public:
87 %rename(_read_ahead) read_ahead() const; 87 %rename(_read_ahead) read_ahead() const;
88 %rename(_time_reference) time_reference() const; 88 %rename(_time_reference) time_reference() const;
89 %rename(_get_observations) get_observations(const bool& get_response = true); 89 %rename(_get_observations) get_observations(const bool& get_response = true);
  90 + %rename(_setup_observations) setup_observations(GObservations& obs);
90 %rename(_create_ebounds) create_ebounds(); 91 %rename(_create_ebounds) create_ebounds();
91 %rename(_create_map) create_map(const GObservations& obs); 92 %rename(_create_map) create_map(const GObservations& obs);
92 %rename(_create_cube) create_cube(const GObservations& obs); 93 %rename(_create_cube) create_cube(const GObservations& obs);
@@ -105,6 +106,7 @@ public: @@ -105,6 +106,7 @@ public:
105 const bool& read_ahead(void) const; 106 const bool& read_ahead(void) const;
106 const GTimeReference& time_reference(void) const; 107 const GTimeReference& time_reference(void) const;
107 GObservations get_observations(const bool& get_response = true); 108 GObservations get_observations(const bool& get_response = true);
  109 + void setup_observations(GObservations& obs);
108 110
109 // Protected methods that create objects from user parameters 111 // Protected methods that create objects from user parameters
110 GEbounds create_ebounds(void); 112 GEbounds create_ebounds(void);
@@ -170,14 +172,15 @@ public: @@ -170,14 +172,15 @@ public:
170 172
171 # Initialise observation container from constructor arguments. In case that 173 # Initialise observation container from constructor arguments. In case that
172 # an observation container is provided as argument, this container will be 174 # an observation container is provided as argument, this container will be
173 -# used to initialise the class. 175 +# used to initialise the class. The function returns a tuple containing the
  176 +# observation container and the eventually reduced argument list.
174 def _set_input_obs(self, argv): 177 def _set_input_obs(self, argv):
175 if len(argv) > 0 and isinstance(argv[0],gammalib.GObservations): 178 if len(argv) > 0 and isinstance(argv[0],gammalib.GObservations):
176 obs = argv[0] 179 obs = argv[0]
177 argv = argv[1:] 180 argv = argv[1:]
178 else: 181 else:
179 obs = gammalib.GObservations() 182 obs = gammalib.GObservations()
180 - return obs 183 + return (obs, argv)
181 cscript._set_input_obs = _set_input_obs 184 cscript._set_input_obs = _set_input_obs
182 185
183 # Initialise application by calling the appropriate class constructor. 186 # Initialise application by calling the appropriate class constructor.
src/support/ctool.cpp
@@ -49,6 +49,7 @@ @@ -49,6 +49,7 @@
49 49
50 50
51 /* __ Method name definitions ____________________________________________ */ 51 /* __ Method name definitions ____________________________________________ */
  52 +#define G_SETUP_OBSERVATION "ctool::setup_observations(GObservations&)"
52 #define G_GET_MEAN_POINTING "ctool::get_mean_pointing(GObservations&)" 53 #define G_GET_MEAN_POINTING "ctool::get_mean_pointing(GObservations&)"
53 #define G_CREATE_EBOUNDS "ctool::create_ebounds()" 54 #define G_CREATE_EBOUNDS "ctool::create_ebounds()"
54 #define G_PROVIDE_HELP "ctool::provide_help()" 55 #define G_PROVIDE_HELP "ctool::provide_help()"
@@ -378,7 +379,98 @@ GObservations ctool::get_observations(const bool& get_response) @@ -378,7 +379,98 @@ GObservations ctool::get_observations(const bool& get_response)
378 379
379 // Return observation container 380 // Return observation container
380 return obs; 381 return obs;
  382 +}
  383 +
  384 +
  385 +/***********************************************************************//**
  386 + * @brief Setup observation container
  387 + *
  388 + * @param[in] obs Observation container
  389 + *
  390 + * @exception GException::invalid_value
  391 + * Invalid "inobs" parameter encountered.
  392 + *
  393 + * Setup an observation container by appending all required information that
  394 + * is missing using user parameters.
  395 + *
  396 + * If the observation container @p obs is empty, the method will extract the
  397 + * filename from the "inobs" parameter, and if the filename is neither empty
  398 + * nor "NONE", will load the observation container from the specified file.
  399 + * If the file is a FITS file, and dependent on the information that is found
  400 + * in the FITS file, the method will append a single CTA observation to the
  401 + * observation container, containing either an event list or a counts cube.
  402 + * Otherwise, the method assumes that the file is an observation definition
  403 + * XML file and will load that file into the observation container.
  404 + *
  405 + * If the filename is empty or "NONE", the method will throw an exception.
  406 + *
  407 + * The method will then setup the response for all CTA observations in the
  408 + * container (see the set_response() method for more information).
  409 + *
  410 + * The method will furthermore setup the observation boundaries for all CTA
  411 + * observations in the container that contain event lists (see the
  412 + * set_obs_bounds() method for more information).
  413 + ***************************************************************************/
  414 +void ctool::setup_observations(GObservations& obs)
  415 +{
  416 + // Load observations if there are none in the container
  417 + if (obs.size() == 0) {
  418 +
  419 + // Get the filename from the "inobs" parameter
  420 + GFilename filename((*this)["inobs"].filename());
  421 +
  422 + // Load observation container from file if the filename is neither
  423 + // "NONE" nor empty ...
  424 + if ((gammalib::toupper(filename.url()) != "NONE") &&
  425 + !filename.is_empty()) {
  426 +
  427 + // If file does not exist then throw an exceptions
  428 + if (!filename.exists()) {
  429 + std::string msg = "The \"inobs\" parameter \""+filename+
  430 + "\" specifies a file that does not "
  431 + "exist. Please specify a valid event "
  432 + "list, counts cube or observation "
  433 + "definition XML file.";
  434 + throw GException::invalid_value(G_SETUP_OBSERVATION, msg);
  435 + }
  436 +
  437 + // If the file is a FITS file then load it into a CTA
  438 + // observation and append that observation to the container ...
  439 + else if (filename.is_fits()) {
  440 + GCTAObservation cta(filename);
  441 + obs.append(cta);
  442 + }
  443 +
  444 + // ... otherwise it is assumed that the file is an observation
  445 + // definition XML file and the file is loaded into the
  446 + // observation container
  447 + else {
  448 + obs.load(filename);
  449 + }
  450 +
  451 + }
  452 +
  453 + // ... otherwise, if the filename is "NONE" or empty then throw
  454 + // an exception.
  455 + else {
  456 + std::string msg = "The \"inobs\" parameter \""+filename+
  457 + "\" is not a valid filename. Please specify "
  458 + "a valid event list, counts cube or "
  459 + "observation definition XML file.";
  460 + throw GException::invalid_value(G_SETUP_OBSERVATION, msg);
  461 + }
  462 +
  463 + } // endif: there were no observations in the container
  464 +
  465 + // Setup the response for all CTA observations in the observation
  466 + // container
  467 + set_response(obs);
381 468
  469 + // Setup boundaries for all CTA observations containing event lists
  470 + set_obs_bounds(obs);
  471 +
  472 + // Return
  473 + return;
382 } 474 }
383 475
384 476
@@ -595,15 +687,16 @@ GCTAEventCube ctool::create_cube(const GObservations& obs) @@ -595,15 +687,16 @@ GCTAEventCube ctool::create_cube(const GObservations& obs)
595 * method furthermore sets the ontime, livetime and deadtime correction 687 * method furthermore sets the ontime, livetime and deadtime correction
596 * factor. 688 * factor.
597 * 689 *
598 - * The following parameters are read:  
599 - * ra - Right Ascension of pointing and RoI centre (deg)  
600 - * dec - Declination of pointing and RoI centre (deg)  
601 - * rad - Radius of RoI (deg)  
602 - * deadc - Deadtime correction factor  
603 - * tmin - Start time  
604 - * tmax - Stop time  
605 - * emin - Minimum energy (TeV)  
606 - * emax - Maximum energy (TeV) 690 + * The following parameters are read
  691 + *
  692 + * ra: Right Ascension of pointing and RoI centre (deg)
  693 + * dec: Declination of pointing and RoI centre (deg)
  694 + * rad: Radius of RoI (deg)
  695 + * deadc: Deadtime correction factor
  696 + * tmin: Start time
  697 + * tmax: Stop time
  698 + * emin: Minimum energy (TeV)
  699 + * emax: Maximum energy (TeV)
607 ***************************************************************************/ 700 ***************************************************************************/
608 GCTAObservation ctool::create_cta_obs(void) 701 GCTAObservation ctool::create_cta_obs(void)
609 { 702 {
@@ -804,12 +897,9 @@ void ctool::require_inobs_nocube(const std::string& method) @@ -804,12 +897,9 @@ void ctool::require_inobs_nocube(const std::string& method)
804 * 897 *
805 * @param[in,out] obs Observation container 898 * @param[in,out] obs Observation container
806 * 899 *
807 - * Set the response for a CTA observations in the container that so far have  
808 - * no response using the "caldb" and "irf" task parameters.  
809 - *  
810 - * The following parameters are read:  
811 - * caldb - Calibration database  
812 - * irf - Instrument response function 900 + * Set the response for all CTA observations in the container that so far
  901 + * have no response information available. See the set_obs_response()
  902 + * method for more information about the user parameters that are read.
813 ***************************************************************************/ 903 ***************************************************************************/
814 void ctool::set_response(GObservations& obs) 904 void ctool::set_response(GObservations& obs)
815 { 905 {
@@ -841,69 +931,83 @@ void ctool::set_response(GObservations& obs) @@ -841,69 +931,83 @@ void ctool::set_response(GObservations& obs)
841 * 931 *
842 * @param[in,out] obs CTA observation 932 * @param[in,out] obs CTA observation
843 * 933 *
844 - * Set the response for a CTA observation. If the CTA observation contains 934 + * Set the response for one CTA observation. If the CTA observation contains
845 * a counts cube the method first attempts to set the response information 935 * a counts cube the method first attempts to set the response information
846 - * using the @a expcube and @a psfcube user parameters. If this is not  
847 - * successful (for example because the parameters do not exist or their  
848 - * value is NONE or blank), the method will set the response information  
849 - * from the @a caldb and @a irf user parameters. 936 + * using the following user parameters
  937 + *
  938 + * expcube: Exposure cube file
  939 + * psfcube: Point spread function cube file
  940 + * edispcube: Energy dispersion cube file (optional)
  941 + * bkgcube: Background cube file
  942 + *
  943 + * If none of the @p expcube, @p psfcube and @p bkgcube parameters is
  944 + * "NONE" or empty, the method will allocate a stacked response for the
  945 + * observation. In case that an energy dispersion cube is provided using
  946 + * the @p edispcube parameter, this energy dispersion cube is also loaded
  947 + * and attached to the stacked response function.
  948 + *
  949 + * In case that any of the @p expcube, @p psfcube and @p bkgcube parameters
  950 + * is "NONE" or empty, the method will assume that a IRF response should
  951 + * be used, and will attempty to set the response information using the
  952 + * following user parameters
  953 + *
  954 + * caldb: Calibration database
  955 + * irf: Instrument response function
850 * 956 *
851 - * The following parameters are read:  
852 - * expcube - Exposure cube file (optional)  
853 - * psfcube - PSF cube file (optional)  
854 - * caldb - Calibration database (optional)  
855 - * irf - Instrument response function (optional)  
856 ***************************************************************************/ 957 ***************************************************************************/
857 void ctool::set_obs_response(GCTAObservation* obs) 958 void ctool::set_obs_response(GCTAObservation* obs)
858 { 959 {
859 // Initialise response flag 960 // Initialise response flag
860 bool has_response = false; 961 bool has_response = false;
861 962
862 - // If the observation contains a counts cube, then first check whether  
863 - // the expcube and psfcube parameters exist and are not NONE 963 + // If the observation contains a counts cube, then check whether
  964 + // response information for a stacked analysis is provided, and if so,
  965 + // set that response information for the CTA observation
864 if (dynamic_cast<const GCTAEventCube*>(obs->events()) != NULL) { 966 if (dynamic_cast<const GCTAEventCube*>(obs->events()) != NULL) {
865 if (has_par("expcube") && has_par("psfcube") && 967 if (has_par("expcube") && has_par("psfcube") &&
866 has_par("edispcube") && has_par("bkgcube")) { 968 has_par("edispcube") && has_par("bkgcube")) {
867 969
868 // Get filenames 970 // Get filenames
869 - std::string expcube = (*this)["expcube"].filename();  
870 - std::string psfcube = (*this)["psfcube"].filename();  
871 - std::string edispcube = (*this)["edispcube"].filename();  
872 - std::string bkgcube = (*this)["bkgcube"].filename();  
873 -  
874 - // Extract response information if available  
875 - if ((expcube != "NONE") && (psfcube != "NONE") &&  
876 - (bkgcube != "NONE") &&  
877 - (gammalib::strip_whitespace(expcube) != "") &&  
878 - (gammalib::strip_whitespace(psfcube) != "") &&  
879 - (gammalib::strip_whitespace(bkgcube) != "")) { 971 + GFilename expcube((*this)["expcube"].filename());
  972 + GFilename psfcube((*this)["psfcube"].filename());
  973 + GFilename edispcube((*this)["edispcube"].filename());
  974 + GFilename bkgcube((*this)["bkgcube"].filename());
  975 +
  976 + // Extract stacked response information if available
  977 + if ((gammalib::toupper(expcube.url()) != "NONE") &&
  978 + (gammalib::toupper(psfcube.url()) != "NONE") &&
  979 + (gammalib::toupper(bkgcube.url()) != "NONE") &&
  980 + !expcube.is_empty() &&
  981 + !psfcube.is_empty() &&
  982 + !bkgcube.is_empty()) {
880 983
881 // Get exposure, PSF and background cubes 984 // Get exposure, PSF and background cubes
882 GCTACubeExposure exposure(expcube); 985 GCTACubeExposure exposure(expcube);
883 GCTACubePsf psf(psfcube); 986 GCTACubePsf psf(psfcube);
884 GCTACubeBackground background(bkgcube); 987 GCTACubeBackground background(bkgcube);
885 988
886 - // Optionally load energy dispersion cube  
887 - if ((edispcube != "NONE") &&  
888 - (gammalib::strip_whitespace(edispcube) != "")) { 989 + // If an energy dispersion cube is available, get also
  990 + // this cube, and set all information as stacked response
  991 + // for this CTA observation
  992 + if ((gammalib::toupper(edispcube.url()) != "NONE") &&
  993 + !edispcube.is_empty()) {
889 GCTACubeEdisp edisp(edispcube); 994 GCTACubeEdisp edisp(edispcube);
890 obs->response(exposure, psf, edisp, background); 995 obs->response(exposure, psf, edisp, background);
891 } 996 }
892 else { 997 else {
893 - // Set reponse  
894 obs->response(exposure, psf, background); 998 obs->response(exposure, psf, background);
895 } 999 }
896 1000
897 - // Signal response availability 1001 + // Signal that response is available
898 has_response = true; 1002 has_response = true;
899 1003
900 } // endif: filenames were available 1004 } // endif: filenames were available
901 1005
902 - } // endif: expcube and psfcube parameters exist 1006 + } // endif: expcube, psfcube and bkdcube parameters valid
903 } // endif: observation contains a counts cube 1007 } // endif: observation contains a counts cube
904 1008
905 - // If we have not yet response information then get it now  
906 - // from the caldb and irf parameters 1009 + // If we have not yet response information then get it now from the
  1010 + // caldb and irf user parameters
907 if (!has_response) { 1011 if (!has_response) {
908 1012
909 // Load response information 1013 // Load response information
@@ -938,59 +1042,70 @@ void ctool::set_obs_response(GCTAObservation* obs) @@ -938,59 +1042,70 @@ void ctool::set_obs_response(GCTAObservation* obs)
938 1042
939 1043
940 /***********************************************************************//** 1044 /***********************************************************************//**
941 - * @brief Set observation boundaries 1045 + * @brief Set observation boundaries for all CTA observations
942 * 1046 *
943 * @param[in,out] obs Observation container 1047 * @param[in,out] obs Observation container
  1048 + *
  1049 + * Sets the observation boundaries for all CTA observations in the container
  1050 + * that contain event lists. If the energy boundaries of an event list are
  1051 + * empty, the method gets the
  1052 + *
  1053 + * emin: Minimum energy (TeV)
  1054 + * emax: Maximum energy (TeV)
  1055 + *
  1056 + * user parameters and sets a single energy boundary element @p [emin,emax].
  1057 + *
  1058 + * If the RoI radius of the event list is invalid, the method gets the
  1059 + *
  1060 + * rad: Region of Interest radius (deg)
  1061 + *
  1062 + * user parameter to set the radius of the RoI, and uses the CTA pointing
  1063 + * direction to set the centre of the RoI.
944 ***************************************************************************/ 1064 ***************************************************************************/
945 void ctool::set_obs_bounds(GObservations& obs) 1065 void ctool::set_obs_bounds(GObservations& obs)
946 { 1066 {
947 - // Setup response for all observations 1067 + // Loop over all observations in the container
948 for (int i = 0; i < obs.size(); ++i) { 1068 for (int i = 0; i < obs.size(); ++i) {
949 1069
950 - // Is this observation a CTA observation? 1070 + // Get pointer to CTA observation
951 GCTAObservation* cta = dynamic_cast<GCTAObservation*>(obs[i]); 1071 GCTAObservation* cta = dynamic_cast<GCTAObservation*>(obs[i]);
952 1072
953 - // Continue only if observation is CTA and has events  
954 - if ((cta != NULL) && (cta->has_events())) {  
955 -  
956 - // Get pointer on event list  
957 - GCTAEventList* list = const_cast<GCTAEventList*>(dynamic_cast<const GCTAEventList*>(cta->events()));  
958 -  
959 - // Continue only if it's valid  
960 - if (list != NULL) {  
961 -  
962 - // If there are no energy boundaries then read the user  
963 - // parameters and add them  
964 - if (list->ebounds().is_empty()) {  
965 -  
966 - // If there are "emin" and "emax" parameters then use  
967 - // them  
968 - if (has_par("emin") && has_par("emax")) {  
969 - double emin = (*this)["emin"].real();  
970 - double emax = (*this)["emax"].real();  
971 - GEbounds ebounds(GEnergy(emin, "TeV"),  
972 - GEnergy(emax, "TeV"));  
973 - list->ebounds(ebounds);  
974 - }  
975 -  
976 - } 1073 + // Fall through if observation is not a CTA observation or if it
  1074 + // does not contain events
  1075 + if ((cta == NULL) || (!cta->has_events())) {
  1076 + continue;
  1077 + }
977 1078
978 - // If there is no ROI then read the user parameters and add  
979 - // them  
980 - if (list->roi().radius() == 0) {  
981 -  
982 - // If there is a "rad" parameter then use it  
983 - if (has_par("rad")) {  
984 - double rad = (*this)["rad"].real();  
985 - GCTARoi roi(GCTAInstDir(cta->pointing().dir()), rad);  
986 - list->roi(roi);  
987 - } 1079 + // Get pointer on CTA event list
  1080 + GCTAEventList* list = const_cast<GCTAEventList*>
  1081 + (dynamic_cast<const GCTAEventList*>(cta->events()));
988 1082
989 - } 1083 + // Fall through if CTA observation does not contain an event list
  1084 + if (list == NULL) {
  1085 + continue;
  1086 + }
990 1087
991 - } // endif: list was valid 1088 + // If there are no energy boundaries then read the "emin" and "emax"
  1089 + // user parameters and add them
  1090 + if (list->ebounds().is_empty()) {
  1091 + if (has_par("emin") && has_par("emax")) {
  1092 + double emin((*this)["emin"].real());
  1093 + double emax((*this)["emax"].real());
  1094 + GEbounds ebounds(GEnergy(emin, "TeV"),
  1095 + GEnergy(emax, "TeV"));
  1096 + list->ebounds(ebounds);
  1097 + }
  1098 + }
992 1099
993 - } // endif: observation was CTA 1100 + // If there is no RoI then read the "rad" user parameters and use
  1101 + // the pointing direction to set the RoI
  1102 + if (list->roi().radius() == 0) {
  1103 + if (has_par("rad")) {
  1104 + double rad = (*this)["rad"].real();
  1105 + GCTARoi roi(GCTAInstDir(cta->pointing().dir()), rad);
  1106 + list->roi(roi);
  1107 + }
  1108 + }
994 1109
995 } // endfor: looped over observations 1110 } // endfor: looped over observations
996 1111
src/support/ctool.hpp
@@ -72,6 +72,7 @@ protected: @@ -72,6 +72,7 @@ protected:
72 const bool& read_ahead(void) const; 72 const bool& read_ahead(void) const;
73 const GTimeReference& time_reference(void) const; 73 const GTimeReference& time_reference(void) const;
74 GObservations get_observations(const bool& get_response = true); 74 GObservations get_observations(const bool& get_response = true);
  75 + void setup_observations(GObservations& obs);
75 76
76 // Protected methods that create objects from user parameters 77 // Protected methods that create objects from user parameters
77 GEbounds create_ebounds(void); 78 GEbounds create_ebounds(void);