36 #define G_PROCESS "ctselect::process()"
37 #define G_GET_PARAMETERS "ctselect::get_parameters()"
38 #define G_SELECT_EVENTS "ctselect::select_events(GCTAObservation*, "\
39 "std::string&, std::string&, std::string&)"
40 #define G_SET_EBOUNDS "ctselect::set_ebounds(GCTAObservation*, GEbounds&)"
190 this->GApplication::clear();
224 log_header1(TERSE,
"Event selection");
227 int n_observations = 0;
230 for (
int i = 0; i <
m_obs.size(); ++i) {
238 m_evtname.push_back(gammalib::extname_cta_events);
239 m_gtiname.push_back(gammalib::extname_gti);
242 GCTAObservation*
obs =
dynamic_cast<GCTAObservation*
>(
m_obs[i]);
246 std::string msg =
" Skipping "+
m_obs[i]->instrument()+
248 log_string(NORMAL, msg);
253 if (obs->eventtype() ==
"CountsCube") {
254 std::string msg =
" Skipping binned "+
m_obs[i]->instrument()+
256 log_string(NORMAL, msg);
268 if (fname.has_extname()) {
274 log_value(NORMAL,
"Input filename",
m_infiles[i]);
275 log_value(NORMAL,
"Event extension name",
m_evtname[i]);
276 log_value(NORMAL,
"GTI extension name",
m_gtiname[i]);
279 if (obs->events()->size() == 0) {
280 log_string(NORMAL,
" Warning: No events in event file \""+
281 m_infiles[i]+
"\". Event selection skipped.");
288 if (!message.empty()) {
289 throw GException::invalid_value(
G_PROCESS, message);
294 std::string filename = gammalib::tmpnam();
304 GFits tmpfile(filename);
305 log.header3(
"FITS file content of temporary file");
306 log << tmpfile << std::endl;
311 if (!filename.empty()) {
313 if (!message.empty()) {
314 throw GException::invalid_value(
G_PROCESS, message);
322 std::remove(filename.c_str());
328 if (n_observations > 1) {
336 if ((*
this)[
"publish"].
boolean()) {
367 log_header1(TERSE, gammalib::number(
"Save event list",
m_obs.size()));
392 log_header1(TERSE, gammalib::number(
"Publish event list",
m_obs.size()));
395 for (
int i = 0; i <
m_obs.size(); ++i) {
398 GCTAObservation*
obs =
dynamic_cast<GCTAObservation*
>(
m_obs[i]);
404 if (obs->events()->size() != 0) {
407 std::string user_name(name);
408 if (user_name.empty()) {
414 user_name += gammalib::str(i);
418 log_value(NORMAL,
"Event list name", user_name);
425 fits.publish(gammalib::extname_cta_events, user_name);
548 if ((*
this)[
"emin"].is_valid() && (*
this)[
"emax"].is_valid()) {
549 m_emin = (*this)[
"emin"].real();
550 m_emax = (*this)[
"emax"].real();
558 std::string phase_expr = gammalib::strip_whitespace(gammalib::tolower(
559 (*
this)[
"phase"].
string()));
562 if ((phase_expr.length() > 0) && (phase_expr !=
"none")) {
565 std::string phase_expr = (*this)[
"phase"].string();
568 std::vector<std::string> phase_splits = gammalib::split(phase_expr,
",");
569 for (
int i = 0; i < phase_splits.size(); ++i) {
572 std::vector<std::string> phase_toks =
573 gammalib::split(phase_splits[i],
":");
576 if (phase_toks.size() != 2) {
577 std::string msg =
"Invalid phase selection string \""+
578 phase_splits[i]+
"\" encountered. The phase "
579 "selection string requires a minimum and "
580 "maximum value separated by a colon, e.g. "
586 if (gammalib::strip_whitespace(phase_toks[0]).length() == 0) {
587 std::string msg =
"Invalid phase selection string \""+
588 phase_splits[i]+
"\" encountered. The phase "
589 "selection string requires a minimum and "
590 "maximum value separated by a colon, e.g. "
594 if (gammalib::strip_whitespace(phase_toks[1]).length() == 0) {
595 std::string msg =
"Invalid phase selection string \""+
596 phase_splits[i]+
"\" encountered. The phase "
597 "selection string requires a minimum and "
598 "maximum value separated by a colon, e.g. "
604 double phase_min = gammalib::todouble(phase_toks[0]);
605 double phase_max = gammalib::todouble(phase_toks[1]);
609 if (phase_min < 0.0 || phase_min > 1.0) {
610 std::string msg =
"Phase minimum "+gammalib::str(phase_min)+
611 " outside the valid range [0,1]. Please "
612 "specify phase interval boundaries comprised "
616 if (phase_max < 0.0 || phase_max > 1.0) {
617 std::string msg =
"Phase maximum "+gammalib::str(phase_max)+
618 " outside the valid range [0,1]. Please "
619 "specify phase interval boundaries comprised "
625 m_phases.append(phase_min, phase_max);
635 m_expr = (*this)[
"expr"].string();
636 m_chatter =
static_cast<GChatter
>((*this)[
"chatter"].integer());
640 (*this)[
"outobs"].query();
641 (*this)[
"prefix"].query();
674 const std::string& filename,
675 const std::string& evtname,
676 const std::string& gtiname)
679 log_header3(NORMAL,
"Event selection");
682 std::string selection;
686 bool remove_all =
false;
689 GCTAEventList* list =
690 static_cast<GCTAEventList*
>(
const_cast<GEvents*
>(obs->events()));
694 GGti user_gti =
get_gti(list->gti().reference());
695 GTime timemin = user_gti.tstart();
696 GTime timemax = user_gti.tstop();
700 GCTARoi old_roi = list->roi();
701 GEbounds old_ebounds = list->ebounds();
707 GEbounds ebounds =
set_ebounds(obs, list->ebounds());
708 if (ebounds.size() >= 1) {
709 emin = ebounds.emin(0).TeV();
710 emax = ebounds.emax(0).TeV();
714 bool select_expr = (gammalib::strip_whitespace(
m_expr).length() > 0);
717 GCTARoi roi =
get_roi(obs->pointing());
718 double ra = roi.centre().dir().ra_deg();
719 double dec = roi.centre().dir().dec_deg();
720 double rad = roi.radius();
723 bool select_roi = roi.is_valid();
727 bool enforced_roi =
false;
732 if (!user_gti.is_empty()) {
737 ((GGti*)(&list->gti()))->reduce(timemin, timemax);
742 GGti gti = list->gti();
745 if (!user_gti.is_empty()) {
748 if (list->gti().is_empty()) {
750 log_value(NORMAL,
"Time range",
"None. There is no overlap "
751 "between existing and requested time interval.");
759 double tmin = gti.tstart().convert(gti.reference());
760 double tmax = gti.tstop().convert(gti.reference());
765 sprintf(cmin,
"%.8f", tmin);
766 sprintf(cmax,
"%.8f", tmax);
767 selection =
"TIME >= "+std::string(cmin)+
" && TIME <= "+std::string(cmax);
769 log_value(NORMAL,
"Time range (MJD)",
770 gammalib::str(gti.tstart().mjd())+
" - "+
771 gammalib::str(gti.tstop().mjd())+
" days");
772 log_value(NORMAL,
"Time range (UTC)",
773 gti.tstart().utc()+
" - "+gti.tstop().utc());
774 log_value(NORMAL,
"Time range (MET)",
775 gammalib::str(tmin)+
" - "+gammalib::str(tmax)+
" seconds");
785 if (list->has_phase()) {
788 for (
int i = 0; i <
m_phases.size(); ++i) {
792 selection += add +
"( ";
801 sprintf(cmin,
"%.8f",
m_phases.pmin(i));
802 sprintf(cmax,
"%.8f",
m_phases.pmax(i));
803 selection +=
"(PHASE >= "+std::string(cmin)+
" && PHASE <= "+
804 std::string(cmax)+
")";
806 log_value(NORMAL,
"Phase range "+gammalib::str(i+1),
807 gammalib::str(
m_phases.pmin(i))+
" - "+
823 log_value(NORMAL,
"Phase range",
"Event list has no \"PHASE\" "
824 "column. Phase selection skipped.");
834 log << gammalib::parformat(
"Selected energy range");
835 if (emin > 0.0 && emax > 0.0) {
837 log <<
"None. There is no overlap between existing ";
838 log <<
"and requested energy range." << std::endl;
841 log << emin <<
" - " << emax <<
" TeV" << std::endl;
844 else if (emin > 0.0) {
845 log <<
"> " << emin <<
" TeV" << std::endl;
847 else if (emax > 0.0) {
848 log <<
"< " << emax <<
" TeV" << std::endl;
851 log <<
"None" << std::endl;
856 if (emin > 0.0 && emax > 0.0) {
863 sprintf(cmin,
"%.8f", emin);
864 sprintf(cmax,
"%.8f", emax);
865 selection += add +
"ENERGY >= " + std::string(cmin)+
866 " && ENERGY <= " + std::string(cmax);
870 else if (emin > 0.0) {
872 sprintf(cmin,
"%.8f", emin);
873 selection += add +
"ENERGY >= " + std::string(cmin);
876 else if (emax > 0.0) {
878 sprintf(cmax,
"%.8f", emax);
879 selection += add +
"ENERGY <= " + std::string(cmax);
892 double original_rad = rad;
895 log_value(NORMAL,
"Requested RoI",
896 "Centre(RA,DEC)=("+gammalib::str(ra)+
", "+
897 gammalib::str(dec)+
") deg, Radius="+gammalib::str(rad)+
902 double roi_radius = list->roi().radius();
903 if (roi_radius > 0.0) {
904 GSkyDir roi_centre = list->roi().centre().dir();
905 log_value(NORMAL,
"RoI of data",
906 "Centre(RA,DEC)=("+gammalib::str(roi_centre.ra_deg())+
907 ", "+gammalib::str(roi_centre.dec_deg())+
") deg, Radius="+
908 gammalib::str(roi_radius)+
" deg");
910 centre.radec_deg(ra, dec);
911 double distance = centre.dist_deg(roi_centre);
918 if ((distance < 1.e-4) && (rad >= roi_radius)) {
926 else if ((distance + rad - roi_radius) > 1.e-4) {
932 "WARNING: Enforced RoI selection leading to inconsis" \
934 " RoI of data: Centre(RA,DEC)=("+ \
935 gammalib::str(ra)+
", "+gammalib::str(dec)+
") deg, " \
936 "Radius="+gammalib::str(rad)+
" deg.\n" \
937 " Requested RoI: Centre(RA,DEC)=("+ \
938 gammalib::str(roi_centre.ra_deg())+
", "+ \
939 gammalib::str(roi_centre.dec_deg())+
") deg, " \
940 "Radius="+gammalib::str(roi_radius)+
" deg.\n" \
941 " The resulting event list file is not usable " \
942 "for likelihood analysis";
943 log_string(TERSE, msg);
949 "Invalid RoI selection: the new RoI must be enclosed "
950 "in the original RoI";
951 throw GException::invalid_value(
G_PROCESS, msg);
963 log_value(NORMAL,
"Selected RoI",
"None. There is no overlap "
964 "between existing and requested RoI.");
967 log_value(NORMAL,
"Selected RoI",
"Centre(RA,DEC)=("+
968 gammalib::str(ra)+
", "+gammalib::str(dec)+
") deg, "
969 "Radius="+gammalib::str(rad)+
" deg");
976 sprintf(cra,
"%.6f", ra);
977 sprintf(cdec,
"%.6f", dec);
978 sprintf(crad,
"%.6f", rad);
979 selection += add +
"ANGSEP("+std::string(cra)+
"," +
980 std::string(cdec)+
",RA,DEC) <= " +
990 selection += add +
"("+gammalib::strip_whitespace(
m_expr)+
")";
995 log_value(NORMAL,
"cfitsio selection", selection);
998 std::string expression = filename +
"[" + evtname +
"]";
999 if (selection.length() > 0) {
1000 expression +=
"["+selection+
"]";
1004 log_value(NORMAL,
"FITS filename", expression);
1007 GFits file(expression);
1010 log_header3(EXPLICIT,
"FITS file content after selection");
1011 log_string(EXPLICIT, file.print(
m_chatter));
1014 if (!file.contains(evtname)) {
1015 std::string msg =
"No events extension \""+evtname+
"\" found in "
1016 "FITS file. The expression \""+expression+
"\" "
1017 "was used to open the FITS file.";
1023 int nevents = (remove_all) ? 0 : file.table(evtname)->nrows();
1030 GCTAEventList eventlist;
1033 obs->events(eventlist);
1043 list =
static_cast<GCTAEventList*
>(
const_cast<GEvents*
>(obs->events()));
1051 if (select_roi && !enforced_roi) {
1053 skydir.radec_deg(ra, dec);
1054 GCTAInstDir instdir(skydir);
1055 list->roi(GCTARoi(instdir, rad));
1059 else if (old_roi.is_valid()) {
1069 ebounds.append(GEnergy(emin,
"TeV"), GEnergy(emax,
"TeV"));
1070 list->ebounds(ebounds);
1074 else if (old_ebounds.size() > 0) {
1075 list->ebounds(old_ebounds);
1084 obs->ontime(list->gti().ontime());
1085 obs->livetime(list->gti().ontime() * obs->deadc());
1127 if (ebounds.size() > 0) {
1130 if ((emin == 0.0) ||
1131 ((ebounds.emin(0).TeV() > 0.0) && (emin < ebounds.emin(0).TeV()))) {
1132 emin = ebounds.emin(0).TeV();
1136 if ((emax == 0.0) ||
1137 ((ebounds.emax(0).TeV() > 0.0) && (emax > ebounds.emax(0).TeV()))) {
1138 emax = ebounds.emax(0).TeV();
1149 const GCTAResponseIrf* rsp =
dynamic_cast<const GCTAResponseIrf*
>
1152 std::string msg =
"No IRF response attached to given observation.";
1157 double lo_safe_thres = rsp->lo_safe_thres();
1158 double hi_safe_thres = rsp->hi_safe_thres();
1161 if ((lo_safe_thres > 0.0) &&
1162 (hi_safe_thres > 0.0) &&
1163 (lo_safe_thres >= hi_safe_thres)) {
1164 std::string msg =
"IRF \""+obs->name()+
"\" contains an invalid "
1165 "energy range ["+gammalib::str(lo_safe_thres)+
","+
1166 gammalib::str(hi_safe_thres)+
"] TeV.";
1171 if ((emin == 0.0) ||
1172 ((lo_safe_thres > 0.0) && (emin < lo_safe_thres))) {
1173 emin = lo_safe_thres;
1177 if ((emax == 0.0) ||
1178 ((hi_safe_thres > 0.0) && (emax > hi_safe_thres))) {
1179 emax = hi_safe_thres;
1188 double lo_user_thres = obs->lo_user_thres();
1189 double hi_user_thres = obs->hi_user_thres();
1192 if ((lo_user_thres > 0.0) &&
1193 (hi_user_thres > 0.0) &&
1194 (lo_user_thres >= hi_user_thres)) {
1195 std::string msg =
"User energy range ["+gammalib::str(lo_user_thres)+
1196 ","+gammalib::str(hi_user_thres)+
"] TeV is invalid.";
1201 if ((emin == 0.0) ||
1202 ((lo_user_thres > 0.0) && (emin < lo_user_thres))) {
1203 emin = lo_user_thres;
1207 if ((emax == 0.0) ||
1208 ((hi_user_thres > 0.0) && (emax > hi_user_thres))) {
1209 emax = hi_user_thres;
1217 result.append(GEnergy(emin,
"TeV"), GEnergy(emax,
"TeV"));
1235 const std::string& evtname)
const
1238 std::string message =
"";
1241 GFits fits(filename);
1244 if (!fits.contains(evtname)) {
1245 message =
"No \""+evtname+
"\" extension found in input file \""+
1253 GFitsTable* table = fits.table(evtname);
1256 std::vector<std::string> missing;
1259 if (!table->contains(
"TIME")) {
1260 missing.push_back(
"TIME");
1264 if (!table->contains(
"ENERGY")) {
1265 missing.push_back(
"ENERGY");
1269 if (!table->contains(
"RA")) {
1270 missing.push_back(
"RA");
1274 if (!table->contains(
"DEC")) {
1275 missing.push_back(
"DEC");
1279 if (!missing.empty()) {
1280 message =
"The following columns are missing in the "
1281 "\""+evtname+
"\" extension of input file \""+
1283 for (
int i = 0; i < missing.size(); ++i) {
1284 message +=
"\"" + missing[i] +
"\"";
1285 if (i < missing.size()-1) {
1307 if (
m_obs.size() > 0) {
1310 m_outobs = (*this)[
"outobs"].filename();
1313 GCTAObservation*
obs =
dynamic_cast<GCTAObservation*
>(
m_obs[0]);
1325 std::string outfile = fname.url();
1329 if (fname.has_extname()) {
1330 outfile +=
"["+fname.extname()+
"]";
1337 log_value(NORMAL,
"Event list file", outfile);
1367 m_outobs = (*this)[
"outobs"].filename();
1373 for (
int i = 0; i <
m_obs.size(); ++i) {
1376 GCTAObservation*
obs =
dynamic_cast<GCTAObservation*
>(
m_obs[i]);
1395 log_value(NORMAL,
"Event list file", outfile);
1398 obs->eventfile(outfile);
1407 log_value(NORMAL,
"Obs. definition file",
m_outobs);
virtual ~ctselect(void)
Destructor.
void free_members(void)
Delete class members.
void publish(const std::string &name="")
Publish event lists.
double m_emin
Lower energy.
void clear(void)
Clear ctselect tool.
void save_fits(void)
Save event list in FITS format.
std::vector< std::string > m_gtiname
GTI extension names.
const GObservations & obs(void) const
Return observation container.
void save_xml(void)
Save event list(s) in XML format.
double m_emax
Upper energy.
std::vector< std::string > m_evtname
Event extension names.
Data selection tool definition.
GPhases m_phases
Phase intervals.
ctselect(void)
Void constructor.
bool m_forcesel
Enforce RoI selection.
Base class for observation tools.
void free_members(void)
Delete class members.
void init_members(void)
Initialise class members.
bool m_select_energy
Perform energy selection.
void process(void)
Select event data.
std::string m_expr
Selection expression.
void copy_members(const ctselect &app)
Copy class members.
std::string m_usethres
Energy threshold type.
GEbounds set_ebounds(GCTAObservation *obs, const GEbounds &ebounds) const
Return energy boundaries for a given observation.
bool m_select_phase
Perform phase selection.
void init_members(void)
Initialise class members.
void save(void)
Save the selected event list(s)
ctobservation & operator=(const ctobservation &app)
Assignment operator.
GChatter m_chatter
Chattiness.
ctselect & operator=(const ctselect &app)
Assignment operator.
GObservations m_obs
Observation container.
void get_parameters(void)
Get application parameters.
std::string m_outobs
Output event list or XML file.
std::string check_infile(const std::string &filename, const std::string &evtname) const
Check input filename.
void select_events(GCTAObservation *obs, const std::string &filename, const std::string &evtname, const std::string >iname)
Select events.
std::vector< std::string > m_infiles
Input event filenames.