ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
ctselect.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * ctselect - Data selection tool *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2010-2022 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file ctselect.cpp
23  * @brief Data selection tool definition
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <cstdlib>
32 #include "ctselect.hpp"
33 #include "GTools.hpp"
34 
35 /* __ Method name definitions ____________________________________________ */
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&)"
41 
42 /* __ Debug definitions __________________________________________________ */
43 
44 /* __ Coding definitions _________________________________________________ */
45 
46 
47 /*==========================================================================
48  = =
49  = Constructors/destructors =
50  = =
51  ==========================================================================*/
52 
53 /***********************************************************************//**
54  * @brief Void constructor
55  ***************************************************************************/
57 {
58  // Initialise members
59  init_members();
60 
61  // Return
62  return;
63 }
64 
65 
66 /***********************************************************************//**
67  * @brief Observations constructor
68  *
69  * param[in] obs Observation container.
70  *
71  * Creates an instance of the class that is initialised using the information
72  * provided in an observation container.
73  ***************************************************************************/
74 ctselect::ctselect(const GObservations& obs) :
75  ctobservation(CTSELECT_NAME, VERSION, obs)
76 {
77  // Initialise members
78  init_members();
79 
80  // Return
81  return;
82 }
83 
84 
85 
86 
87 /***********************************************************************//**
88  * @brief Command line constructor
89  *
90  * @param[in] argc Number of arguments in command line.
91  * @param[in] argv Array of command line arguments.
92  ***************************************************************************/
93 ctselect::ctselect(int argc, char *argv[]) :
94  ctobservation(CTSELECT_NAME, VERSION, argc, argv)
95 {
96  // Initialise members
97  init_members();
98 
99  // Return
100  return;
101 }
102 
103 
104 /***********************************************************************//**
105  * @brief Copy constructor
106  *
107  * @param[in] app Application.
108  ***************************************************************************/
110 {
111  // Initialise members
112  init_members();
113 
114  // Copy members
115  copy_members(app);
116 
117  // Return
118  return;
119 }
120 
121 
122 /***********************************************************************//**
123  * @brief Destructor
124  ***************************************************************************/
126 {
127  // Free members
128  free_members();
129 
130  // Return
131  return;
132 }
133 
134 
135 /*==========================================================================
136  = =
137  = Operators =
138  = =
139  ==========================================================================*/
140 
141 /***********************************************************************//**
142  * @brief Assignment operator
143  *
144  * @param[in] app Application.
145  * @return Application.
146  ***************************************************************************/
148 {
149  // Execute only if object is not identical
150  if (this != &app) {
151 
152  // Copy base class members
153  this->ctobservation::operator=(app);
154 
155  // Free members
156  free_members();
157 
158  // Initialise members
159  init_members();
160 
161  // Copy members
162  copy_members(app);
163 
164  } // endif: object was not identical
165 
166  // Return this object
167  return *this;
168 }
169 
170 
171 /*==========================================================================
172  = =
173  = Public methods =
174  = =
175  ==========================================================================*/
176 
177 /***********************************************************************//**
178  * @brief Clear ctselect tool
179  *
180  * Clears ctselect tool.
181  ***************************************************************************/
182 void ctselect::clear(void)
183 {
184  // Free members
185  free_members();
187  this->ctool::free_members();
188 
189  // Clear base class (needed to conserve tool name and version)
190  this->GApplication::clear();
191 
192  // Initialise members
193  this->ctool::init_members();
195  init_members();
196 
197  // Write header into logger
198  log_header();
199 
200  // Return
201  return;
202 }
203 
204 
205 /***********************************************************************//**
206  * @brief Select event data
207  *
208  * This method reads in the application parameters and loops over all
209  * observations that were found to perform an event selection. Event
210  * selection is done by writing each observation to a temporary file and
211  * re-opening the temporary file using the cfitsio event filter syntax.
212  * The temporary file is deleted after this action so that no disk overflow
213  * will occur.
214  ***************************************************************************/
216 {
217  // Get parameters
218  get_parameters();
219 
220  // Write input observation container into logger
221  log_observations(NORMAL, m_obs, "Input observation");
222 
223  // Write header into logger
224  log_header1(TERSE, "Event selection");
225 
226  // Initialise counters
227  int n_observations = 0;
228 
229  // Loop over all observation in the container
230  for (int i = 0; i < m_obs.size(); ++i) {
231 
232  // Write header for the current observation
233  log_header3(TERSE, get_obs_header(m_obs[i]));
234 
235  // Initialise event input and output filenames and the event
236  // and GTI extension names
237  m_infiles.push_back("");
238  m_evtname.push_back(gammalib::extname_cta_events);
239  m_gtiname.push_back(gammalib::extname_gti);
240 
241  // Get CTA observation
242  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
243 
244  // Skip observation if it's not CTA
245  if (obs == NULL) {
246  std::string msg = " Skipping "+m_obs[i]->instrument()+
247  " observation";
248  log_string(NORMAL, msg);
249  continue;
250  }
251 
252  // Skip observation if we have a binned observation
253  if (obs->eventtype() == "CountsCube") {
254  std::string msg = " Skipping binned "+m_obs[i]->instrument()+
255  " observation";
256  log_string(NORMAL, msg);
257  continue;
258  }
259 
260  // Increment counter
261  n_observations++;
262 
263  // Save event file name (for possible saving)
264  m_infiles[i] = obs->eventfile();
265 
266  // Extract event and GTI extension names from input FITS file
267  GFilename fname(m_infiles[i]);
268  if (fname.has_extname()) {
269  m_evtname[i] = fname.extname();
270  }
271  m_gtiname[i] = get_gtiname(fname.url(), m_evtname[i]);
272 
273  // Write input file information into logger
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]);
277 
278  // Fall through in case that the event file is empty
279  if (obs->events()->size() == 0) {
280  log_string(NORMAL, " Warning: No events in event file \""+
281  m_infiles[i]+"\". Event selection skipped.");
282  continue;
283  }
284 
285  // If we have an input file then check it
286  if (!m_infiles[i].empty()) {
287  std::string message = check_infile(m_infiles[i], m_evtname[i]);
288  if (!message.empty()) {
289  throw GException::invalid_value(G_PROCESS, message);
290  }
291  }
292 
293  // Get temporary file name
294  std::string filename = gammalib::tmpnam();
295 
296  // Save observation in temporary file. We add here the events and
297  // GTI extension name so that the GCTAObservation::save method can
298  // use this information for writing the proper extension names into
299  // the temporary file
300  obs->save(filename+"["+m_evtname[i]+";"+ m_gtiname[i]+"]", true);
301 
302  // Log saved FITS file.
303  if (logExplicit()) {
304  GFits tmpfile(filename);
305  log.header3("FITS file content of temporary file");
306  log << tmpfile << std::endl;
307  tmpfile.close();
308  }
309 
310  // If we have a temporary file then check it
311  if (!filename.empty()) {
312  std::string message = check_infile(filename, m_evtname[i]);
313  if (!message.empty()) {
314  throw GException::invalid_value(G_PROCESS, message);
315  }
316  }
317 
318  // Load observation from temporary file, including event selection
319  select_events(obs, filename, m_evtname[i], m_gtiname[i]);
320 
321  // Remove temporary file
322  std::remove(filename.c_str());
323 
324  } // endfor: looped over all observations
325 
326  // If more than a single observation has been handled then make sure that
327  // an XML file will be used for storage
328  if (n_observations > 1) {
329  m_use_xml = true;
330  }
331 
332  // Write observation(s) into logger
333  log_observations(NORMAL, m_obs, "Output observation");
334 
335  // Optionally publish event list(s)
336  if ((*this)["publish"].boolean()) {
337  publish();
338  }
339 
340  // Return
341  return;
342 }
343 
344 
345 /***********************************************************************//**
346  * @brief Save the selected event list(s)
347  *
348  * This method saves the selected event list(s) into FITS file(s). There are
349  * two modes, depending on the m_use_xml flag.
350  *
351  * If m_use_xml is true, all selected event list(s) will be saved into FITS
352  * files, where the output filenames are constructued from the input
353  * filenames by prepending the @a prefix string to name. Any path information
354  * will be stripped form the input name, hence event files will be written
355  * into the local working directory (unless some path information is present
356  * in the prefix). In addition, an XML file will be created that gathers
357  * the filename information for the selected event list(s). If an XML file
358  * was present on input, all metadata information will be copied from this
359  * input file.
360  *
361  * If m_use_xml is false, the selected event list will be saved into a FITS
362  * file.
363  ***************************************************************************/
364 void ctselect::save(void)
365 {
366  // Write header into logger
367  log_header1(TERSE, gammalib::number("Save event list", m_obs.size()));
368 
369  // Case A: Save event file(s) and XML metadata information
370  if (m_use_xml) {
371  save_xml();
372  }
373 
374  // Case B: Save event file as FITS file
375  else {
376  save_fits();
377  }
378 
379  // Return
380  return;
381 }
382 
383 
384 /***********************************************************************//**
385  * @brief Publish event lists
386  *
387  * @param[in] name Event list name.
388  ***************************************************************************/
389 void ctselect::publish(const std::string& name)
390 {
391  // Write header into logger
392  log_header1(TERSE, gammalib::number("Publish event list", m_obs.size()));
393 
394  // Loop over all observation in the container
395  for (int i = 0; i < m_obs.size(); ++i) {
396 
397  // Get CTA observation
398  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
399 
400  // Handle only CTA observations
401  if (obs != NULL) {
402 
403  // Continue only if there is an event list
404  if (obs->events()->size() != 0) {
405 
406  // Set default name if user name is empty
407  std::string user_name(name);
408  if (user_name.empty()) {
409  user_name = CTSELECT_NAME;
410  }
411 
412  // If there are several event lists then add an index
413  if (m_use_xml) {
414  user_name += gammalib::str(i);
415  }
416 
417  // Write event list name into logger
418  log_value(NORMAL, "Event list name", user_name);
419 
420  // Write events into in-memory FITS file
421  GFits fits;
422  obs->write(fits);
423 
424  // Publish
425  fits.publish(gammalib::extname_cta_events, user_name);
426 
427  } // endif: there were events
428 
429  } // endif: observation was a CTA observation
430 
431  } // endfor: looped over observations
432 
433  // Return
434  return;
435 }
436 
437 
438 /*==========================================================================
439  = =
440  = Private methods =
441  = =
442  ==========================================================================*/
443 
444 /***********************************************************************//**
445  * @brief Initialise class members
446  ***************************************************************************/
448 {
449  // Initialise parameters
450  m_outobs.clear();
451  m_emin = 0.0;
452  m_emax = 0.0;
453  m_expr.clear();
454  m_usethres.clear();
455  m_chatter = static_cast<GChatter>(2);
456 
457  // Initialise protected members
458  m_infiles.clear();
459  m_evtname.clear();
460  m_gtiname.clear();
461  m_phases.clear();
462  m_select_energy = false;
463  m_select_phase = false;
464  m_forcesel = false;
465 
466  // Return
467  return;
468 }
469 
470 
471 /***********************************************************************//**
472  * @brief Copy class members
473  *
474  * @param[in] app Application.
475  ***************************************************************************/
477 {
478  // Copy parameters
479  m_outobs = app.m_outobs;
480  m_emin = app.m_emin;
481  m_emax = app.m_emax;
482  m_expr = app.m_expr;
483  m_usethres = app.m_usethres;
484  m_chatter = app.m_chatter;
485 
486  // Copy protected members
487  m_infiles = app.m_infiles;
488  m_evtname = app.m_evtname;
489  m_gtiname = app.m_gtiname;
490  m_phases = app.m_phases;
493  m_forcesel = app.m_forcesel;
494 
495  // Return
496  return;
497 }
498 
499 
500 /***********************************************************************//**
501  * @brief Delete class members
502  ***************************************************************************/
504 {
505  // Return
506  return;
507 }
508 
509 
510 /***********************************************************************//**
511  * @brief Get application parameters
512  *
513  * Get all user parameters from parameter file or (if required) by querying
514  * the user. Times are assumed to be in the native CTA MJD format.
515  *
516  * This method also loads observations if no observations are yet allocated.
517  * Observations are either loaded from a single CTA even list, or from a
518  * XML file using the metadata information that is stored in that file.
519  ***************************************************************************/
521 {
522  // Clear some members
523  m_phases.clear();
524 
525  // Initialise selection flags
526  m_select_energy = false;
527  m_select_phase = false;
528 
529  // Setup observations from "inobs" parameter. Do not request response
530  // information and do not accept counts cubes.
531  setup_observations(m_obs, false, true, false);
532 
533  // Query the RoI
534  GCTAPointing pnt;
535  GCTARoi roi = get_roi(pnt);
536 
537  // Signal whether the RoI selection should be enforced even when leading
538  // to invalid RoI
539  m_forcesel = (*this)["forcesel"].boolean();
540 
541  // Query Good Time Intervals. The GammaLib time reference is specified
542  // as a dummy argument since the relevant intervals will be queried later
543  // using the time reference for each observation
544  get_gti(GTimeReference());
545 
546  // Get energy selection parameters
547  m_usethres = (*this)["usethres"].string();
548  if ((*this)["emin"].is_valid() && (*this)["emax"].is_valid()) {
549  m_emin = (*this)["emin"].real();
550  m_emax = (*this)["emax"].real();
551  m_select_energy = true;
552  }
553  else if ((m_usethres == "DEFAULT") || (m_usethres == "USER")) {
554  m_select_energy = true;
555  }
556 
557  // Get phase selection string
558  std::string phase_expr = gammalib::strip_whitespace(gammalib::tolower(
559  (*this)["phase"].string()));
560 
561  // Get phase selection parameters
562  if ((phase_expr.length() > 0) && (phase_expr != "none")) {
563 
564  // Get phase selection string
565  std::string phase_expr = (*this)["phase"].string();
566 
567  // Extract phase bins from selection string
568  std::vector<std::string> phase_splits = gammalib::split(phase_expr, ",");
569  for (int i = 0; i < phase_splits.size(); ++i) {
570 
571  // Get minimum and maximum of phase bin
572  std::vector<std::string> phase_toks =
573  gammalib::split(phase_splits[i], ":");
574 
575  // Throw an exception if there are not two elements
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. "
581  "\"0.3:0.5\".";
582  throw GException::invalid_value(G_GET_PARAMETERS, msg);
583  }
584 
585  // Throw an exception if one of the elements is empty
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. "
591  "\"0.3:0.5\".";
592  throw GException::invalid_value(G_GET_PARAMETERS, msg);
593  }
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. "
599  "\"0.3:0.5\".";
600  throw GException::invalid_value(G_GET_PARAMETERS, msg);
601  }
602 
603  // Get phase bin boundaries
604  double phase_min = gammalib::todouble(phase_toks[0]);
605  double phase_max = gammalib::todouble(phase_toks[1]);
606 
607  // Throw an exception if phase minimum or maximum is outside the
608  // range [0,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 "
613  "within 0 and 1.";
614  throw GException::invalid_value(G_GET_PARAMETERS, msg);
615  }
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 "
620  "within 0 and 1.";
621  throw GException::invalid_value(G_GET_PARAMETERS, msg);
622  }
623 
624  // Append phase interval
625  m_phases.append(phase_min, phase_max);
626 
627  } // endfor: looped over phase selection string
628 
629  // Signal that a phase selection information is available
630  m_select_phase = (m_phases.size() > 0);
631 
632  } // endif: phase selection parameters were valid
633 
634  // Get other User parameters
635  m_expr = (*this)["expr"].string();
636  m_chatter = static_cast<GChatter>((*this)["chatter"].integer());
637 
638  // If needed later, query output filename and prefix now
639  if (read_ahead()) {
640  (*this)["outobs"].query();
641  (*this)["prefix"].query();
642  }
643 
644  // Write parameters into logger
645  log_parameters(TERSE);
646 
647  // Return
648  return;
649 }
650 
651 
652 /***********************************************************************//**
653  * @brief Select events
654  *
655  * @param[in,out] obs CTA observation.
656  * @param[in] filename File name.
657  * @param[in] evtname Event extension name.
658  * @param[in] gtiname GTI extension name.
659  *
660  * @exception GException::invalid_value
661  * No events extension found in FITS file.
662  *
663  * Select events from a FITS file by making use of the selection possibility
664  * of the cfitsio library on loading a file. A selection string is created
665  * from the specified criteria that is appended to the filename so that
666  * cfitsio will automatically filter the event data. This selection string
667  * is then applied when opening the FITS file. The event list in the current
668  * observation is replaced by selected event list read from the FITS file.
669  *
670  * Good Time Intervals of the observation will be limited to the time
671  * interval specified by the User parameters tmin and tmax.
672  ***************************************************************************/
673 void ctselect::select_events(GCTAObservation* obs,
674  const std::string& filename,
675  const std::string& evtname,
676  const std::string& gtiname)
677 {
678  // Write header into logger
679  log_header3(NORMAL, "Event selection");
680 
681  // Initialise selection and addition strings
682  std::string selection;
683  std::string add;
684 
685  // Initialise selection flags
686  bool remove_all = false;
687 
688  // Get CTA event list pointer
689  GCTAEventList* list =
690  static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events()));
691 
692  // Get User GTI with MET in the time reference that is specified for the
693  // observation
694  GGti user_gti = get_gti(list->gti().reference());
695  GTime timemin = user_gti.tstart();
696  GTime timemax = user_gti.tstop();
697 
698  // Get existing Roi and energy bounds for possible later use
699  // (will be empty if unavailable)
700  GCTARoi old_roi = list->roi();
701  GEbounds old_ebounds = list->ebounds();
702 
703  // Determine new energy boundaries for selection taking into account
704  // previous existing ones
705  double emin = 0.0;
706  double emax = 0.0;
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();
711  }
712 
713  // Analyse expression to see if a selection is required
714  bool select_expr = (gammalib::strip_whitespace(m_expr).length() > 0);
715 
716  // Set RoI selection
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();
721 
722  // Signal that RoI selection should be performed
723  bool select_roi = roi.is_valid();
724 
725  // Create boolean to remember if RoI selection is enforced regardless of
726  // its validity
727  bool enforced_roi = false;
728 
729  // Set time selection interval. We make sure here that the time selection
730  // interval cannot be wider than the GTIs covering the data. This is done
731  // using GGti's reduce() method.
732  if (!user_gti.is_empty()) {
733 
734  // Reduce GTIs to specified time interval. The complicated cast is
735  // necessary here because the gti() method is declared const, so
736  // we're not officially allowed to modify the GTIs.
737  ((GGti*)(&list->gti()))->reduce(timemin, timemax);
738 
739  } // endif: time selection was required
740 
741  // Save GTI for later usage
742  GGti gti = list->gti();
743 
744  // Make time selection
745  if (!user_gti.is_empty()) {
746 
747  // If there are no GTIs then request removal of all events
748  if (list->gti().is_empty()) {
749  remove_all = true;
750  log_value(NORMAL, "Time range", "None. There is no overlap "
751  "between existing and requested time interval.");
752  }
753 
754  // ... otherwise set time interval
755  else {
756 
757  // Extract effective time interval in the reference time of the
758  // event list. We get this reference time from gti.reference().
759  double tmin = gti.tstart().convert(gti.reference());
760  double tmax = gti.tstop().convert(gti.reference());
761 
762  // Format time with sufficient accuracy and add to selection string
763  char cmin[80];
764  char cmax[80];
765  sprintf(cmin, "%.8f", tmin);
766  sprintf(cmax, "%.8f", tmax);
767  selection = "TIME >= "+std::string(cmin)+" && TIME <= "+std::string(cmax);
768  add = " && ";
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");
776 
777  } // endelse: set time interval
778 
779  } // endif: made time selection
780 
781  // Make phase selection
782  if (m_select_phase) {
783 
784  // Check if event_list has phase
785  if (list->has_phase()) {
786 
787  // Loop over phase selections
788  for (int i = 0; i < m_phases.size(); ++i) {
789 
790  // Check if phasemax is larger than phasemin
791  if (i == 0) {
792  selection += add + "( ";
793  }
794  else {
795  selection += add;
796  }
797 
798  // Format phase with sufficient accuracy and add to selection string
799  char cmin[80];
800  char cmax[80];
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)+")";
805  add = " || ";
806  log_value(NORMAL, "Phase range "+gammalib::str(i+1),
807  gammalib::str(m_phases.pmin(i))+" - "+
808  gammalib::str(m_phases.pmax(i)));
809 
810  } // end for loop
811 
812  // If phase intervals have been appended then close the selection
813  // string and take provision for appending another selection
814  if (m_phases.size() > 0) {
815  selection += " )";
816  add = " && ";
817  }
818 
819  } // endif: there was PHASE information in the event list
820 
821  // Otherwise signal that no PHASE information is available
822  else {
823  log_value(NORMAL, "Phase range", "Event list has no \"PHASE\" "
824  "column. Phase selection skipped.");
825  }
826 
827  } // endif: made phase selection
828 
829  // Make energy selection
830  if (m_select_energy) {
831 
832  // Log the requested energy selection
833  if (logNormal()) {
834  log << gammalib::parformat("Selected energy range");
835  if (emin > 0.0 && emax > 0.0) {
836  if (emin >= emax) {
837  log << "None. There is no overlap between existing ";
838  log << "and requested energy range." << std::endl;
839  }
840  else {
841  log << emin << " - " << emax << " TeV" << std::endl;
842  }
843  }
844  else if (emin > 0.0) {
845  log << "> " << emin << " TeV" << std::endl;
846  }
847  else if (emax > 0.0) {
848  log << "< " << emax << " TeV" << std::endl;
849  }
850  else {
851  log << "None" << std::endl;
852  }
853  }
854 
855  // Apply the requested energy selection
856  if (emin > 0.0 && emax > 0.0) {
857  if (emin >= emax) {
858  remove_all = true;
859  }
860  else {
861  char cmin[80];
862  char cmax[80];
863  sprintf(cmin, "%.8f", emin);
864  sprintf(cmax, "%.8f", emax);
865  selection += add + "ENERGY >= " + std::string(cmin)+
866  " && ENERGY <= " + std::string(cmax);
867  add = " && ";
868  }
869  }
870  else if (emin > 0.0) {
871  char cmin[80];
872  sprintf(cmin, "%.8f", emin);
873  selection += add + "ENERGY >= " + std::string(cmin);
874  add = " && ";
875  }
876  else if (emax > 0.0) {
877  char cmax[80];
878  sprintf(cmax, "%.8f", emax);
879  selection += add + "ENERGY <= " + std::string(cmax);
880  add = " && ";
881  }
882  else {
883  remove_all = true;
884  }
885 
886  } // endif: made energy selection
887 
888  // Make RoI selection
889  if (select_roi) {
890 
891  // Store the original radius
892  double original_rad = rad;
893 
894  // Log the requested RoI
895  log_value(NORMAL, "Requested RoI",
896  "Centre(RA,DEC)=("+gammalib::str(ra)+", "+
897  gammalib::str(dec)+") deg, Radius="+gammalib::str(rad)+
898  " deg");
899 
900  // If we have already an RoI then make sure that the selected
901  // ROI overlaps with the existing RoI
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");
909  GSkyDir centre;
910  centre.radec_deg(ra, dec);
911  double distance = centre.dist_deg(roi_centre);
912 
913  // If the requested RoI and selected RoI are centred at the same
914  // position and the requested RoI is bigger or same size as
915  // the original keep original radius
916  // RoIs same centre = distance < 1.e-4
917  // to cope with numerical precision limitations
918  if ((distance < 1.e-4) && (rad >= roi_radius)) {
919  rad = roi_radius;
920  }
921 
922  // ... otherwise, check if requested RoI is enclosed within
923  // original RoI
924  // enclosed = distance + rad - roi_radius > 1.e-4
925  // to cope with numerical precision limitations
926  else if ((distance + rad - roi_radius) > 1.e-4) {
927 
928  // If selection is enforced log warning and store
929  if (m_forcesel) {
930  enforced_roi = true;
931  std::string msg = \
932  "WARNING: Enforced RoI selection leading to inconsis" \
933  "tent RoI.\n" \
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);
944  }
945 
946  // ... otherwise, throw an exception
947  else {
948  std::string msg = \
949  "Invalid RoI selection: the new RoI must be enclosed "
950  "in the original RoI";
951  throw GException::invalid_value(G_PROCESS, msg);
952  }
953  }
954  }
955 
956  // If the RoI radius is negative then there is no overlap between
957  // the existing and the requested RoI and the radius will be restored
958  // to the original value. We signal that all events should be
959  // removed.
960  if (rad <= 0.0) {
961  rad = original_rad;
962  remove_all = true;
963  log_value(NORMAL, "Selected RoI", "None. There is no overlap "
964  "between existing and requested RoI.");
965  }
966  else {
967  log_value(NORMAL, "Selected RoI", "Centre(RA,DEC)=("+
968  gammalib::str(ra)+", "+gammalib::str(dec)+") deg, "
969  "Radius="+gammalib::str(rad)+" deg");
970  }
971 
972  // Format RoI selection
973  char cra[80];
974  char cdec[80];
975  char crad[80];
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) <= " +
981  std::string(crad);
982  add = " && ";
983 
984  } // endif: made ROI selection
985 
986  // Make an expression selection
987  if (select_expr) {
988 
989  // Append the expression
990  selection += add + "("+gammalib::strip_whitespace(m_expr)+")";
991 
992  } // endif: made expression selection
993 
994  // Dump cfitsio selection string
995  log_value(NORMAL, "cfitsio selection", selection);
996 
997  // Build input filename including selection expression
998  std::string expression = filename + "[" + evtname + "]";
999  if (selection.length() > 0) {
1000  expression += "["+selection+"]";
1001  }
1002 
1003  // Dump FITS filename including selection expression
1004  log_value(NORMAL, "FITS filename", expression);
1005 
1006  // Open FITS file
1007  GFits file(expression);
1008 
1009  // Log selected FITS file
1010  log_header3(EXPLICIT, "FITS file content after selection");
1011  log_string(EXPLICIT, file.print(m_chatter));
1012 
1013  // Check if we have an events HDU
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.";
1018  throw GException::invalid_value(G_SELECT_EVENTS, msg);
1019  }
1020 
1021  // Determine number of events in the events HDU. If removal of all events
1022  // has been requested then set the number of events to zero.
1023  int nevents = (remove_all) ? 0 : file.table(evtname)->nrows();
1024 
1025  // If the selected event list is empty then append an empty event list
1026  // to the observation
1027  if (nevents < 1) {
1028 
1029  // Create empty event list
1030  GCTAEventList eventlist;
1031 
1032  // Append list to observation
1033  obs->events(eventlist);
1034 
1035  }
1036 
1037  // ... otherwise load the data from the temporary file
1038  else {
1039  obs->read(file);
1040  }
1041 
1042  // Get CTA event list pointer
1043  list = static_cast<GCTAEventList*>(const_cast<GEvents*>(obs->events()));
1044 
1045  // Make sure that events are fetched since the temporary file will be
1046  // closed later
1047  list->fetch();
1048 
1049  // If RoI selection has been applied without forcing then set the event
1050  // list RoI
1051  if (select_roi && !enforced_roi) {
1052  GSkyDir skydir;
1053  skydir.radec_deg(ra, dec);
1054  GCTAInstDir instdir(skydir);
1055  list->roi(GCTARoi(instdir, rad));
1056  }
1057 
1058  // ... otherwise restore old RoI information if it existed
1059  else if (old_roi.is_valid()) {
1060  list->roi(old_roi);
1061  }
1062 
1063  // Set event list GTI (in any case as any event list has a GTI)
1064  list->gti(gti);
1065 
1066  // If an energy selection has been applied then set the energy boundaries
1067  if (m_select_energy) {
1068  GEbounds ebounds;
1069  ebounds.append(GEnergy(emin, "TeV"), GEnergy(emax, "TeV"));
1070  list->ebounds(ebounds);
1071  }
1072 
1073  // ... otherwise restore old Ebounds if they existed
1074  else if (old_ebounds.size() > 0) {
1075  list->ebounds(old_ebounds);
1076  }
1077 
1078  // If a phase selection has been applied set the phase boundaries
1079  if (m_select_phase && list->has_phase()) {
1080  list->phases(m_phases);
1081  }
1082 
1083  // Recompute ontime and livetime.
1084  obs->ontime(list->gti().ontime());
1085  obs->livetime(list->gti().ontime() * obs->deadc());
1086 
1087  // Return
1088  return;
1089 }
1090 
1091 
1092 /***********************************************************************//**
1093  * @brief Return energy boundaries for a given observation
1094  *
1095  * @param[in] obs Pointer to CTA observation.
1096  * @param[in] ebounds Current energy boundaries.
1097  * @return Energy boundaries.
1098  *
1099  * Returns the energy boundaries for a given observation. Depending on the
1100  * value of the usethres parameter, the following values will be
1101  * returned:
1102  *
1103  * NONE: [max(emin,emin_exist),min(emax,emax_exist)]
1104  * DEFAULT: [max(emin,emin_exist,emin_safe),min(emax,emax_exist,emax_safe)]
1105  * USER: [max(emin,emin_exist,emin_user),min(emax,emax_exist,emax_user)]
1106  *
1107  * where
1108  *
1109  * emin is the value of the emin parameter
1110  * emax is the value of the emax parameter
1111  * emin_exist is the value of any existing minimum boundary
1112  * emax_exist is the value of any existing maximum boundary
1113  * emin_safe is the lower safe threshold
1114  * emax_safe is the upper safe threshold
1115  * emin_user is the lower user threshold
1116  * emax_user is the upper user threshold
1117  *
1118  * Any threshold value of 0 will be ignored.
1119  ***************************************************************************/
1120 GEbounds ctselect::set_ebounds(GCTAObservation* obs, const GEbounds& ebounds) const
1121 {
1122  // Set emin and emax values from user parameters
1123  double emin = m_emin;
1124  double emax = m_emax;
1125 
1126  // Check if we have already energy boundaries
1127  if (ebounds.size() > 0) {
1128 
1129  // Raise minimum energy to lower boundary
1130  if ((emin == 0.0) ||
1131  ((ebounds.emin(0).TeV() > 0.0) && (emin < ebounds.emin(0).TeV()))) {
1132  emin = ebounds.emin(0).TeV();
1133  }
1134 
1135  // Lower maximum energy to upper boundary
1136  if ((emax == 0.0) ||
1137  ((ebounds.emax(0).TeV() > 0.0) && (emax > ebounds.emax(0).TeV()))) {
1138  emax = ebounds.emax(0).TeV();
1139  }
1140 
1141  } // endif: there were already energy boundaries
1142 
1143  // Check if default threshold (the one from the IRF, also known as safe
1144  // threshold) should be applied
1145  if (m_usethres == "DEFAULT") {
1146 
1147  // Get CTA IRF respsonse pointer and throw exception if we don't
1148  // find IRF
1149  const GCTAResponseIrf* rsp = dynamic_cast<const GCTAResponseIrf*>
1150  (obs->response());
1151  if (rsp == NULL) {
1152  std::string msg = "No IRF response attached to given observation.";
1153  throw GException::invalid_value(G_SET_EBOUNDS, msg);
1154  }
1155 
1156  // Retrieve energy range from response information
1157  double lo_safe_thres = rsp->lo_safe_thres();
1158  double hi_safe_thres = rsp->hi_safe_thres();
1159 
1160  // Check threshold information for validity
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.";
1167  throw GException::invalid_value(G_SET_EBOUNDS, msg);
1168  }
1169 
1170  // Raise minimum energy to lower threshold (if lower threshold exists)
1171  if ((emin == 0.0) ||
1172  ((lo_safe_thres > 0.0) && (emin < lo_safe_thres))) {
1173  emin = lo_safe_thres;
1174  }
1175 
1176  // Lower maximum energy to upper threshold (if upper threshold exists)
1177  if ((emax == 0.0) ||
1178  ((hi_safe_thres > 0.0) && (emax > hi_safe_thres))) {
1179  emax = hi_safe_thres;
1180  }
1181 
1182  } // endif: usethres was "DEFAULT"
1183 
1184  // ... otherwise check if a user specified threshold should be applied
1185  else if (m_usethres == "USER") {
1186 
1187  // Retrieve thresholds from observation
1188  double lo_user_thres = obs->lo_user_thres();
1189  double hi_user_thres = obs->hi_user_thres();
1190 
1191  // Check threshold information for validity
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.";
1197  throw GException::invalid_value(G_SET_EBOUNDS, msg);
1198  }
1199 
1200  // Raise minimum energy to lower threshold (if lower threshold exists)
1201  if ((emin == 0.0) ||
1202  ((lo_user_thres > 0.0) && (emin < lo_user_thres))) {
1203  emin = lo_user_thres;
1204  }
1205 
1206  // Lower maximum energy to upper threshold (if upper threshold exists)
1207  if ((emax == 0.0) ||
1208  ((hi_user_thres > 0.0) && (emax > hi_user_thres))) {
1209  emax = hi_user_thres;
1210  }
1211 
1212  } // endif: usethres was "USER"
1213 
1214  // Set selection energy boundaries
1215  GEbounds result;
1216  if (emax > emin) {
1217  result.append(GEnergy(emin, "TeV"), GEnergy(emax, "TeV"));
1218  }
1219 
1220  // Return result
1221  return result;
1222 
1223 }
1224 
1225 
1226 /***********************************************************************//**
1227  * @brief Check input filename
1228  *
1229  * @param[in] filename File name.
1230  * @param[in] evtname Event extension name.
1231  *
1232  * This method checks if the input FITS file is correct.
1233  ***************************************************************************/
1234 std::string ctselect::check_infile(const std::string& filename,
1235  const std::string& evtname) const
1236 {
1237  // Initialise message string
1238  std::string message = "";
1239 
1240  // Open FITS file
1241  GFits fits(filename);
1242 
1243  // Check for existence of events extensions
1244  if (!fits.contains(evtname)) {
1245  message = "No \""+evtname+"\" extension found in input file \""+
1246  filename + "\".";
1247  }
1248 
1249  // ... otherwise check column names
1250  else {
1251 
1252  // Get pointer to FITS table
1253  GFitsTable* table = fits.table(evtname);
1254 
1255  // Initialise list of missing columns
1256  std::vector<std::string> missing;
1257 
1258  // Check for existence of TIME column
1259  if (!table->contains("TIME")) {
1260  missing.push_back("TIME");
1261  }
1262 
1263  // Check for existence of ENERGY column
1264  if (!table->contains("ENERGY")) {
1265  missing.push_back("ENERGY");
1266  }
1267 
1268  // Check for existence of RA column
1269  if (!table->contains("RA")) {
1270  missing.push_back("RA");
1271  }
1272 
1273  // Check for existence of DEC column
1274  if (!table->contains("DEC")) {
1275  missing.push_back("DEC");
1276  }
1277 
1278  // Set error message for missing columns
1279  if (!missing.empty()) {
1280  message = "The following columns are missing in the "
1281  "\""+evtname+"\" extension of input file \""+
1282  filename + "\": ";
1283  for (int i = 0; i < missing.size(); ++i) {
1284  message += "\"" + missing[i] + "\"";
1285  if (i < missing.size()-1) {
1286  message += ", ";
1287  }
1288  }
1289  }
1290 
1291  } // endelse: checked column names
1292 
1293  // Return
1294  return message;
1295 }
1296 
1297 
1298 /***********************************************************************//**
1299  * @brief Save event list in FITS format.
1300  *
1301  * Save the event list as a FITS file. The file name of the FITS file is
1302  * specified by the "outobs" parameter.
1303  ***************************************************************************/
1305 {
1306  // Save only if there are observations
1307  if (m_obs.size() > 0) {
1308 
1309  // Get output filename
1310  m_outobs = (*this)["outobs"].filename();
1311 
1312  // Get CTA observation from observation container
1313  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[0]);
1314 
1315  // Save only if it's a CTA observation
1316  if (obs != NULL) {
1317 
1318  // Save only if file name is non-empty
1319  if (m_infiles[0].length() > 0) {
1320 
1321  // Create file name object
1322  GFilename fname(m_outobs);
1323 
1324  // Extract filename and event extension name
1325  std::string outfile = fname.url();
1326 
1327  // Append event extension name. We handle here the possibility
1328  // to write the events into a different extension.
1329  if (fname.has_extname()) {
1330  outfile += "["+fname.extname()+"]";
1331  }
1332  else {
1333  outfile += "["+m_evtname[0]+"]";
1334  }
1335 
1336  // Log filename
1337  log_value(NORMAL, "Event list file", outfile);
1338 
1339  // Save event list
1340  save_event_list(obs, m_infiles[0], m_evtname[0], m_gtiname[0],
1341  outfile);
1342 
1343  } // endif: filename was non empty
1344 
1345  } // endif: observation was CTA observation
1346 
1347  } // endif: there were observations
1348 
1349  // Return
1350  return;
1351 }
1352 
1353 
1354 /***********************************************************************//**
1355  * @brief Save event list(s) in XML format.
1356  *
1357  * Save the event list(s) into FITS files and write the file path information
1358  * into a XML file. The filename of the XML file is specified by the outfile
1359  * parameter, the filename(s) of the event lists are built by prepending a
1360  * prefix to the input event list filenames. Any path present in the input
1361  * filename will be stripped, i.e. the event list(s) will be written in the
1362  * local working directory (unless a path is specified in the prefix).
1363  ***************************************************************************/
1365 {
1366  // Get output filename and prefix
1367  m_outobs = (*this)["outobs"].filename();
1368 
1369  // Issue warning if output filename has no .xml suffix
1370  log_string(TERSE, warn_xml_suffix(m_outobs));
1371 
1372  // Loop over all observation in the container
1373  for (int i = 0; i < m_obs.size(); ++i) {
1374 
1375  // Get CTA observation
1376  GCTAObservation* obs = dynamic_cast<GCTAObservation*>(m_obs[i]);
1377 
1378  // Skip observations that are no CTA observations
1379  if (obs == NULL) {
1380  continue;
1381  }
1382 
1383  // Skip observations that have empty names
1384  if (m_infiles[i].length() == 0) {
1385  continue;
1386  }
1387 
1388  // Set event output file name
1389  std::string outfile = set_outfile_name(m_infiles[i]);
1390 
1391  // Append event extension name
1392  outfile += "["+m_evtname[i]+"]";
1393 
1394  // Log filename
1395  log_value(NORMAL, "Event list file", outfile);
1396 
1397  // Store output file name in observation
1398  obs->eventfile(outfile);
1399 
1400  // Save event list
1401  save_event_list(obs, m_infiles[i], m_evtname[i], m_gtiname[i],
1402  outfile);
1403 
1404  } // endfor: looped over observations
1405 
1406  // Write observation definition XML file name into logger
1407  log_value(NORMAL, "Obs. definition file", m_outobs);
1408 
1409  // Save observations in XML file
1410  m_obs.save(m_outobs);
1411 
1412  // Return
1413  return;
1414 }
GCTARoi get_roi(const GCTAPointing &pnt=GCTAPointing())
Return RoI from User parameters.
Definition: ctool.cpp:1325
virtual ~ctselect(void)
Destructor.
Definition: ctselect.cpp:125
void free_members(void)
Delete class members.
Definition: ctselect.cpp:503
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition: ctool.cpp:431
void publish(const std::string &name="")
Publish event lists.
Definition: ctselect.cpp:389
double m_emin
Lower energy.
Definition: ctselect.hpp:84
void clear(void)
Clear ctselect tool.
Definition: ctselect.cpp:182
void save_fits(void)
Save event list in FITS format.
Definition: ctselect.cpp:1304
GGti get_gti(const GTimeReference &ref)
Return Good Time Intervals from User parameter.
Definition: ctool.cpp:1415
#define G_GET_PARAMETERS
Definition: ctselect.cpp:37
std::vector< std::string > m_gtiname
GTI extension names.
Definition: ctselect.hpp:94
#define CTSELECT_NAME
Definition: ctselect.hpp:38
std::string warn_xml_suffix(const GFilename &filename) const
Set warning string if file has no .xml suffix.
Definition: ctool.cpp:2427
void init_members(void)
Initialise class members.
Definition: ctool.cpp:321
const GObservations & obs(void) const
Return observation container.
void save_xml(void)
Save event list(s) in XML format.
Definition: ctselect.cpp:1364
double m_emax
Upper energy.
Definition: ctselect.hpp:85
#define G_PROCESS
Definition: ctselect.cpp:36
std::vector< std::string > m_evtname
Event extension names.
Definition: ctselect.hpp:93
Data selection tool definition.
GPhases m_phases
Phase intervals.
Definition: ctselect.hpp:95
ctselect(void)
Void constructor.
Definition: ctselect.cpp:56
bool m_forcesel
Enforce RoI selection.
Definition: ctselect.hpp:89
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition: ctool.cpp:1208
Base class for observation tools.
void free_members(void)
Delete class members.
void init_members(void)
Initialise class members.
Definition: ctselect.cpp:447
bool m_select_energy
Perform energy selection.
Definition: ctselect.hpp:96
std::string get_obs_header(const GObservation *obs) const
Return observation header string.
Definition: ctool.cpp:2090
void process(void)
Select event data.
Definition: ctselect.cpp:215
std::string m_expr
Selection expression.
Definition: ctselect.hpp:86
Data selection tool.
Definition: ctselect.hpp:46
void copy_members(const ctselect &app)
Copy class members.
Definition: ctselect.cpp:476
void free_members(void)
Delete class members.
Definition: ctool.cpp:357
std::string m_usethres
Energy threshold type.
Definition: ctselect.hpp:87
GEbounds set_ebounds(GCTAObservation *obs, const GEbounds &ebounds) const
Return energy boundaries for a given observation.
Definition: ctselect.cpp:1120
void save_event_list(const GCTAObservation *obs, const std::string &infile, const std::string &evtname, const std::string &gtiname, const std::string &outfile) const
Save event list into FITS file.
Definition: ctool.cpp:2266
bool m_select_phase
Perform phase selection.
Definition: ctselect.hpp:97
void init_members(void)
Initialise class members.
void save(void)
Save the selected event list(s)
Definition: ctselect.cpp:364
#define G_SET_EBOUNDS
Definition: ctselect.cpp:40
std::string get_gtiname(const std::string &filename, const std::string &evtname) const
Get Good Time Intervals extension name.
Definition: ctool.cpp:2352
ctobservation & operator=(const ctobservation &app)
Assignment operator.
#define G_SELECT_EVENTS
Definition: ctselect.cpp:38
const bool & read_ahead(void) const
Signal whether parameters should be read ahead.
Definition: ctool.hpp:177
GChatter m_chatter
Chattiness.
Definition: ctselect.hpp:88
bool m_use_xml
Use XML file instead of FITS file for observations.
Definition: ctool.hpp:162
std::string set_outfile_name(const std::string &filename)
Set output file name.
Definition: ctool.cpp:1491
ctselect & operator=(const ctselect &app)
Assignment operator.
Definition: ctselect.cpp:147
void log_observations(const GChatter &chatter, const GObservations &obs, const std::string &what="Observation")
Log observation container.
Definition: ctool.cpp:1251
GObservations m_obs
Observation container.
void get_parameters(void)
Get application parameters.
Definition: ctselect.cpp:520
std::string m_outobs
Output event list or XML file.
Definition: ctselect.hpp:83
std::string check_infile(const std::string &filename, const std::string &evtname) const
Check input filename.
Definition: ctselect.cpp:1234
void select_events(GCTAObservation *obs, const std::string &filename, const std::string &evtname, const std::string &gtiname)
Select events.
Definition: ctselect.cpp:673
std::vector< std::string > m_infiles
Input event filenames.
Definition: ctselect.hpp:92