ctools 2.1.0.dev
Loading...
Searching...
No Matches
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
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 ***************************************************************************/
74ctselect::ctselect(const GObservations& obs) :
75 ctobservation(CTSELECT_NAME, VERSION, obs)
76{
77 // Initialise 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 ***************************************************************************/
93ctselect::ctselect(int argc, char *argv[]) :
94 ctobservation(CTSELECT_NAME, VERSION, argc, argv)
95{
96 // Initialise 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 ***************************************************************************/
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
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 ***************************************************************************/
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 ***************************************************************************/
389void 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;
491 m_select_energy = app.m_select_energy;
492 m_select_phase = app.m_select_phase;
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 ***************************************************************************/
673void 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 ***************************************************************************/
1120GEbounds 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 ***************************************************************************/
1234std::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
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
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}
Base class for observation tools.
void free_members(void)
Delete class members.
ctobservation & operator=(const ctobservation &app)
Assignment operator.
GObservations m_obs
Observation container.
const GObservations & obs(void) const
Return observation container.
void init_members(void)
Initialise class members.
bool m_use_xml
Use XML file instead of FITS file for observations.
Definition ctool.hpp:162
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
void free_members(void)
Delete class members.
Definition ctool.cpp:357
std::string get_gtiname(const std::string &filename, const std::string &evtname) const
Get Good Time Intervals extension name.
Definition ctool.cpp:2352
void log_observations(const GChatter &chatter, const GObservations &obs, const std::string &what="Observation")
Log observation container.
Definition ctool.cpp:1251
GGti get_gti(const GTimeReference &ref)
Return Good Time Intervals from User parameter.
Definition ctool.cpp:1415
const bool & read_ahead(void) const
Signal whether parameters should be read ahead.
Definition ctool.hpp:177
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition ctool.cpp:1208
void init_members(void)
Initialise class members.
Definition ctool.cpp:321
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition ctool.cpp:431
std::string warn_xml_suffix(const GFilename &filename) const
Set warning string if file has no .xml suffix.
Definition ctool.cpp:2427
std::string get_obs_header(const GObservation *obs) const
Return observation header string.
Definition ctool.cpp:2090
std::string set_outfile_name(const std::string &filename)
Set output file name.
Definition ctool.cpp:1491
GCTARoi get_roi(const GCTAPointing &pnt=GCTAPointing())
Return RoI from User parameters.
Definition ctool.cpp:1325
Data selection tool.
Definition ctselect.hpp:46
GPhases m_phases
Phase intervals.
Definition ctselect.hpp:95
std::string m_usethres
Energy threshold type.
Definition ctselect.hpp:87
ctselect & operator=(const ctselect &app)
Assignment operator.
Definition ctselect.cpp:147
void publish(const std::string &name="")
Publish event lists.
Definition ctselect.cpp:389
virtual ~ctselect(void)
Destructor.
Definition ctselect.cpp:125
bool m_select_energy
Perform energy selection.
Definition ctselect.hpp:96
void clear(void)
Clear ctselect tool.
Definition ctselect.cpp:182
void process(void)
Select event data.
Definition ctselect.cpp:215
void free_members(void)
Delete class members.
Definition ctselect.cpp:503
std::string check_infile(const std::string &filename, const std::string &evtname) const
Check input filename.
double m_emax
Upper energy.
Definition ctselect.hpp:85
bool m_select_phase
Perform phase selection.
Definition ctselect.hpp:97
std::vector< std::string > m_gtiname
GTI extension names.
Definition ctselect.hpp:94
std::vector< std::string > m_evtname
Event extension names.
Definition ctselect.hpp:93
void save(void)
Save the selected event list(s)
Definition ctselect.cpp:364
void select_events(GCTAObservation *obs, const std::string &filename, const std::string &evtname, const std::string &gtiname)
Select events.
Definition ctselect.cpp:673
void copy_members(const ctselect &app)
Copy class members.
Definition ctselect.cpp:476
double m_emin
Lower energy.
Definition ctselect.hpp:84
GChatter m_chatter
Chattiness.
Definition ctselect.hpp:88
void init_members(void)
Initialise class members.
Definition ctselect.cpp:447
void save_xml(void)
Save event list(s) in XML format.
ctselect(void)
Void constructor.
Definition ctselect.cpp:56
std::string m_outobs
Output event list or XML file.
Definition ctselect.hpp:83
GEbounds set_ebounds(GCTAObservation *obs, const GEbounds &ebounds) const
Return energy boundaries for a given observation.
void get_parameters(void)
Get application parameters.
Definition ctselect.cpp:520
std::vector< std::string > m_infiles
Input event filenames.
Definition ctselect.hpp:92
bool m_forcesel
Enforce RoI selection.
Definition ctselect.hpp:89
void save_fits(void)
Save event list in FITS format.
std::string m_expr
Selection expression.
Definition ctselect.hpp:86
#define G_GET_PARAMETERS
Definition ctbin.cpp:42
#define G_PROCESS
Definition ctbkgcube.cpp:36
#define G_SET_EBOUNDS
Definition ctselect.cpp:40
#define G_SELECT_EVENTS
Definition ctselect.cpp:38
Data selection tool definition.
#define CTSELECT_NAME
Definition ctselect.hpp:38