Commit 98c5cdc0aeb766e4269dafa77da4f527d5594d57

Authored by Jürgen Knödlseder
2 parents 34fcdde8 272721f9

Merge branch '1039-allow-energy-thresholds-in-ctselect' into integration

ChangeLog
1   -2014-10-30 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu>
  1 +2014-11-08 Juergen Knoedlseder <jurgen.knodlseder@irap.omp.eu>
2 2  
3 3 * Version 0.8.0 released
4 4 ========================
... ... @@ -17,6 +17,7 @@
17 17 ctmodel now always creates a single model cube
18 18 Support energy dispersion in ctobssim, ctlike and ctmodel
19 19 Correct ctobssim for thread safety
  20 + Support save and user energy thresholds in ctselect
20 21 Correctly apply time references in ctselect
21 22 Reduce memory usage
22 23 Move obsutils to ctools namespace
... ...
... ... @@ -2,7 +2,7 @@ New Features and Important Changes in ctools 00-08-00
2 2  
3 3 Author(s) : Juergen Knoedlseder
4 4  
5   -30 October 2014
  5 +8 November 2014
6 6  
7 7  
8 8 1. Introduction
... ... @@ -65,6 +65,30 @@ tmin and tmax parameters are given in CTA reference time, they are
65 65 internally converted to the reference time of the GTI before event
66 66 selection (#1329).
67 67  
  68 +ctselect now supports save and user energy thresholds (#1039). There
  69 +are three different possibilities for specifying a threshold. They
  70 +are toggled using the hidden usethres parameter:
  71 +
  72 +NONE: Use the emin and emax parameters to select an energy range.
  73 + This is the default mode which is identical to the previous
  74 + workings. The only difference is now that if the emin and/or
  75 + emax parameter is set to 0.0, the threshold will be ignored.
  76 + This allows to select all events above or below a given
  77 + threshold.
  78 +
  79 +DEFAULT: Use the save threshold that is found in the effective
  80 + area FITS header keywords LO_THRES and HI_THRES. If the
  81 + emin and emax parameters are non-zero, they will provide
  82 + an additional energy constraint.
  83 +
  84 +USER: Use the user specified threshold that are found in the
  85 + XML observation definition file. If the emin and emax
  86 + parameters are non-zero, they will provide an additional
  87 + energy constraint.
  88 +
  89 +Note that any existing energy boundaries will be used as envelope,
  90 +i.e. a wider energy threshold specification will have no effect.
  91 +
68 92  
69 93 7. ctbin - CTA event binning
70 94 ----------------------------
... ...
src/ctselect/ctselect.cpp
... ... @@ -36,6 +36,8 @@
36 36 #define G_RUN "ctselect::run()"
37 37 #define G_SELECT_EVENTS "ctselect::select_events(GCTAObservation*, "\
38 38 "std::string&)"
  39 +#define G_SET_EBOUNDS "ctselect::set_ebounds(GCTAObservation*, GEbounds&)"
  40 +#define G_GET_PARAMETERS "ctselect::get_parameters()"
39 41  
40 42 /* __ Debug definitions __________________________________________________ */
41 43  
... ... @@ -397,13 +399,14 @@ void ctselect::init_members(void)
397 399 m_emin = 0.0;
398 400 m_emax = 0.0;
399 401 m_expr.clear();
  402 + m_usethres.clear();
400 403  
401 404 // Initialise protected members
402 405 m_obs.clear();
403 406 m_infiles.clear();
404 407 m_timemin.clear();
405 408 m_timemax.clear();
406   - m_use_xml = false;
  409 + m_use_xml = false;
407 410  
408 411 // Set CTA time reference. G_CTA_MJDREF is the CTA reference MJD,
409 412 // which is defined in GCTALib.hpp. This is somehow a kluge. We need
... ... @@ -423,18 +426,19 @@ void ctselect::init_members(void)
423 426 void ctselect::copy_members(const ctselect& app)
424 427 {
425 428 // Copy parameters
426   - m_infile = app.m_infile;
427   - m_outfile = app.m_outfile;
428   - m_prefix = app.m_prefix;
429   - m_usepnt = app.m_usepnt;
430   - m_ra = app.m_ra;
431   - m_dec = app.m_dec;
432   - m_rad = app.m_rad;
433   - m_tmin = app.m_tmin;
434   - m_tmax = app.m_tmax;
435   - m_emin = app.m_emin;
436   - m_emax = app.m_emax;
437   - m_expr = app.m_expr;
  429 + m_infile = app.m_infile;
  430 + m_outfile = app.m_outfile;
  431 + m_prefix = app.m_prefix;
  432 + m_usepnt = app.m_usepnt;
  433 + m_ra = app.m_ra;
  434 + m_dec = app.m_dec;
  435 + m_rad = app.m_rad;
  436 + m_tmin = app.m_tmin;
  437 + m_tmax = app.m_tmax;
  438 + m_emin = app.m_emin;
  439 + m_emax = app.m_emax;
  440 + m_expr = app.m_expr;
  441 + m_usethres = app.m_usethres;
438 442  
439 443 // Copy protected members
440 444 m_obs = app.m_obs;
... ... @@ -514,12 +518,21 @@ void ctselect::get_parameters(void)
514 518 m_ra = (*this)["ra"].real();
515 519 m_dec = (*this)["dec"].real();
516 520 }
517   - m_rad = (*this)["rad"].real();
518   - m_tmin = (*this)["tmin"].real();
519   - m_tmax = (*this)["tmax"].real();
520   - m_emin = (*this)["emin"].real();
521   - m_emax = (*this)["emax"].real();
522   - m_expr = (*this)["expr"].string();
  521 + m_rad = (*this)["rad"].real();
  522 + m_tmin = (*this)["tmin"].real();
  523 + m_tmax = (*this)["tmax"].real();
  524 + m_emin = (*this)["emin"].real();
  525 + m_emax = (*this)["emax"].real();
  526 + m_expr = (*this)["expr"].string();
  527 + m_usethres = (*this)["usethres"].string();
  528 +
  529 + // Check parameters (Kluge as the parfile interface does not yet check
  530 + // parameters; this can be removed once this is done properly)
  531 + if (m_usethres != "NONE" && m_usethres != "DEFAULT" && m_usethres != "USER") {
  532 + std::string msg = "Parameter \"usethres\" must be one of \"NONE\", "
  533 + "\"DEFAULT\" or \"USER\".";
  534 + throw GException::invalid_value(G_GET_PARAMETERS, msg);
  535 + }
523 536  
524 537 // Optionally read ahead parameters so that they get correctly
525 538 // dumped into the log file
... ... @@ -569,16 +582,25 @@ void ctselect::select_events(GCTAObservation* obs, const std::string&amp; filename)
569 582 // Initialise selection flags
570 583 bool remove_all = false;
571 584  
572   - // Analyse task parameters to see what selections are required
  585 + // Get CTA event list pointer
  586 + GCTAEventList* list =
  587 + static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events()));
  588 +
  589 + // Get energy boundaries
  590 + double emin = 0.0;
  591 + double emax = 0.0;
  592 + GEbounds ebounds = set_ebounds(obs, list->ebounds());
  593 + if (ebounds.size() >= 1) {
  594 + emin = ebounds.emin(0).TeV();
  595 + emax = ebounds.emax(0).TeV();
  596 + }
  597 +
  598 + // Analyse parameters to see what selections are required
573 599 bool select_time = (m_tmin != 0.0 || m_tmax != 0.0);
574   - bool select_energy = true;
  600 + bool select_energy = (emin != 0.0 || emax != 0.0);
575 601 bool select_roi = ((m_dec != -1.0 && m_ra != -1.0 && m_rad != -1.0) || m_usepnt);
576 602 bool select_expr = (gammalib::strip_whitespace(m_expr).length() > 0);
577 603  
578   - // Set energy selection
579   - double emin = m_emin;
580   - double emax = m_emax;
581   -
582 604 // Set RA/DEC selection. If the "usepnt" parameter is set to true then
583 605 // use the pointing direction as the ROI centre.
584 606 double ra = m_ra;
... ... @@ -589,10 +611,6 @@ void ctselect::select_events(GCTAObservation* obs, const std::string&amp; filename)
589 611 ra = pnt.dir().ra_deg();
590 612 dec = pnt.dir().dec_deg();
591 613 }
592   -
593   - // Get CTA event list pointer
594   - GCTAEventList* list =
595   - static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events()));
596 614  
597 615 // Set time selection interval. We make sure here that the time selection
598 616 // interval cannot be wider than the GTIs covering the data. This is done
... ... @@ -638,48 +656,58 @@ void ctselect::select_events(GCTAObservation* obs, const std::string&amp; filename)
638 656 selection += " && ";
639 657 }
640 658  
641   - // Log the requested energy range
  659 + // Log the requested energy selection
642 660 if (logTerse()) {
643   - log << gammalib::parformat("Requested energy range");
644   - log << m_emin << " - " << m_emax << " TeV" << std::endl;
645   - }
646   -
647   - // Set the energy boundaries. Make sure that they overlap with any
648   - // existing energy boundaries
649   - const GEbounds& ebounds = list->ebounds();
650   - if (ebounds.size() >= 1) {
651   - if (logTerse()) {
652   - log << gammalib::parformat("Energy range of data");
653   - log << ebounds.emin(0).TeV() << " - ";
654   - log << ebounds.emax(0).TeV() << " TeV" << std::endl;
  661 + log << gammalib::parformat("Selected energy range");
  662 + if (emin > 0.0 && emax > 0.0) {
  663 + if (emin >= emax) {
  664 + log << "None. There is no overlap between existing ";
  665 + log << "and requested energy range." << std::endl;
  666 + }
  667 + else {
  668 + log << emin << " - " << emax << " TeV" << std::endl;
  669 + }
  670 + }
  671 + else if (emin > 0.0) {
  672 + log << "> " << emin << " TeV" << std::endl;
655 673 }
656   - if (emin < ebounds.emin(0).TeV()) {
657   - emin = ebounds.emin(0).TeV();
  674 + else if (emax > 0.0) {
  675 + log << "< " << emax << " TeV" << std::endl;
658 676 }
659   - if (emax > ebounds.emax(0).TeV()) {
660   - emax = ebounds.emax(0).TeV();
  677 + else {
  678 + log << "None" << std::endl;
661 679 }
662 680 }
663 681  
664   - // Log the selected energy range
665   - if (logTerse()) {
666   - log << gammalib::parformat("Selected energy range");
  682 + // Apply the requested energy selection
  683 + if (emin > 0.0 && emax > 0.0) {
667 684 if (emin >= emax) {
668   - log << "None. There is no overlap between existing ";
669   - log << "and requested energy range." << std::endl;
  685 + remove_all = true;
670 686 }
671 687 else {
672   - log << emin << " - " << emax << " TeV" << std::endl;
  688 + char cmin[80];
  689 + char cmax[80];
  690 + sprintf(cmin, "%.8f", emin);
  691 + sprintf(cmax, "%.8f", emax);
  692 + selection += "ENERGY >= "+std::string(cmin)+" && ENERGY <= "+
  693 + std::string(cmax);
673 694 }
674 695 }
  696 + else if (emin > 0.0) {
  697 + char cmin[80];
  698 + sprintf(cmin, "%.8f", emin);
  699 + selection += "ENERGY >= "+std::string(cmin);
  700 + }
  701 + else if (emax > 0.0) {
  702 + char cmax[80];
  703 + sprintf(cmax, "%.8f", emax);
  704 + selection += "ENERGY <= "+std::string(cmax);
  705 + }
  706 + else {
  707 + log << "None" << std::endl;
  708 + remove_all = true;
  709 + }
675 710  
676   - // Format energy selection
677   - char cmin[80];
678   - char cmax[80];
679   - sprintf(cmin, "%.8f", emin);
680   - sprintf(cmax, "%.8f", emax);
681   - selection += "ENERGY >= "+std::string(cmin)+" && ENERGY <= "+std::string(cmax);
682   -
683 711 } // endif: made energy selection
684 712  
685 713 // Make ROI selection
... ... @@ -841,6 +869,137 @@ void ctselect::select_events(GCTAObservation* obs, const std::string&amp; filename)
841 869  
842 870  
843 871 /***********************************************************************//**
  872 + * @brief Return energy boundaries for a given observation
  873 + *
  874 + * @param[in] obs Pointer to CTA observation.
  875 + * @param[in] ebounds Current energy boundaries.
  876 + * @return Energy boundaries.
  877 + *
  878 + * Returns the energy boundaries for a given observation. Depending on the
  879 + * value of the usethres parameter, the following values will be
  880 + * returned:
  881 + *
  882 + * NONE: [max(emin,emin_exist),min(emax,emax_exist)]
  883 + * DEFAULT: [max(emin,emin_exist,emin_save),min(emax,emax_exist,emax_save)]
  884 + * USER: [max(emin,emin_exist,emin_user),min(emax,emax_exist,emax_user)]
  885 + *
  886 + * where
  887 + *
  888 + * emin is the value of the emin parameter
  889 + * emax is the value of the emax parameter
  890 + * emin_exist is the value of any existing minimum boundary
  891 + * emax_exist is the value of any existing maximum boundary
  892 + * emin_save is the lower save threshold
  893 + * emax_save is the upper save threshold
  894 + * emin_user is the lower user threshold
  895 + * emax_user is the upper user threshold
  896 + *
  897 + * Any threshold value of 0 will be ignored.
  898 + ***************************************************************************/
  899 +GEbounds ctselect::set_ebounds(GCTAObservation* obs, const GEbounds& ebounds) const
  900 +{
  901 + // Set emin and emax values from user parameters
  902 + double emin = m_emin;
  903 + double emax = m_emax;
  904 +
  905 + // Check if we have already energy boundaries
  906 + if (ebounds.size() > 0) {
  907 +
  908 + // Raise minimum energy to lower boundary
  909 + if ((emin == 0.0) ||
  910 + ((ebounds.emin(0).TeV() > 0.0) && (emin < ebounds.emin(0).TeV()))) {
  911 + emin = ebounds.emin(0).TeV();
  912 + }
  913 +
  914 + // Lower maximum energy to upper boundary
  915 + if ((emax == 0.0) ||
  916 + ((ebounds.emax(0).TeV() > 0.0) && (emax > ebounds.emax(0).TeV()))) {
  917 + emax = ebounds.emax(0).TeV();
  918 + }
  919 +
  920 + } // endif: there were already energy boundaries
  921 +
  922 + // Check if default threshold (the one from the IRF, also known as save
  923 + // threshold) should be applied
  924 + if (m_usethres == "DEFAULT") {
  925 +
  926 + // Get CTA IRF respsonse pointer and throw exception if we don't
  927 + // find IRF
  928 + const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>(obs->response());
  929 + if (rsp == NULL) {
  930 + std::string msg = "No IRF response attached to given observation.";
  931 + throw GException::invalid_value(G_SET_EBOUNDS, msg);
  932 + }
  933 +
  934 + // Retrieve energy range from response information
  935 + double lo_save_thres = rsp->lo_save_thres();
  936 + double hi_save_thres = rsp->hi_save_thres();
  937 +
  938 + // Check threshold information for validity
  939 + if ((lo_save_thres > 0.0) &&
  940 + (hi_save_thres > 0.0) &&
  941 + (lo_save_thres >= hi_save_thres)) {
  942 + std::string msg = "IRF \""+obs->name()+"\" contains an invalid "
  943 + "energy range ["+gammalib::str(lo_save_thres)+","+
  944 + gammalib::str(hi_save_thres)+"] TeV.";
  945 + throw GException::invalid_value(G_SET_EBOUNDS, msg);
  946 + }
  947 +
  948 + // Raise minimum energy to lower threshold (if lower threshold exists)
  949 + if ((emin == 0.0) ||
  950 + ((lo_save_thres > 0.0) && (emin < lo_save_thres))) {
  951 + emin = lo_save_thres;
  952 + }
  953 +
  954 + // Lower maximum energy to upper threshold (if upper threshold exists)
  955 + if ((emax == 0.0) ||
  956 + ((hi_save_thres > 0.0) && (emax > hi_save_thres))) {
  957 + emax = hi_save_thres;
  958 + }
  959 +
  960 + } //endif: usethres was "DEFAULT"
  961 +
  962 + // ... otherwise check if a user specified threshold should be applied
  963 + else if (m_usethres == "USER") {
  964 +
  965 + // Retrieve thresholds from observation
  966 + double lo_user_thres = obs->lo_user_thres();
  967 + double hi_user_thres = obs->hi_user_thres();
  968 +
  969 + // Check threshold information for validity
  970 + if ((lo_user_thres > 0.0) &&
  971 + (hi_user_thres > 0.0) &&
  972 + (lo_user_thres >= hi_user_thres)) {
  973 + std::string msg = "User energy range ["+gammalib::str(lo_user_thres)+
  974 + ","+gammalib::str(hi_user_thres)+"] TeV is invalid.";
  975 + throw GException::invalid_value(G_SET_EBOUNDS, msg);
  976 + }
  977 +
  978 + // Raise minimum energy to lower threshold (if lower threshold exists)
  979 + if ((emin == 0.0) ||
  980 + ((lo_user_thres > 0.0) && (emin < lo_user_thres))) {
  981 + emin = lo_user_thres;
  982 + }
  983 +
  984 + // Lower maximum energy to upper threshold (if upper threshold exists)
  985 + if ((emax == 0.0) ||
  986 + ((hi_user_thres > 0.0) && (emax > hi_user_thres))) {
  987 + emax = hi_user_thres;
  988 + }
  989 +
  990 + } // endif: usethres was "USER
  991 +
  992 + // Set selection energy boundaries
  993 + GEbounds result;
  994 + result.append(GEnergy(emin, "TeV"), GEnergy(emax, "TeV"));
  995 +
  996 + // Return result
  997 + return result;
  998 +
  999 +}
  1000 +
  1001 +
  1002 +/***********************************************************************//**
844 1003 * @brief Check input filename
845 1004 *
846 1005 * @param[in] filename File name.
... ... @@ -1060,4 +1219,3 @@ void ctselect::save_event_list(const GCTAObservation* obs,
1060 1219 // Return
1061 1220 return;
1062 1221 }
1063   -
... ...
src/ctselect/ctselect.hpp
... ... @@ -71,6 +71,8 @@ protected:
71 71 void free_members(void);
72 72 void get_parameters(void);
73 73 void select_events(GCTAObservation* obs, const std::string& filename);
  74 + GEbounds set_ebounds(GCTAObservation* obs,
  75 + const GEbounds& ebounds) const;
74 76 std::string check_infile(const std::string& filename) const;
75 77 std::string set_outfile_name(const std::string& filename) const;
76 78 void save_fits(void);
... ... @@ -92,6 +94,7 @@ protected:
92 94 double m_emin; //!< Lower energy
93 95 double m_emax; //!< Upper energy
94 96 std::string m_expr; //!< Selection expression
  97 + std::string m_usethres; //!< Energy threshold type
95 98  
96 99 // Protected members
97 100 GObservations m_obs; //!< Observations container
... ...
src/ctselect/ctselect.par
... ... @@ -23,15 +23,16 @@ prefix, s, h, &quot;selected_&quot;,,,&quot;Prefix for event lists in observation definition fi
23 23 #
24 24 # Selection parameters
25 25 #=====================
26   -usepnt, b, h, no,,, "Use pointing instead of RA/DEC parameters?"
27   -ra, r, a, 83.63, 0,360, "RA for ROI centre (degrees)"
28   -dec, r, a, 22.01, -90, 90, "Dec for ROI centre (degrees)"
29   -rad, r, a, 3.0, 0,180, "Radius of ROI (degrees)"
30   -tmin, r, a, 0.0, 0, , "Start time (CTA MET in seconds)"
31   -tmax, r, a, 0.0, 0, , "End time (CTA MET in seconds)"
32   -emin, r, a, 0.1, 0, , "Lower energy limit (TeV)"
33   -emax, r, a, 100.0, 0, , "Upper energy limit (TeV)"
34   -expr, s, h, "", , , "Additional selection expression"
  26 +usepnt, b, h, no,,, "Use pointing instead of RA/DEC parameters?"
  27 +ra, r, a, 83.63, 0,360, "RA for ROI centre (degrees)"
  28 +dec, r, a, 22.01, -90, 90, "Dec for ROI centre (degrees)"
  29 +rad, r, a, 3.0, 0,180, "Radius of ROI (degrees)"
  30 +tmin, r, a, 0.0, 0, , "Start time (CTA MET in seconds)"
  31 +tmax, r, a, 0.0, 0, , "End time (CTA MET in seconds)"
  32 +emin, r, a, 0.1, 0, , "Lower energy limit (TeV)"
  33 +emax, r, a, 100.0, 0, , "Upper energy limit (TeV)"
  34 +expr, s, h, "", , , "Additional selection expression"
  35 +usethres,s, h,"NONE",NONE|DEFAULT|USER,,"Energy threshold type (NONE|DEFAULT|USER)"
35 36 #
36 37 # Standard parameters
37 38 #====================
... ...