ctools 2.1.0
Loading...
Searching...
No Matches
ctool.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * ctool - ctool base class *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2014-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 ctool.hpp
23 * @brief ctool base class implementation
24 * @author Juergen Knoedlseder
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <cstdlib> // std::getenv() function
32#include <cstdio> // std::fopen(), etc. functions
33#include <clocale> // std::setlocale function
34#include "ctool.hpp"
35#include "GTools.hpp"
36
37/* __ Includes for memory usage determination ____________________________ */
38#if defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
39#include <unistd.h>
40#include <sys/resource.h>
41#if defined(__APPLE__) && defined(__MACH__)
42#include <mach/mach.h>
43#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
44#include <fcntl.h>
45#include <procfs.h>
46#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
47#include <stdio.h>
48#endif
49#endif
50
51
52/* __ Method name definitions ____________________________________________ */
53#define G_SETUP_OBSERVATION "ctool::setup_observations(GObservations&)"
54#define G_SETUP_MODELS "ctool::setup_models(GObservations&, std::string&)"
55#define G_GET_MEAN_POINTING "ctool::get_mean_pointing(GObservations&)"
56#define G_CREATE_EBOUNDS "ctool::create_ebounds()"
57#define G_CREATE_ENERGIES "ctool::create_energies()"
58#define G_RESTORE_EDISP "ctool::restore_edisp(GObservations&, "\
59 "std::vector<bool>&)"
60#define G_SET_OBS_RESPONSE "ctool::set_obs_response(GCTAObservation*)"
61#define G_PROVIDE_HELP "ctool::provide_help()"
62
63/* __ Debug definitions __________________________________________________ */
64
65/* __ Coding definitions _________________________________________________ */
66
67
68/*==========================================================================
69 = =
70 = Constructors/destructors =
71 = =
72 ==========================================================================*/
73
74/***********************************************************************//**
75 * @brief Name constructor
76 *
77 * @param[in] name Application name.
78 * @param[in] version Application version.
79 *
80 * Constructs a ctool from the @p name and @p version. The constructor uses
81 * the equivalent GApplication constructor to set the parameter filename to
82 * "<name>.par" and the log filename to "<name>".log. The parameters will be
83 * loaded from the parameter file.
84 *
85 * No log file will be opened. To open the log file an explicit call to the
86 * logFileOpen() method is required.
87 *
88 * This constructor should be used for using a ctool from Python.
89 ***************************************************************************/
90ctool::ctool(const std::string& name,
91 const std::string& version) : GApplication(name, version)
92{
93 // Initialise members
95
96 // Synchronise parameter files
98
99 // Return
100 return;
101}
102
103
104/***********************************************************************//**
105 * @brief Application parameter constructor
106 *
107 * @param[in] name Application name.
108 * @param[in] version Application version.
109 * @param[in] pars Application parameters.
110 *
111 * Constructs a ctool from the @p name, @p version and application parameters
112 * @p pars. The constructor uses the equivalent GApplication constructor
113 * to set the parameter filename to "<name>.par" and the log filename to
114 * "<name>".log. The constructor opens the log file.
115 ***************************************************************************/
116ctool::ctool(const std::string& name,
117 const std::string& version,
118 const GApplicationPars& pars) : GApplication(name, version, pars)
119{
120 // Initialise members
121 init_members();
122
123 // Open the log file
124 logFileOpen();
125
126 // Return
127 return;
128}
129
130
131/***********************************************************************//**
132 * @brief Command line constructor
133 *
134 * @param[in] name Application name.
135 * @param[in] version Application version.
136 * @param[in] argc Number of arguments in command line.
137 * @param[in] argv Array of command line arguments.
138 *
139 * Constructs a ctool from the @p name, @p version and command line
140 * arguments. The constructor uses the equivalent GApplication constructor
141 * to set the parameter filename to "<name>.par" and the log filename to
142 * "<name>".log. The parameters will be loaded from the parameter file.
143 * In addition, the constructor opens the log file.
144 *
145 * If the "--help" option is provided as command line argument a help text
146 * about the usage of the ctool will be shown in the console and the ctool
147 * will exit. No log file will be opened in that case.
148 ***************************************************************************/
149ctool::ctool(const std::string& name,
150 const std::string& version,
151 int argc,
152 char* argv[]) : GApplication(name, version, argc, argv)
153{
154 // Catch --help option before doing anything else
155 if (need_help()) {
156 provide_help();
157 exit(0);
158 }
159
160 // Initialise members
161 init_members();
162
163 // Synchronise parameter files
164 sync_pfiles();
165
166 // Open the log file
167 logFileOpen();
168
169 // Return
170 return;
171}
172
173
174/***********************************************************************//**
175 * @brief Copy constructor
176 *
177 * @param[in] app Application.
178 *
179 * Construct a ctools from another ctool instance.
180 ***************************************************************************/
182{
183 // Initialise members
184 init_members();
185
186 // Copy members
187 copy_members(app);
188
189 // Return
190 return;
191}
192
193
194/***********************************************************************//**
195 * @brief Destructor
196 ***************************************************************************/
198{
199 // Free members
200 free_members();
201
202 // Return
203 return;
204}
205
206
207/*==========================================================================
208 = =
209 = Operators =
210 = =
211 ==========================================================================*/
212
213/***********************************************************************//**
214 * @brief Assignment operator
215 *
216 * @param[in] app Application.
217 * @return Application.
218 *
219 * Assigns one ctool application to another.
220 ***************************************************************************/
222{
223 // Execute only if object is not identical
224 if (this != &app) {
225
226 // Copy base class members
227 this->GApplication::operator=(app);
228
229 // Free members
230 free_members();
231
232 // Initialise members
233 init_members();
234
235 // Copy members
236 copy_members(app);
237
238 } // endif: object was not identical
239
240 // Return this object
241 return *this;
242}
243
244
245/*==========================================================================
246 = =
247 = Public methods =
248 = =
249 ==========================================================================*/
250
251/***********************************************************************//**
252 * @brief Run ctool
253 *
254 * This method runs a ctool by calling the process() method of a ctool. The
255 * method switches on screen dump in case that the debug parameter is true.
256 ***************************************************************************/
257void ctool::run(void)
258{
259 // Increment number of running tools
260 running()++;
261
262 // If we're in debug mode then all output is also dumped on the screen
263 if (logDebug()) {
264 log.cout(true);
265 }
266
267 // Run the tool
268 process();
269
270 // Decrement number of running tools
271 running()--;
272
273 // Return
274 return;
275}
276
277
278/***********************************************************************//**
279 * @brief Execute ctool
280 *
281 * This method executes a ctool by calling the process() and save() methods
282 * of a ctool. The method signals that some parameters should be read ahead
283 * and switches on screen dump in case that the debug parameter is true.
284 ***************************************************************************/
286{
287 // Increment number of running tools
288 running()++;
289
290 // Signal that some parameters should be read ahead
291 m_read_ahead = true;
292
293 // If we're in debug mode then all output is also dumped on the screen
294 if (logDebug()) {
295 log.cout(true);
296 }
297
298 // Run the tool
299 process();
300
301 // Save the results
302 save();
303
304 // Decrement number of running tools
305 running()--;
306
307 // Return
308 return;
309}
310
311
312/*==========================================================================
313 = =
314 = Private methods =
315 = =
316 ==========================================================================*/
317
318/***********************************************************************//**
319 * @brief Initialise class members
320 ***************************************************************************/
322{
323 // Initialise members
324 m_read_ahead = false;
325 m_use_xml = false;
326
327 // Set logger properties
328 log.date(true);
329
330 // Set language to english to make sure that a dot is a dot
331 std::setlocale(LC_ALL, "en_US.UTF-8");
332
333 // Return
334 return;
335}
336
337
338/***********************************************************************//**
339 * @brief Copy class members
340 *
341 * @param[in] app Application.
342 ***************************************************************************/
344{
345 // Copy members
346 m_read_ahead = app.m_read_ahead;
347 m_use_xml = app.m_use_xml;
348
349 // Return
350 return;
351}
352
353
354/***********************************************************************//**
355 * @brief Delete class members
356 ***************************************************************************/
358{
359 // Return
360 return;
361}
362
363
364/***********************************************************************//**
365 * @brief Synchronise parameter files
366 *
367 * Make sure that the structure of the parameter files in the syspfiles
368 * folder is used as a reference.
369 ***************************************************************************/
371{
372 // If CTOOLS environment variable is set and if a parameter file exists
373 // in the syspfiles folder then set syspfiles folder and reload
374 // parameter file
375 char* ptr = std::getenv("CTOOLS");
376 if (ptr != NULL) {
377 std::string path = std::string(ptr) + "/syspfiles";
378 std::string filename = path + "/" + par_filename();
379 if (access(filename.c_str(), R_OK) == 0) {
380 m_pars.clear();
381 m_pars.syspfiles(path);
382 m_pars.load(par_filename(), m_args);
383 }
384 }
385
386 // Return
387 return;
388}
389
390
391/***********************************************************************//**
392 * @brief Setup observation container
393 *
394 * @param[in] obs Observation container
395 * @param[in] response Require response
396 * @param[in] list Accept event list as "inobs" parameter
397 * @param[in] cube Accept counts cube as "inobs" parameter
398 *
399 * @exception GException::invalid_value
400 * Invalid "inobs" parameter encountered.
401 *
402 * Setup an observation container by extracting all required missing
403 * information from user parameters.
404 *
405 * If the observation container @p obs is empty, the method will extract the
406 * filename from the "inobs" parameter and load the observation container
407 * from the specified file. If the filename is either empty or "NONE" the
408 * method will throw an exception.
409 *
410 * If the "inobs" file is a FITS file, and depending on the information that
411 * is found in the FITS file, the method will append a single CTA observation
412 * to the observation container, containing either an event list or a counts
413 * cube. It will set the m_use_xml member to false to signal that no
414 * observation XML file should be used for saving. The @p list and @p cube
415 * parameters can be used to specify whether the method actually should accept
416 * event lists or counts cube. If it shouldn't the method will throw an
417 * exception.
418 *
419 * If the "inobs" file is not a FITS file, the method assumes that an
420 * observation definition XML file has been specified, and will load that
421 * file into the observation container. It will set the m_use_xml member to
422 * true to signal that an observation XML file should be used for saving. No
423 * checking on event list or counts cubes will be done on the observation
424 * container.
425 *
426 * By default, the method will also setup the response for all CTA
427 * observations in the container. In case that no response information is
428 * required, the @p response argument should be set to @p false. See the
429 * set_response() method for details.
430 ***************************************************************************/
431void ctool::setup_observations(GObservations& obs,
432 const bool& response,
433 const bool& list,
434 const bool& cube)
435{
436 // Load observations if there are none in the container
437 if (obs.size() == 0) {
438
439 // Throw an exception if the "inobs" parameter is not a valid
440 // filename
441 if ((*this)["inobs"].is_undefined()) {
442 std::string msg = "The \"inobs\" parameter \""+
443 (*this)["inobs"].current_value()+
444 "\" is not a valid filename. Please specify "
445 "a valid event list, counts cube or "
446 "observation definition XML file.";
447 throw GException::invalid_value(G_SETUP_OBSERVATION, msg);
448 }
449
450 // Get the filename from the "inobs" parameter
451 GFilename filename = (*this)["inobs"].filename();
452
453 // If file does not exist then throw an exceptions
454 if (!filename.exists()) {
455 std::string msg = "The \"inobs\" parameter \""+filename.url()+
456 "\" specifies a file that does not "
457 "exist. Please specify a valid event "
458 "list, counts cube or observation "
459 "definition XML file.";
460 throw GException::invalid_value(G_SETUP_OBSERVATION, msg);
461 }
462
463 // If the file is a FITS file then load it into a CTA
464 // observation and append that observation to the container ...
465 else if (filename.is_fits()) {
466
467 // Load CTA observation
468 GCTAObservation cta(filename);
469
470 // If no event list is accepted but an event list has been
471 // loaded then throw an exception
472 if (!list && cta.eventtype() == "EventList") {
473 std::string msg = "The \"inobs\" parameter \""+filename.url()+
474 "\" specifies an event list, but this "
475 "tool does not accept event lists.";
476 throw GException::invalid_value(G_SETUP_OBSERVATION, msg);
477 }
478
479 // If no counts cube is accepted but a conts cube has been
480 // loaded then throw an exception
481 if (!cube && cta.eventtype() == "CountsCube") {
482 std::string msg = "The \"inobs\" parameter \""+filename.url()+
483 "\" specifies a counts cube, but this "
484 "tool does not accept counts cubes.";
485 throw GException::invalid_value(G_SETUP_OBSERVATION, msg);
486 }
487
488 // Append CTA observation to observation container
489 obs.append(cta);
490
491 // Signal that no XML file should be used for storage
492 m_use_xml = false;
493
494 } // endelse: file was a FITS file
495
496 // ... otherwise it is assumed that the file is an observation
497 // definition XML file and the file is loaded into the
498 // observation container
499 else {
500
501 // Load observation definition file
502 obs.load(filename);
503
504 // Signal that XML file should be used for storage
505 m_use_xml = true;
506
507 } // endelse: file was an XML file
508
509 } // endif: there were no observations in the container
510
511 // Setup the response for all CTA observations in the observation
512 // container if response information is required
513 if (response) {
514 set_response(obs);
515 }
516
517 // Return
518 return;
519}
520
521
522/***********************************************************************//**
523 * @brief Setup model container
524 *
525 * @param[in] obs Observation container
526 * @param[in] name Mandatory model name
527 *
528 * @exception GException::invalid_value
529 * Model definiton XML filename not valid or mandatory model name
530 * not found in model container.
531 *
532 * Setup a model container by loading the models from the "inmodel"
533 * parameter.
534 *
535 * If the model container in the observation container @p obs is empty, the
536 * method will extract the model container filename from the "inmodel"
537 * parameter, load the model container and assign it to the observations
538 * container. If the filename is either empty or "NONE" the method will
539 * throw an exception.
540 *
541 * If a mandatory model name is specified, the method will further check
542 * whether a model with the name exists in the model container. If it does
543 * not exist, an exception is thrown.
544 ***************************************************************************/
545void ctool::setup_models(GObservations& obs,
546 const std::string& name)
547{
548 // If there are no models in the observation container then load model
549 // container from the "inmodel" parameter and attach it to the
550 // observation container
551 if (obs.models().size() == 0) {
552
553 // Throw an exception if the "inmodel" parameter is not a valid
554 // filename
555 if ((*this)["inmodel"].is_undefined()) {
556 std::string msg = "The \"inmodel\" parameter \""+
557 (*this)["inmodel"].current_value()+
558 "\" is not a valid filename. Please specify "
559 "a valid model definition XML file.";
560 throw GException::invalid_value(G_SETUP_MODELS, msg);
561 }
562
563 // Get models XML filename
564 GFilename filename = (*this)["inmodel"].filename();
565
566 // Load model container and assign it to observations container
567 obs.models(GModels(filename.url()));
568
569 } // endif: no models were in observations container
570
571 // If a mandatory model name exists then check whether this model is
572 // part of the observation container. Throw an exception if the model
573 // does not exist
574 if (!name.empty()) {
575
576 // Throw an exception if the model name does not exist
577 if (!obs.models().contains(name)) {
578 std::string msg = "Model \""+name+"\" not found in model "
579 "container. Please add a model with that name "
580 "or check for possible typos.";
581 throw GException::invalid_value(G_SETUP_MODELS, msg);
582 }
583
584 } // endif: mandatory model name existed
585
586 // Return
587 return;
588}
589
590
591/***********************************************************************//**
592 * @brief Create energy boundaries from user parameters
593 *
594 * @exception GException::invalid_value
595 * No valid energy boundary extension found or invalid extension
596 * name.
597 *
598 * Get the energy boundaries according to the user parameters. The method
599 * supports loading of energy boundary information from the `EBOUNDS` or
600 * `ENERGYBINS` extension, loading of energy value information from the
601 * `ENERGIES` extension, or setting energy boundaries using a linear or
602 * logarithmical spacing.
603 *
604 * The following parameters are read:
605 *
606 * ebinalg - Energy binning algorithm
607 * ebinfile - Name of file with energy binning
608 * emin - Minimum energy (if ebinalg != FILE)
609 * emax - Maximum energy (if ebinalg != FILE)
610 * enumbins - Number of energy bins (if ebinalg != FILE)
611 ***************************************************************************/
613{
614 // Allocate energy boundaries
615 GEbounds ebounds;
616
617 // Get energy binning algorithm
618 std::string ebinalg = (*this)["ebinalg"].string();
619
620 // If energy binning algorithm is of type "FILE" (case sensitive), then
621 // read energy boundaries from FITS file ...
622 if (ebinalg == "FILE") {
623
624 // Get filename
625 GFilename ebinfile = (*this)["ebinfile"].filename();
626
627 // Initialise exception message string
628 std::string msg;
629
630 // Make loading of energy boundaries OpenMP thread save. The critical
631 // region is put into an exception block to prevent throwing exceptions
632 // within the critical block
633 #pragma omp critical(ctool_create_ebounds)
634 {
635 try {
636
637 // If no extension name was provided then use default extension
638 // names
639 if (!ebinfile.has_extname()) {
640
641 // Open energy boundary file using the EBOUNDS or ENERGYBINS
642 // extension or energy values using the ENERGIES extension.
643 // If opening fails then set exception message. The
644 // exception is thrown later outside the critical OpenMP
645 // block.
646 GFits file(ebinfile.url());
647 if (file.contains(gammalib::extname_ebounds)) {
648 file.close();
649 ebounds.load(ebinfile);
650 }
651 else if (file.contains("ENERGYBINS")) {
652 file.close();
653 ebinfile = ebinfile.url() + "[ENERGYBINS]";
654 ebounds.load(ebinfile);
655 }
656 else if (file.contains(gammalib::extname_energies)) {
657 file.close();
658 ebounds.set(GEnergies(ebinfile));
659 }
660 else {
661 file.close();
662 msg = "No extension with name \""+
663 gammalib::extname_ebounds+"\", "
664 "\"ENERGYBINS\" or \""+
665 gammalib::extname_energies+"\" found in FITS "
666 "file \""+ebinfile+"\". Please specify a "
667 "valid energy binning file.";
668 }
669
670 } // endif: no extension name specified
671
672 // ... otherwise load energy boundaries from filename including
673 // extension
674 else {
675
676 // Open energy boundary file
677 GFits file(ebinfile.url());
678
679 // If FITS file does not contain requested extension then
680 // set exception message. The exception is thrown later
681 // outside the critical OpenMP zone
682 if (!file.contains(ebinfile.extname())) {
683 msg = "No extension \""+ebinfile.extname()+"\" "
684 "found in energy binning file \""+
685 ebinfile.url()+"\". Please provide a valid "
686 "extension name.";
687 }
688
689 // ... otherwise load energy boundaries
690 else {
691
692 // Get table extension
693 GFitsTable& table = *file.table(ebinfile.extname());
694
695 // If table contains one column then load energies,
696 // otherwise load energy boundaries.
697 if (table.ncols() == 1) {
698 file.close();
699 ebounds.set(GEnergies(ebinfile));
700 }
701 else {
702 file.close();
703 ebounds.load(ebinfile);
704 }
705
706 } // endelse: extension name was present in FITS file
707
708 } // endelse: loaded energy boundaries from table extension
709
710 } // endtry
711
712 // Catch any exceptions that occured in OpenMP critical block
713 // and recover the error message
714 catch (const std::exception& e) {
715 msg = e.what();
716 }
717
718 } // end critical OpenMP block
719
720 // If we have an exception message then throw an exception now. We
721 // have to do this since we should not throw exceptions inside OpenMP
722 // critical blocks
723 if (!msg.empty()) {
724 throw GException::invalid_value(G_CREATE_EBOUNDS, msg);
725 }
726
727 } // endif: ebinalg was "FILE"
728
729 // ... otherwise use native energy binning algorithms
730 else {
731
732 // Get task parameters
733 double emin = (*this)["emin"].real();
734 double emax = (*this)["emax"].real();
735 int enumbins = (*this)["enumbins"].integer();
736 double ebingamma = 1.0;
737
738 // Get task parameter for POW method
739 if (ebinalg == "POW") {
740 ebingamma = (*this)["ebingamma"].real();
741 }
742
743 // Setup energy boundaries
744 ebounds = GEbounds(enumbins, GEnergy(emin, "TeV"), GEnergy(emax, "TeV"),
745 ebinalg, ebingamma);
746
747 } // endelse: ebinalg was not "FILE"
748
749 // Return energy boundaries
750 return ebounds;
751}
752
753
754/***********************************************************************//**
755 * @brief Create energies from user parameters
756 *
757 * @exception GException::invalid_value
758 * No valid energies extension found or invalid extension
759 * name.
760 *
761 * Get the energy boundaries according to the user parameters. The method
762 * supports loading of energy node information from the `ENERGIES`
763 * extension, loading energy nodes from energy the boundary extensions
764 * `EBOUNDS` or `ENERGYBINS` extension, or setting energy nodes using a
765 * linear or logarithmical spacing.
766 *
767 * The following parameters are read:
768 *
769 * ebinalg - Energy binning algorithm
770 * ebinfile - Name of file with energy binning (if ebinalg == FILE)
771 * emin - Minimum energy (if ebinalg != FILE)
772 * emax - Maximum energy (if ebinalg != FILE)
773 * enumbins - Number of energy bins (if ebinalg != FILE)
774 * ebingamma - Power law index (if ebinalg == POW)
775 ***************************************************************************/
777{
778 // Allocate energies
779 GEnergies energies;
780
781 // Get energy binning algorithm
782 std::string ebinalg = (*this)["ebinalg"].string();
783
784 // If energy binning algorithm is of type "FILE" (case sensitive), then
785 // read energy boundaries from FITS file ...
786 if (ebinalg == "FILE") {
787
788 // Get filename
789 GFilename ebinfile = (*this)["ebinfile"].filename();
790
791 // Initialise exception message string
792 std::string msg;
793
794 // Make loading of energies OpenMP thread save. The critical region is
795 // put into an exception block to prevent throwing exceptions within
796 // the critical block
797 #pragma omp critical(ctool_create_energies)
798 {
799 try {
800
801 // If no extension name was provided then use default extension
802 // names
803 if (!ebinfile.has_extname()) {
804
805 // Open energy boundary file using the EBOUNDS or ENERGYBINS
806 // extension or energy values using the ENERGIES extension.
807 // If opening fails then set exception message. The
808 // exception is thrown later outside the critical OpenMP
809 // block.
810 GFits file(ebinfile.url());
811 if (file.contains(gammalib::extname_energies)) {
812 file.close();
813 energies.load(ebinfile);
814 }
815 else if (file.contains(gammalib::extname_ebounds)) {
816 file.close();
817 energies.set(GEbounds(ebinfile));
818 }
819 else if (file.contains("ENERGYBINS")) {
820 file.close();
821 ebinfile = ebinfile.url() + "[ENERGYBINS]";
822 energies.set(GEbounds(ebinfile));
823 }
824 else {
825 file.close();
826 msg = "No extension with name \""+
827 gammalib::extname_energies+"\", "
828 "\"ENERGYBINS\" or \""+
829 gammalib::extname_ebounds+"\" found in FITS "
830 "file \""+ebinfile+"\". Please specify a "
831 "valid energy binning file.";
832 }
833
834 }
835
836 // ... otherwise load energy boundaries from filename including
837 // extension
838 else {
839
840 // Open energy boundary file
841 GFits file(ebinfile.url());
842
843 // If FITS file does not contain requested extension then
844 // set exception message. The exception is thrown later
845 // outside the critical OpenMP zone
846 if (!file.contains(ebinfile.extname())) {
847 msg = "No extension \""+ebinfile.extname()+"\" "
848 "found in energy node file \""+
849 ebinfile.url()+"\". Please provide a valid "
850 "extension name.";
851 }
852
853 // ... otherwise load energies
854 else {
855
856 // Get table extension
857 GFitsTable& table = *file.table(ebinfile.extname());
858
859 // If table contains one column then load energies,
860 // otherwise load energy boundaries.
861 if (table.ncols() == 1) {
862 file.close();
863 energies.load(ebinfile);
864 }
865 else {
866 file.close();
867 energies.set(GEbounds(ebinfile));
868 }
869
870 } // endelse: extension name was present in FITS file
871
872 } // endelse: loaded energies from table extension
873
874 } // endtry
875
876 // Catch any exceptions that occured in OpenMP critical block
877 // and recover the error message
878 catch (const std::exception& e) {
879 msg = e.what();
880 }
881
882 } // end critical OpenMP block
883
884 // If we have an exception message then throw an exception now. We
885 // have to do this since we should not throw exceptions inside OpenMP
886 // critical blocks
887 if (!msg.empty()) {
888 throw GException::invalid_value(G_CREATE_ENERGIES, msg);
889 }
890
891 } // endif: ebinalg was "FILE"
892
893 // ... otherwise use native energy binning algorithms
894 else {
895
896 // Get task parameters
897 double emin = (*this)["emin"].real();
898 double emax = (*this)["emax"].real();
899 int enumbins = (*this)["enumbins"].integer();
900 double ebingamma = 1.0;
901
902 // Get task parameter for POW method
903 if (ebinalg == "POW") {
904 ebingamma = (*this)["ebingamma"].real();
905 }
906
907 // Setup energy nodes
908 energies = GEnergies(enumbins, GEnergy(emin, "TeV"), GEnergy(emax, "TeV"),
909 ebinalg, ebingamma);
910
911 } // endelse: ebinalg was not "FILE"
912
913 // Return energy nodes
914 return energies;
915}
916
917
918/***********************************************************************//**
919 * @brief Create a skymap from user parameters
920 *
921 * @param[in] obs Observation container.
922 *
923 * Creates an empty skymap from input user parameters. The reference
924 * coordinate is extracted from the mean pointing of the CTA observations
925 * in the observation container (see get_mean_pointing()).
926 *
927 * The following parameters are read:
928 * usepnt - Use pointing for reference coordinate?
929 * xref - Reference in Right Ascension or longitude (if usepnt=no)
930 * yref - Reference in Declination or latitude (if usepnt=no)
931 * proj - Coordinate projection
932 * coordsys - Coordinate system
933 * binsz - Bin size (deg)
934 * nxpix - Number of pixels in Right Ascension or longitude
935 * nypix - Number of pixels in Declination or latitude
936 ***************************************************************************/
937GSkyMap ctool::create_map(const GObservations& obs)
938{
939 // Read coordinate system and projection
940 std::string coordsys = (*this)["coordsys"].string();
941 std::string proj = (*this)["proj"].string();
942
943 // Read sky map reference
944 double xref = 0.0;
945 double yref = 0.0;
946 bool usepnt = (*this)["usepnt"].boolean();
947 if (!usepnt) {
948 xref = (*this)["xref"].real();
949 yref = (*this)["yref"].real();
950 }
951
952 // Read sky map pixel size and number of bins
953 double binsz = (*this)["binsz"].real();
954 int nxpix = (*this)["nxpix"].integer();
955 int nypix = (*this)["nypix"].integer();
956
957 // If requested, get pointing from observations
958 if (usepnt) {
959
960 // Get mean pointing
961 GSkyDir pnt = get_mean_pointing(obs);
962
963 // Use correct coordinate system
964 if (gammalib::toupper(coordsys) == "GAL") {
965 xref = pnt.l_deg();
966 yref = pnt.b_deg();
967 }
968 else {
969 xref = pnt.ra_deg();
970 yref = pnt.dec_deg();
971 }
972
973 } // endif: got mean pointing as reference
974
975 // Initialise sky map
976 GSkyMap map = GSkyMap(proj, coordsys, xref, yref, -binsz, binsz,
977 nxpix, nypix, 1);
978
979 // Return sky map
980 return map;
981}
982
983
984/***********************************************************************//**
985 * @brief Create a CTA event cube from user parameters
986 *
987 * @param[in] obs Observation container.
988 *
989 * Creates an empty CTA event cube from input user parameters. The reference
990 * coordinate is extracted from the mean pointing of the CTA observations
991 * in the observation container (see get_mean_pointing()). This method will
992 * be used if a ctool has the input parameter usepnt=true. The method
993 * appends a dummy GTI to the event cube.
994 *
995 * The following parameters are read:
996 * proj - Coordinate projection
997 * coordsys - Coordinate system
998 * binsz - Bin size (deg)
999 * nxpix - Number of pixels in Right Ascension or longitude
1000 * nypix - Number of pixels in Declination or latitude
1001 * ebinalg - Energy binning algorithm
1002 * emin - Minimum energy (if ebinalg != FILE)
1003 * emax - Maximum energy (if ebinalg != FILE)
1004 * enumbins - Number of energy bins (if ebinalg != FILE)
1005 ***************************************************************************/
1006GCTAEventCube ctool::create_cube(const GObservations& obs)
1007{
1008 // Get skymap
1009 GSkyMap map = create_map(obs);
1010
1011 // Set energy boundaries
1012 GEbounds ebounds = create_ebounds();
1013
1014 // Set dummy GTI that is needed for event cube creation
1015 GGti gti;
1016 gti.append(GTime(0.0), GTime(0.1234));
1017
1018 // Extend skymap to the requested number of maps
1019 map.nmaps(ebounds.size());
1020
1021 // Initialise event cube from all parameters
1022 GCTAEventCube cube = GCTAEventCube(map, ebounds, gti);
1023
1024 // Return event cube
1025 return cube;
1026}
1027
1028
1029/***********************************************************************//**
1030 * @brief Create a CTA observation from User parameters
1031 *
1032 * Creates an empty CTA observation from User parameters. An empty event list
1033 * including RoI, GTI and energy boundary information is attached to the
1034 * observation. The method also sets the pointing direction using the ra and
1035 * dec parameter, the ROI based on ra, dec and rad, a single GTI based on
1036 * tmin and tmax, and a single energy boundary based on emin and emax. The
1037 * method furthermore sets the ontime, livetime and deadtime correction
1038 * factor.
1039 *
1040 * The following parameters are read
1041 *
1042 * ra: Right Ascension of pointing and RoI centre (deg)
1043 * dec: Declination of pointing and RoI centre (deg)
1044 * rad: Radius of RoI (deg)
1045 * deadc: Deadtime correction factor
1046 * tmin: Start time
1047 * tmax: Stop time
1048 * emin: Minimum energy (TeV)
1049 * emax: Maximum energy (TeV)
1050 * mjdref: Time reference (optional)
1051 * instrument: Name of Cherenkov Telescope (optional)
1052 *
1053 * If the time reference parametere "mjdref" is not available, the CTA time
1054 * reference will be assumed.
1055 ***************************************************************************/
1056GCTAObservation ctool::create_cta_obs(void)
1057{
1058 // Get deadtime correction User parameter
1059 double deadc = (*this)["deadc"].real();
1060
1061 // Allocate CTA observation and empty event list
1062 GCTAObservation obs;
1063 GCTAEventList list;
1064
1065 // Set pointing direction
1066 GCTAPointing pnt = get_pointing();
1067
1068 // Set RoI
1069 GCTARoi roi = get_roi(pnt);
1070
1071 // Set time reference
1072 GTimeReference ref = (has_par("mjdref"))
1073 ? GTimeReference((*this)["mjdref"].real(), "s", "TT", "LOCAL")
1074 : GTimeReference(G_CTA_MJDREF, "s", "TT", "LOCAL");
1075
1076 // Set instrument
1077 std::string instrument = (has_par("instrument"))
1078 ? (*this)["instrument"].string()
1079 : "CTA";
1080
1081 // Set GTI
1082 GGti gti = get_gti(ref);
1083
1084 // Set energy boundaries
1085 GEbounds ebounds = get_ebounds();
1086
1087 // Set CTA event list attributes
1088 list.roi(roi);
1089 list.gti(gti);
1090 list.ebounds(ebounds);
1091
1092 // Attach empty event list to CTA observation
1093 obs.events(list);
1094
1095 // Set instrument, ontime, livetime and deadtime correction factor
1096 obs.instrument(instrument);
1097 obs.pointing(pnt);
1098 obs.ontime(gti.ontime());
1099 obs.livetime(gti.ontime()*deadc);
1100 obs.deadc(deadc);
1101
1102 // Return CTA observation
1103 return obs;
1104
1105}
1106
1107
1108/***********************************************************************//**
1109 * @brief Throws exception if inobs parameter is not valid
1110 *
1111 * @param[in] method Method name.
1112 *
1113 * Throw an exception if the "inobs" parameter is either "NONE" or an empty
1114 * string.
1115 *
1116 * @todo This method is only used by csobsinfo, csresmap and csspec. We
1117 * should try to get rid of it
1118 ***************************************************************************/
1119void ctool::require_inobs(const std::string& method)
1120{
1121 // Throw exception if no infile is given
1122 if ((*this)["inobs"].is_undefined()) {
1123 std::string msg = "A valid file needs to be specified for the "
1124 "\"inobs\" parameter, yet \""+
1125 (*this)["inobs"].current_value()+
1126 "\" was given."
1127 " Specify a valid observation definition or "
1128 "FITS file to proceed.";
1129 throw GException::invalid_value(method, msg);
1130 }
1131
1132 // Get inobs filename
1133 GFilename filename = (*this)["inobs"].filename();
1134
1135 // Return
1136 return;
1137}
1138
1139
1140/***********************************************************************//**
1141 * @brief Throws exception if inobs parameter is a counts cube
1142 *
1143 * @param[in] method Method name.
1144 *
1145 * Throw an exception if the inobs parameter is a counts cube.
1146 *
1147 * @todo This method is only used by ctobssim. We should try to get rid of it
1148 ***************************************************************************/
1149void ctool::require_inobs_nocube(const std::string& method)
1150{
1151 // Continues only if we have a valid filename
1152 if ((*this)["inobs"].is_valid()) {
1153
1154 // Get inobs filename
1155 GFilename filename = (*this)["inobs"].filename();
1156
1157 // Continue only if we have a FITS file
1158 if (filename.is_fits()) {
1159
1160 // Signal no cube
1161 bool is_cube = false;
1162
1163 // Try loading file as counts cube. If this is successful then
1164 // throw an exception
1165 try {
1166
1167 // Load cube from file
1168 GCTAEventCube cube(filename);
1169
1170 // If we're still alive then signal that we have a cube
1171 is_cube = true;
1172
1173 }
1174
1175 // Catch any exceptions
1176 catch (...) {
1177 ;
1178 }
1179
1180 // If we have a counts cube then throw an exception
1181 if (is_cube) {
1182 std::string msg = "A counts cube has been specified for the "
1183 "\"inobs\" parameter, yet no counts cube "
1184 "can be specified as input observation."
1185 " Instead, specify an event list or an "
1186 "observation definition file.";
1187 throw GException::invalid_value(method, msg);
1188 }
1189
1190 } // endif: we had a FITS file
1191
1192 } // endif: filename was valid
1193
1194 // Return
1195 return;
1196}
1197
1198
1199/***********************************************************************//**
1200 * @brief Log application parameters
1201 *
1202 * @param[in] chatter Minimum required chattiness
1203 *
1204 * Log all application parameters. If the ctools has an edisp parameter and
1205 * the edisp parameter is false then log a warning that energy dispersion
1206 * is not used.
1207 ***************************************************************************/
1208void ctool::log_parameters(const GChatter& chatter)
1209{
1210 // Use base class logger
1211 this->GApplication::log_parameters(chatter);
1212
1213 // If application has an edisp parameter but edisp is set to now then
1214 // log a warning that indicates that energy dispersion is not used
1215 if (has_par("edisp") && !(*this)["edisp"].boolean()) {
1216
1217 // Get chattiness of application
1218 GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1219
1220 // Only write parameters if the chattiness is at least equal to the
1221 // minimum required chattiness
1222 if (chattiness >= chatter) {
1223
1224 // Log warning
1225 log << std::endl;
1226 log << "WARNING: Energy dispersion will *NOT* be considered for the "
1227 "computation. To consider" << std::endl;
1228 log << " energy dispersion, please set the \"edisp\" "
1229 "parameter to \"yes\". Be aware that" << std::endl;
1230 log << " using energy dispersion will considerably slow "
1231 "down the computations." << std::endl;
1232
1233 } // endif: chatter level was sufficient
1234
1235 } // endif: application has edisp parameter and edisp=no
1236
1237 // Return
1238 return;
1239}
1240
1241
1242/***********************************************************************//**
1243 * @brief Log observation container
1244 *
1245 * @param[in] chatter Minimum required chattiness
1246 * @param[in] obs Observation container
1247 * @param[in] what String specifying the container content
1248 *
1249 * Log observations if chattiness is at least @p chatter.
1250 ***************************************************************************/
1251void ctool::log_observations(const GChatter& chatter,
1252 const GObservations& obs,
1253 const std::string& what)
1254{
1255 // Get chattiness of ctool
1256 GChatter chattiness = static_cast<GChatter>((*this)["chatter"].integer());
1257
1258 // Only write message if chattiness is at least equal to the minimum
1259 // required chattiness
1260 if (chattiness >= chatter) {
1261
1262 // Log observation header
1263 log << std::endl;
1264 log.header1(gammalib::number(what, obs.size()));
1265
1266 // Log observation content dependent on chattiness
1267 log << obs.print(chattiness) << std::endl;
1268
1269 } // endif: Chattiness satisfied minimum required level
1270
1271 // Return
1272 return;
1273}
1274
1275
1276/***********************************************************************//**
1277 * @brief Log model container
1278 *
1279 * @param[in] chatter Minimum required chattiness
1280 * @param[in] models Model container
1281 * @param[in] what String specifying the container content
1282 *
1283 * Log models if chattiness is at least @p chatter.
1284 ***************************************************************************/
1285void ctool::log_models(const GChatter& chatter,
1286 const GModels& models,
1287 const std::string& what)
1288{
1289 // Get chattiness of ctool
1290 GChatter chattiness = static_cast<GChatter>((*this)["chatter"].integer());
1291
1292 // Only write message if chattiness is at least equal to the minimum
1293 // required chattiness
1294 if (chattiness >= chatter) {
1295
1296 // Log observation header
1297 log << std::endl;
1298 log.header1(gammalib::number(what, models.size()));
1299
1300 // Log observation content dependent on chattiness
1301 log << models.print(chattiness) << std::endl;
1302
1303 } // endif: Chattiness satisfied minimum required level
1304
1305 // Return
1306 return;
1307}
1308
1309
1310/***********************************************************************//**
1311 * @brief Return RoI from User parameters
1312 *
1313 * @param[in] pnt Pointing direction.
1314 * @return Region of Interest.
1315 *
1316 * Returns Region of Interest (RoI) from the User parameters `ra`, `dec` and
1317 * `rad`. If any of the parameters `ra`, `dec` or `rad` is not valid an
1318 * invalid RoI is returned.
1319 *
1320 * If the `usepnt` parameter exists and is set to `yes`, the RoI centre will
1321 * be taken from the specified pointing direction instead of the `ra` and
1322 * `dec` parameters. Make sure to provide a valid pointing direction in case
1323 * that you want to use the `usepnt` User parameter.
1324 ***************************************************************************/
1325GCTARoi ctool::get_roi(const GCTAPointing& pnt)
1326{
1327 // Initialise an empty RoI
1328 GCTARoi roi;
1329
1330 // Signal whether the pointing instead of the user parameters should be
1331 // use for the RoI centre
1332 bool usepnt = (has_par("usepnt") && (*this)["usepnt"].boolean());
1333
1334 // If the pointing direction should be used then extract the RoI centre
1335 // from the pointing direction
1336 if (usepnt) {
1337 roi.centre(GCTAInstDir(pnt.dir()));
1338 }
1339
1340 // ... otherwise extract the RoI centre from "ra" and "dec" User
1341 // parameters if they are valid
1342 else if ((*this)["ra"].is_valid() && (*this)["dec"].is_valid()) {
1343
1344 // Get Sky direction
1345 GSkyDir skydir = get_skydir();
1346
1347 // Convert into CTA instrument direction
1348 GCTAInstDir instdir(skydir);
1349
1350 // Set RoI centre
1351 roi.centre(instdir);
1352
1353 // Signal that we have a centre
1354 usepnt = true;
1355
1356 } // endelse: set RoI centre from User parameters
1357
1358 // Extract the RoI radius from the "rad" parameter if we have a centre
1359 // and the "rad" parameter is valid
1360 if (usepnt && (*this)["rad"].is_valid()) {
1361 roi.radius((*this)["rad"].real());
1362 }
1363
1364 // Return RoI
1365 return roi;
1366}
1367
1368
1369/***********************************************************************//**
1370 * @brief Return energy boundaries from User parameters
1371 *
1372 * @return Energy boundaries.
1373 *
1374 * Returns energy boundaries from the User parameters `emin` and `emax`. If
1375 * one of `emin` or `emax` is not valid, an empty energy boundaries object
1376 * is returned. If `emin` is not valid, `emax` is not queried.
1377 ***************************************************************************/
1379{
1380 // Initialise energy boundaries
1381 GEbounds ebounds;
1382
1383 // Continue only if "emin" and "emax" has a valid energy value
1384 if ((*this)["emin"].is_valid() && (*this)["emax"].is_valid()) {
1385
1386 // Read minimum and maximum energy parameters in TeV
1387 double e_min = (*this)["emin"].real();
1388 double e_max = (*this)["emax"].real();
1389
1390 // Set energy boundaries
1391 GEnergy emin;
1392 GEnergy emax;
1393 emin.TeV(e_min);
1394 emax.TeV(e_max);
1395 ebounds.append(emin, emax);
1396
1397 } // endif: "emin" and "emax" parameters were valid
1398
1399 // Return energy boundaries
1400 return ebounds;
1401}
1402
1403
1404/***********************************************************************//**
1405 * @brief Return Good Time Intervals from User parameter
1406 *
1407 * @param[in] ref Time reference.
1408 * @return Good Time Intervals.
1409 *
1410 * Returns Good Time Intervals from the User parameters `tmin` and `tmax`,
1411 * taking into account the transformation into the CTA reference time system.
1412 * If one of `tmin` or `tmax` is not valid, an empty Good Time Interval
1413 * object is returned. If `tmin` is not valid, `tmax` is not queried.
1414 ***************************************************************************/
1415GGti ctool::get_gti(const GTimeReference& ref)
1416{
1417 // Initialise Good Time Intervals with reference time
1418 GGti gti(ref);
1419
1420 // Continue only if "tmin" has a valid time value
1421 if ((*this)["tmin"].is_valid() && (*this)["tmax"].is_valid()) {
1422
1423 // Get minimum and maximum time using the time reference for MET
1424 // times
1425 GTime tmin = (*this)["tmin"].time(ref);
1426 GTime tmax = (*this)["tmax"].time(ref);
1427
1428 // Append GTI
1429 gti.append(tmin, tmax);
1430
1431 } // endif: "tmin" and "tmax" parameters were valid
1432
1433 // Return GTI
1434 return gti;
1435}
1436
1437
1438/***********************************************************************//**
1439 * @brief Return CTA pointing from User parameters
1440 *
1441 * @return CTA pointing.
1442 *
1443 * Returns CTA pointing from the User parameters `ra` and `dec`.
1444 ***************************************************************************/
1445GCTAPointing ctool::get_pointing(void)
1446{
1447 // Get sky direction
1448 GSkyDir skydir = get_skydir();
1449
1450 // Set CTA pointing
1451 GCTAPointing pnt(skydir);
1452
1453 // Return pointing
1454 return pnt;
1455}
1456
1457
1458/***********************************************************************//**
1459 * @brief Return sky direction from User parameters
1460 *
1461 * @return Sky direction.
1462 *
1463 * Returns sky direction from the User parameters `ra` and `dec`.
1464 ***************************************************************************/
1466{
1467 // Read Right Ascension and Declination parameters
1468 double ra = (*this)["ra"].real();
1469 double dec = (*this)["dec"].real();
1470
1471 // Set sky direction
1472 GSkyDir skydir;
1473 skydir.radec_deg(ra, dec);
1474
1475 // Return sky direction
1476 return skydir;
1477}
1478
1479
1480/***********************************************************************//**
1481 * @brief Set output file name.
1482 *
1483 * @param[in] filename Input file name.
1484 * @return Output file name.
1485 *
1486 * Converts an input file name into an output filename by prepending the
1487 * prefix specified by the `prefix` parameter to the input file name. Any
1488 * path as well as extension will be stripped from the input file name. Also
1489 * a trailing `.gz` will be stripped as one cannot write into gzipped files.
1490 ***************************************************************************/
1491std::string ctool::set_outfile_name(const std::string& filename)
1492{
1493 // Get prefix User parameter
1494 std::string prefix = (*this)["prefix"].string();
1495
1496 // Create filename
1497 GFilename fname(filename);
1498
1499 // Split input filename without any extensions into path elements
1500 std::vector<std::string> elements = gammalib::split(fname.url(), "/");
1501
1502 // The last path element is the filename
1503 std::string outname = (elements.size() > 0) ?
1504 prefix + elements[elements.size()-1] : "";
1505
1506 // Strip any ".gz" from the outname
1507 std::string strip = ".gz";
1508 std::string::size_type i_start = outname.find(strip);
1509 if (i_start != std::string::npos) {
1510 outname.erase(i_start, strip.length());
1511 }
1512
1513 // Return output filename
1514 return outname;
1515}
1516
1517
1518/***********************************************************************//**
1519 * @brief Query user parameters for stacked analysis
1520 *
1521 * @return True if a stacked analysis should be done.
1522 *
1523 * Queries the user parameters `emin`, `emax` and `enumbins`, and if
1524 * `enumbins` is positive, signal that a stacked analysis is requested. In
1525 * that case, also the `coordsys`, `proj`, `xref`, `yref`, `nxpix`, `nypix`
1526 * and `binsz` parameters are queried.
1527 *
1528 * Using this method assures that the parameters are always queried in the
1529 * same order.
1530 ***************************************************************************/
1532{
1533 // First query the minimum and maximum energies
1534 (*this)["emin"].real();
1535 (*this)["emax"].real();
1536
1537 // Now query the number of energy bins and set the stacked flag
1538 bool stacked = ((*this)["enumbins"].integer() > 0) ? true : false;
1539
1540 // If a stacked analysis is requested then query the spatial definition
1541 // of the cube
1542 if (stacked) {
1543 (*this)["coordsys"].string();
1544 (*this)["proj"].string();
1545 (*this)["xref"].real();
1546 (*this)["yref"].real();
1547 (*this)["nxpix"].integer();
1548 (*this)["nypix"].integer();
1549 (*this)["binsz"].real();
1550 }
1551
1552 // Return stacked flag
1553 return stacked;
1554}
1555
1556
1557/***********************************************************************//**
1558 * @brief Query user parameters for On/Off analysis
1559 *
1560 * @return True if an On/Off analysis should be done.
1561 *
1562 * Queries the parameter "method", and if this is "ONOFF" signal that an On/
1563 * Off analysis is requested. In that case, all necessary input parameters of
1564 * csphagen are queried: "inexclusion", "emin", "emax", "enumbins",
1565 * "coordsys", "xref", "yref", "srcshape", "rad", "bkgmethod", "bkgregmin",
1566 * "maxoffset", "etruemin", "etruemax", and "etruebins".
1567 *
1568 * Using this method assures that the parameters are always queried in the
1569 * same order.
1570 ***************************************************************************/
1572{
1573 // Set onoff flag
1574 bool onoff = ((*this)["method"].string() == "ONOFF");
1575
1576 // Query analysis method chosen
1577 if (onoff) {
1578
1579 // Query csphagen parameters
1580 (*this)["inexclusion"].query();
1581 (*this)["emin"].real();
1582 (*this)["emax"].real();
1583 (*this)["enumbins"].integer();
1584 (*this)["coordsys"].string();
1585 (*this)["xref"].real();
1586 (*this)["yref"].real();
1587 if ((*this)["srcshape"].string() == "CIRCLE"){
1588 (*this)["rad"].real();
1589 }
1590 if ((*this)["bkgmethod"].string() == "REFLECTED"){
1591 (*this)["bkgregmin"].integer();
1592 }
1593 (*this)["maxoffset"].real();
1594 (*this)["etruemin"].real();
1595 (*this)["etruemax"].real();
1596 (*this)["etruebins"].integer();
1597
1598 } // endif: method was ONOFF
1599
1600 // Return onoff flag
1601 return onoff;
1602}
1603
1604
1605/***********************************************************************//**
1606 * @brief Set response for all CTA observations in container
1607 *
1608 * @param[in,out] obs Observation container
1609 *
1610 * Set the response for all CTA observations in the container that so far
1611 * have no response information available. See the set_obs_response()
1612 * method for more information about the user parameters that are read.
1613 ***************************************************************************/
1614void ctool::set_response(GObservations& obs)
1615{
1616 // Setup response for all observations
1617 for (int i = 0; i < obs.size(); ++i) {
1618
1619 // Is this observation a CTA observation?
1620 GCTAObservation* cta = dynamic_cast<GCTAObservation*>(obs[i]);
1621
1622 // Yes, the set the response if the observation does not yet have one
1623 if (cta != NULL) {
1624
1625 // Set response if we don't have one
1626 if (!cta->has_response()) {
1627 set_obs_response(cta);
1628 }
1629
1630 } // endif: observation was a CTA observation
1631
1632 } // endfor: looped over all observations
1633
1634 // Return
1635 return;
1636}
1637
1638
1639/***********************************************************************//**
1640 * @brief Set energy dispersion to CTA observations
1641 *
1642 * @param[in] obs Observation container.
1643 * @param[in] edisp Requested energy dispersion flag.
1644 * @return Vector of old energy dispersion flags.
1645 *
1646 * Applies energy dispersion to all CTA observations. The method returns a
1647 * vector with the old energy dispersion flags.
1648 ***************************************************************************/
1649std::vector<bool> ctool::set_edisp(GObservations& obs, const bool& edisp) const
1650{
1651 // Allocate vector for energy dispersion flags of all observation in
1652 // the container
1653 std::vector<bool> old_edisp(obs.size(), false);
1654
1655 // Loop over all observations in observation container
1656 for (int i = 0; i < obs.size(); ++i) {
1657
1658 // Is this observation a CTA observation?
1659 GCTAObservation* cta = dynamic_cast<GCTAObservation*>(obs[i]);
1660
1661 // Yes, then set the energy dispersion flag
1662 if (cta != NULL) {
1663
1664 // Save old energy dispersion flag
1665 old_edisp[i] = cta->response()->apply_edisp();
1666
1667 // Set energy dispersion flag according to input parameter
1668 cta->response()->apply_edisp(edisp);
1669
1670 } // endif: observation was CTA observation
1671
1672 } // endfor: loop over all observations in container
1673
1674 // Return old energy dispersion flags
1675 return old_edisp;
1676}
1677
1678
1679/***********************************************************************//**
1680 * @brief Restore energy dispersion flags of CTA observations
1681 *
1682 * @param[in] obs Observation container.
1683 * @param[in] edisp Vector of energy dispersion flags.
1684 *
1685 * Restores energy dispersion flags that have previously been returned by
1686 * the set_edisp() method. The number of observations between the calls
1687 * of the set_edisp() and this method has to be the same.
1688 ***************************************************************************/
1689void ctool::restore_edisp(GObservations& obs, const std::vector<bool>& edisp) const
1690{
1691 // Check that energy dispersion flag vector has the same size as the
1692 // observation container
1693 if (edisp.size() != obs.size()) {
1694 std::string msg = "Size of energy dispersion flag vector ("+
1695 gammalib::str(edisp.size())+" elements) is "
1696 "incompatible with number of observations in the "
1697 "observation container ("+gammalib::str(obs.size())+
1698 " observation).";
1699 throw GException::invalid_value(G_RESTORE_EDISP, msg);
1700 }
1701
1702 // Loop over all observations in observation container
1703 for (int i = 0; i < obs.size(); ++i) {
1704
1705 // Is this observation a CTA observation?
1706 GCTAObservation* cta = dynamic_cast<GCTAObservation*>(obs[i]);
1707
1708 // Yes, then restore the energy dispersion flag
1709 if (cta != NULL) {
1710 cta->response()->apply_edisp(edisp[i]);
1711 }
1712
1713 } // endfor: loop over all observations in container
1714
1715 // Return
1716 return;
1717}
1718
1719
1720/***********************************************************************//**
1721 * @brief Set response for CTA observation
1722 *
1723 * @param[in,out] obs CTA observation
1724 *
1725 * @exception GException::invalid_value
1726 * Energy dispersion requested but not energy dispersion cube given
1727 *
1728 * Set the response for one CTA observation. If the CTA observation contains
1729 * a counts cube the method first attempts to set the response information
1730 * using the following user parameters
1731 *
1732 * expcube: Exposure cube file
1733 * psfcube: Point spread function cube file
1734 * edispcube: Energy dispersion cube file (optional)
1735 * bkgcube: Background cube file
1736 *
1737 * If none of the @p expcube, @p psfcube and @p bkgcube parameters is
1738 * "NONE" or empty, the method will allocate a stacked response for the
1739 * observation. In case that an energy dispersion cube is provided using
1740 * the @p edispcube parameter, this energy dispersion cube is also loaded
1741 * and attached to the stacked response function.
1742 *
1743 * In case that any of the @p expcube, @p psfcube and @p bkgcube parameters
1744 * is "NONE" or empty, the method will assume that a IRF response should
1745 * be used, and will attempty to set the response information using the
1746 * following user parameters
1747 *
1748 * caldb: Calibration database
1749 * irf: Instrument response function
1750 *
1751 ***************************************************************************/
1752void ctool::set_obs_response(GCTAObservation* obs)
1753{
1754 // Initialise response flag
1755 bool has_response = false;
1756
1757 // If the observation contains a counts cube, then check whether
1758 // response information for a stacked analysis is provided, and if so,
1759 // set that response information for the CTA observation
1760 if (dynamic_cast<const GCTAEventCube*>(obs->events()) != NULL) {
1761
1762 // Check if response cube parameters are provided. We need all four
1763 // response cube parameters.
1764 if (has_par("expcube") && has_par("psfcube") &&
1765 has_par("bkgcube") && has_par("edispcube")) {
1766
1767 // Check if we need to query the energy dispersion cube. We do
1768 // not need to query the cube is an "edisp" parameter exists and
1769 // if this parameter is set to "no".
1770 bool query_edisp = true;
1771 if (has_par("edisp")) {
1772 query_edisp = (*this)["edisp"].boolean();
1773 }
1774
1775 // Extract stacked response information if available
1776 if ((*this)["expcube"].is_valid() &&
1777 (*this)["psfcube"].is_valid() &&
1778 (*this)["bkgcube"].is_valid()) {
1779
1780 // Load exposure, PSF and background cubes and query energy
1781 // dispersion cube
1782 GCTACubeExposure exposure((*this)["expcube"].filename());
1783 GCTACubePsf psf((*this)["psfcube"].filename());
1784 if (query_edisp && (*this)["edispcube"].is_valid()) {
1785 (*this)["edispcube"].filename();
1786 }
1787 GCTACubeBackground background((*this)["bkgcube"].filename());
1788
1789 // Set response
1790 if (query_edisp) {
1791
1792 // If energy dispersion cube was provided then use it
1793 if ((*this)["edispcube"].is_valid()) {
1794 GCTACubeEdisp edisp((*this)["edispcube"].filename());
1795 obs->response(exposure, psf, edisp, background);
1796 }
1797
1798 // ... otherwise work without energy dispersion
1799 else {
1800
1801 // If energy dispersion was requested but no cube
1802 // was provided then throw an exception
1803 if (has_par("edisp") && (*this)["edisp"].boolean()) {
1804 std::string msg = "Energy dispersion requested but "
1805 "no energy dispersion cube was "
1806 "specified.";
1807 throw GException::invalid_value(G_SET_OBS_RESPONSE,
1808 msg);
1809 }
1810
1811 // Set response without energy dispersion
1812 obs->response(exposure, psf, background);
1813
1814 } // endelse: no energy dispersion was available
1815
1816 } // endif: energy dispersion needed
1817
1818 // ... otherwise work without energy dispersion
1819 else {
1820 obs->response(exposure, psf, background);
1821 }
1822
1823 // Signal that response is available
1824 has_response = true;
1825
1826 } // endif: filenames were available
1827
1828 } // endif: expcube, psfcube and bkdcube parameters valid
1829 } // endif: observation contains a counts cube
1830
1831 // If we have not yet response information then get it now from the
1832 // caldb and irf user parameters
1833 if (!has_response) {
1834
1835 // Load response information
1836 std::string database = (*this)["caldb"].string();
1837 std::string irf = (*this)["irf"].string();
1838
1839 // Optionally set instrument
1840 std::string instrument = (has_par("instrument"))
1841 ? (*this)["instrument"].string()
1842 : "CTA";
1843
1844 // Create an XML element containing the database and IRF name. This
1845 // kluge will make sure that the information is later written
1846 // to the observation definition XML file, in case an observation
1847 // definition XML file is written.
1848 std::string parameter = "parameter name=\"Calibration\""
1849 " database=\""+database+"\""
1850 " response=\""+irf+"\"";
1851
1852 // Allocate XML element
1853 GXmlElement xml;
1854
1855 // Set instrument attribute
1856 xml.attribute("instrument", instrument);
1857
1858 // Append parameter
1859 xml.append(parameter);
1860
1861 // Create CTA response
1862 GCTAResponseIrf response(xml);
1863
1864 // Attach response to observation
1865 obs->response(response);
1866
1867 } // endif: no response information was available
1868
1869 // Return
1870 return;
1871}
1872
1873
1874/***********************************************************************//**
1875 * @brief Get observation container
1876 *
1877 * @param[in] get_response Indicates whether response information should
1878 * be loaded (default: true)
1879 *
1880 * Get an observation container according to the user parameters. The method
1881 * supports loading of a individual FITS file or an observation definition
1882 * file in XML format. If the input filename is empty, parameters are read
1883 * to build a CTA observation from scratch.
1884 ***************************************************************************/
1885GObservations ctool::get_observations(const bool& get_response)
1886{
1887 // Initialise empty observation container
1888 GObservations obs;
1889
1890 // If no observation definition file has been specified then read all
1891 // parameters that are necessary to create an observation from scratch
1892 if ((*this)["inobs"].is_undefined()) {
1893
1894 // Setup a new CTA observation
1895 GCTAObservation cta_obs = create_cta_obs();
1896
1897 // Get response if required
1898 if (get_response) {
1899
1900 // Set response
1901 set_obs_response(&cta_obs);
1902
1903 } // endif: response was required
1904
1905 // Append observation to container
1906 obs.append(cta_obs);
1907
1908 } // endif: filename was valid
1909
1910 // ... otherwise we have a file name
1911 else {
1912
1913 // Get the filename from the input parameters
1914 std::string filename = (*this)["inobs"].filename();
1915
1916 // If file is a FITS file then create an empty CTA observation
1917 // and load file into observation
1918 if (GFilename(filename).is_fits()) {
1919
1920 // Load CTA observation
1921 GCTAObservation cta_obs(filename);
1922
1923 // Get response if required
1924 if (get_response) {
1925
1926 // Set response
1927 set_obs_response(&cta_obs);
1928
1929 } // endif: response was required
1930
1931 // Append observation to container
1932 obs.append(cta_obs);
1933
1934 // Signal that no XML file should be used for storage
1935 m_use_xml = false;
1936
1937 }
1938
1939 // ... otherwise load file into observation container
1940 else {
1941
1942 // Load observations from XML file
1943 obs.load(filename);
1944
1945 // Get response if required
1946 if (get_response) {
1947
1948 // For all observations that have no response, set the response
1949 // from the task parameters
1950 set_response(obs);
1951
1952 } // endif: response was required
1953
1954 // Signal that XML file should be used for storage
1955 m_use_xml = true;
1956
1957 } // endelse: file was an XML file
1958
1959 } // endif: "inobs" filename was specified
1960
1961 // Return observation container
1962 return obs;
1963}
1964
1965
1966/***********************************************************************//**
1967 * @brief Derives mean pointing from CTA observations
1968 *
1969 * @param[in] obs Observation container.
1970 * @return Pointing direction.
1971 *
1972 * Computes the mean pointing direction from all CTA observations in the
1973 * observation container.
1974 *
1975 * @todo This method does not work properly for wrap arounds in Right
1976 * Ascension. It certainly will also lead to bad results near the
1977 * celestial pole.
1978 ***************************************************************************/
1979GSkyDir ctool::get_mean_pointing(const GObservations& obs)
1980{
1981 // Initialise values
1982 double xref = 0.0;
1983 double yref = 0.0;
1984 int n_pnt = 0;
1985
1986 // Loop over all observations to compute mean pointings
1987 for (int i = 0; i < obs.size(); ++i) {
1988
1989 // If we have a CTA observation then extract the pointing direction
1990 const GCTAObservation* cta_obs = dynamic_cast<const GCTAObservation*>(obs[i]);
1991 if (cta_obs != NULL) {
1992
1993 // Get pointing
1994 const GCTAPointing& pnt = cta_obs->pointing();
1995
1996 // Add to coordinates
1997 xref += pnt.dir().ra_deg();
1998 yref += pnt.dir().dec_deg();
1999 n_pnt += 1;
2000
2001 } // endif: cta observation was valid
2002
2003 } // endfor: loop over all observations
2004
2005 // Signal if no pointing is found
2006 if (n_pnt < 1) {
2007 std::string msg = "No valid CTA observation has been found in "
2008 "observation list, hence no pointing information "
2009 "could be extracted. Use the \"usepnt=no\" "
2010 "option and specify the pointing explicitly.";
2011 throw GException::invalid_value(G_GET_MEAN_POINTING, msg);
2012 }
2013
2014 // Calculate mean coordinates
2015 double ra = xref / double(n_pnt);
2016 double dec = yref / double(n_pnt);
2017
2018 // Initialise sky dir
2019 GSkyDir dir = GSkyDir();
2020 dir.radec_deg(ra,dec);
2021
2022 // Return sky direction
2023 return dir;
2024}
2025
2026
2027/***********************************************************************//**
2028 * @brief Get current resident set size (physical memory use) in Bytes
2029 *
2030 * @return Physical memory use in Bytes.
2031 *
2032 * @todo This method is currently not used and can be removed if we do not
2033 * use it
2034 ***************************************************************************/
2036{
2037 // Initialize resident set size
2038 size_t rss = 0;
2039
2040 // Determine resident set size (architecture dependent)
2041 // OSX
2042 #if defined(__APPLE__) && defined(__MACH__)
2043 #ifdef MACH_TASK_BASIC_INFO
2044 struct mach_task_basic_info info;
2045 mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
2046 if (task_info(mach_task_self( ), MACH_TASK_BASIC_INFO,
2047 (task_info_t)&info, &infoCount) == KERN_SUCCESS) {
2048 rss = (size_t)info.resident_size;
2049 }
2050 #else
2051 struct task_basic_info info;
2052 mach_msg_type_number_t info_count = TASK_BASIC_INFO_COUNT;
2053 if (task_info(mach_task_self(), TASK_BASIC_INFO,
2054 (task_info_t)&info, &info_count) == KERN_SUCCESS) {
2055 rss = (size_t)info.resident_size;
2056 }
2057 #endif
2058 // Linux
2059 #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
2060 FILE* fp = NULL;
2061 if ((fp = fopen( "/proc/self/statm", "r" )) != NULL) {
2062 if (fscanf( fp, "%*s%ld", &rss ) == 1) {
2063 rss *= (size_t)sysconf(_SC_PAGESIZE);
2064 }
2065 fclose(fp);
2066 }
2067 // AIX, BSD, Solaris, and Unknown OS
2068 #else
2069 rss = 0;
2070 #endif
2071
2072 // Return resident set size
2073 return rss;
2074}
2075
2076
2077/***********************************************************************//**
2078 * @brief Return observation header string
2079 *
2080 * @param[in] obs Pointer to observation.
2081 * @return String containing observation information.
2082 *
2083 * Returns a string that contains observation information, including the
2084 * instrument name, the observation name, and the observation ID. The format
2085 * of the string is
2086 *
2087 * XXX observation "name" (id=YYYYY)
2088 *
2089 ***************************************************************************/
2090std::string ctool::get_obs_header(const GObservation* obs) const
2091{
2092 // Initialise header string
2093 std::string header = obs->instrument() + " observation";
2094
2095 // If observation name is not empty then add name
2096 if (!obs->name().empty()) {
2097 header += " \"" + obs->name() + "\"";
2098 }
2099
2100 // If observation ID is not empty then add ID
2101 if (!obs->id().empty()) {
2102 header += " (id=" + obs->id() +")";
2103 }
2104
2105 // Return header
2106 return header;
2107}
2108
2109
2110/***********************************************************************//**
2111 * @brief Insert observation energy boundaries into list of energies
2112 *
2113 * @param[in] energies Energies.
2114 * @param[in] obs Observation container.
2115 * @return Energies.
2116 *
2117 * Inserts the energy boundaries of an observation in a list of @p energies.
2118 ***************************************************************************/
2119GEnergies ctool::insert_energy_boundaries(const GEnergies& energies,
2120 const GCTAObservation& obs)
2121{
2122 // Create copy of input energies
2123 GEnergies engs = energies;
2124
2125 // Get the energy boundaries of the event list
2126 GEbounds ebounds = obs.ebounds();
2127
2128 // Loop over all boundary energies
2129 for (int iebin = 0; iebin <= ebounds.size(); ++iebin) {
2130
2131 // Get boundary energy
2132 GEnergy energy;
2133 if (iebin < ebounds.size()) {
2134 energy = ebounds.emin(iebin);
2135 }
2136 else {
2137 energy = ebounds.emax();
2138 }
2139
2140 // Get index before which the energy should be appended
2141 bool insert = true;
2142 int index = -1;
2143 for (int k = 0; k < engs.size(); ++k) {
2144
2145 // If energy exists already then skip the boundary and examine the
2146 // next one. We consider here 1 MeV as being sufficiently close.
2147 if (std::abs(engs[k].MeV()-energy.MeV()) < 1.0) {
2148 insert = false;
2149 break;
2150 }
2151
2152 // If energy is above the boundary energy we found the index
2153 if (engs[k] > energy) {
2154 index = k;
2155 break;
2156 }
2157
2158 } // endfor: search index for insertion
2159
2160 // Insert energy if requested
2161 if (insert) {
2162
2163 // Insert or append energy
2164 if (index != -1) {
2165 engs.insert(index, energy);
2166 }
2167 else {
2168 engs.append(energy);
2169 }
2170
2171 // Log energy insertion. Circumvent const correctness
2172 log_value(NORMAL, "Insert energy", energy.print());
2173
2174 } // endif: energy insertion requested
2175
2176 } // endfor: looped over all energy boundaries
2177
2178 // Return energies
2179 return engs;
2180}
2181
2182
2183/***********************************************************************//**
2184 * @brief Determine the counts cube layer usage.
2185 *
2186 * @param[in] cube_ebounds Energy boundaries of the counts cube.
2187 * @param[in] list_ebounds Energy boundaries of the event list.
2188 * @return Vector of usage flags.
2189 *
2190 * Determines a vector of counts cube layer usage flags that signal whether
2191 * an event should actually be filled in the counts cube or not. This makes
2192 * sure that no partially filled bins will exist in the counts cube.
2193 ***************************************************************************/
2194std::vector<bool> ctool::cube_layer_usage(const GEbounds& cube_ebounds,
2195 const GEbounds& list_ebounds) const
2196{
2197 // Set energy margin
2198 const GEnergy energy_margin(0.01, "GeV");
2199
2200 // Initialise usage vector
2201 std::vector<bool> usage(cube_ebounds.size(), true);
2202
2203 // Loop over all energy bins of the cube
2204 for (int iebin = 0; iebin < cube_ebounds.size(); ++iebin) {
2205
2206 // If the counts cube energy bin is fully contained within the
2207 // energy boundaries of the event list the signal usage of this
2208 // counts cube bin. Partially overlapping energy bins are signaled
2209 // for non-usage, which avoids having partially filled bins in the
2210 // counts cube. Some margin is applied that effectively reduces the
2211 // width of the counts cube energy bin. This should cope with any
2212 // rounding errors that come from reading and writing the energy
2213 // boundary information to a FITS file.
2214 usage[iebin] = list_ebounds.contains(cube_ebounds.emin(iebin) +
2215 energy_margin,
2216 cube_ebounds.emax(iebin) -
2217 energy_margin);
2218
2219 } // endfor: looped over all energy bins
2220
2221 // Return usage
2222 return usage;
2223}
2224
2225
2226/***********************************************************************//**
2227 * @brief Save event list into FITS file
2228 *
2229 * @param[in] obs Pointer to CTA observation.
2230 * @param[in] infile Input file name.
2231 * @param[in] evtname Event extension name.
2232 * @param[in] gtiname GTI extension name.
2233 * @param[in] outfile Output file name.
2234 *
2235 * Saves an event list and the corresponding Good Time Intervals into a FITS
2236 * file and copy all others extensions from the input file to the output
2237 * file.
2238 *
2239 * If an extension name is specified in the @p outfile argument, the events
2240 * and eventually also the Good Time Intervals will be extracted from the
2241 * argument and used for writing the events. The format is
2242 *
2243 * <filename>[<event extension name;GTI extension name>]
2244 *
2245 * where <filename> needs to be replaced by the name of the FITS file,
2246 * and <event extension name;GTI extension name> by the name of the events
2247 * and Good Time Intervals extensions. For example
2248 *
2249 * myfits.fits[EVENTS1;GTI1]
2250 *
2251 * will write the selected events into the `EVENTS1` extension and the
2252 * Good Time Intervals into the `GTI1` extension of the `myfits.fits` FITS
2253 * file. If the Good Time Intervals extension name is skipped, e.g.
2254 *
2255 * myfits.fits[EVENTS1]
2256 *
2257 * the original extension name for the Good Time Intervals will be kept.
2258 * Analogously, only the Good Time Intervals extension name can be changed
2259 * by specifying
2260 *
2261 * myfits.fits[;GTI1]
2262 *
2263 * In none of the cases will the original events and Good Time Intervals be
2264 * copied over to the output file.
2265 ***************************************************************************/
2266void ctool::save_event_list(const GCTAObservation* obs,
2267 const std::string& infile,
2268 const std::string& evtname,
2269 const std::string& gtiname,
2270 const std::string& outfile) const
2271{
2272 // Save only if we have an event list
2273 if (obs->eventtype() == "EventList") {
2274
2275 // Set output FITS file event extension names
2276 GFilename outname(outfile);
2277 std::string outevt = evtname;
2278 std::string outgti = gtiname;
2279 if (outname.has_extname()) {
2280 std::vector<std::string> extnames =
2281 gammalib::split(outname.extname(), ";");
2282 if (extnames.size() > 0) {
2283 std::string extname = gammalib::strip_whitespace(extnames[0]);
2284 if (!extname.empty()) {
2285 outevt = extname;
2286 }
2287 }
2288 if (extnames.size() > 1) {
2289 std::string extname = gammalib::strip_whitespace(extnames[1]);
2290 if (!extname.empty()) {
2291 outgti = extname;
2292 }
2293 }
2294 }
2295
2296 // Make writing of FITS file OpenMP thread save
2297 #pragma omp critical(ctool_save_event_list)
2298 {
2299
2300 // Create output FITS file
2301 GFits outfits;
2302
2303 // Write observation into FITS file
2304 obs->write(outfits, outevt, outgti);
2305
2306 // Copy all extensions other than evtname and gtiname extensions
2307 // from the input to the output event list. The evtname and
2308 // gtiname extensions are written by the save method, all others
2309 // that may eventually be present have to be copied over
2310 // explicitly.
2311 GFits infits(infile);
2312 for (int extno = 1; extno < infits.size(); ++extno) {
2313 GFitsHDU* hdu = infits.at(extno);
2314 if (hdu->extname() != evtname &&
2315 hdu->extname() != gtiname &&
2316 hdu->extname() != outevt &&
2317 hdu->extname() != outgti) {
2318 outfits.append(*hdu);
2319 }
2320 }
2321
2322 // Close input file
2323 infits.close();
2324
2325 // Stamp FITS file
2326 stamp(outfits);
2327
2328 // Save file to disk and close it (we need both operations)
2329 outfits.saveto(outname.url(), clobber());
2330 outfits.close();
2331
2332 } // end: omp critical section
2333
2334 } // endif: observation was unbinned
2335
2336 // Return
2337 return;
2338}
2339
2340
2341/***********************************************************************//**
2342 * @brief Get Good Time Intervals extension name
2343 *
2344 * @param[in] filename Input file name.
2345 * @param[in] evtname Events extension name.
2346 *
2347 * Extracts the Good Time Intervals extension name from the event file. We
2348 * do this by loading the events and accessing the Good Time Intervals
2349 * extension name using the GCTAEventList::gtiname() method. If the file name
2350 * is empty, the method returns `GTI`.
2351 ***************************************************************************/
2352std::string ctool::get_gtiname(const std::string& filename,
2353 const std::string& evtname) const
2354{
2355 // Initialise GTI name
2356 std::string gtiname = gammalib::extname_gti;
2357
2358 // Continue only if the filename is not empty
2359 if (!filename.empty()) {
2360
2361 // Load events
2362 GCTAEventList events(filename+"["+evtname+"]");
2363
2364 // Get GTI name
2365 gtiname = events.gtiname();
2366
2367 }
2368
2369 // Return GTI name
2370 return (gtiname);
2371}
2372
2373
2374/***********************************************************************//**
2375 * @brief Set warning string if there are too few energies
2376 *
2377 * @param[in] energies Energies.
2378 * @return Warning string.
2379 *
2380 * Sets a warning string if there are too few @p energies in the container.
2381 * For a binned or stacked analysis to be accurate, at least 25 bins per
2382 * decade are required. If the provided number of energies is less than this
2383 * number a warning string will be returned. Otherwise an empty string will
2384 * be returned.
2385 ***************************************************************************/
2386std::string ctool::warn_too_few_energies(const GEnergies& energies) const
2387{
2388 // Initialise warning
2389 std::string warning;
2390
2391 // Retrieve the number of energies
2392 int n = energies.size();
2393
2394 // Compute the required number of energy bins
2395 int nrequired = 0;
2396 if (n > 0) {
2397 double logEmin = std::log10(energies[0].TeV());
2398 double logEmax = std::log10(energies[n-1].TeV());
2399 nrequired = int((logEmax - logEmin) * 25.0);
2400 }
2401
2402 // Warn if there are not enough energy bins
2403 if (n < nrequired) {
2404 warning.append("\nWARNING: Only "+gammalib::str(n-1)+" energy bins "
2405 "have been requested. This may be too few energy "
2406 "bins."
2407 "\n At least 25 bins per decade in energy are "
2408 "recommended, which for the given"
2409 "\n energy range would be "+
2410 gammalib::str(nrequired)+" bins. Consider increasing "
2411 "the number of energy bins.");
2412 }
2413
2414 // Return warning
2415 return warning;
2416}
2417
2418
2419/***********************************************************************//**
2420 * @brief Set warning string if file has no .xml suffix
2421 *
2422 * @param[in] filename Filename.
2423 * @return Warning string.
2424 *
2425 * Sets a warning string if the filename has not ".xml" suffix.
2426 ***************************************************************************/
2427std::string ctool::warn_xml_suffix(const GFilename& filename) const
2428{
2429 // Initialise warning
2430 std::string warning;
2431
2432 // Get filename suffix
2433 std::string fname = std::string(filename);
2434 std::string suffix = gammalib::tolower(fname.substr(fname.length()-4,4));
2435
2436 // Warn if there are not enough energy bins
2437 if (suffix != ".xml") {
2438 warning.append("WARNING: Name of observation definition output file "
2439 "\""+filename+"\" does not terminate with \".xml\"."
2440 "\n This is not an error, but may be misleading. "
2441 "It is recommended to use the suffix"
2442 "\n \".xml\" for observation definition files.");
2443 }
2444
2445 // Return warning
2446 return warning;
2447}
2448
2449
2450/*==========================================================================
2451 = =
2452 = Private methods for SWIG =
2453 = =
2454 ==========================================================================*/
2455
2456/***********************************************************************//**
2457 * @brief Dump help text in the console
2458 *
2459 * Dumps the help text for the ctool into the console. The help text is
2460 * located in the folder
2461 *
2462 * $CTOOLS//share/help/
2463 *
2464 * of the ctools installation and the help file has the name "name().txt",
2465 * where name() stands for the name of the ctool.
2466 ***************************************************************************/
2467void ctool::provide_help(void) const
2468{
2469 // Allocate line buffer
2470 const int n = 1000;
2471
2472 // Build help file name
2473 std::string helpfile = name()+".txt";
2474
2475 // Get ctools environment variable
2476 char* ptr = std::getenv("CTOOLS");
2477 if (ptr == NULL) {
2478 std::string msg = "CTOOLS environment variable not set, cannot "
2479 "display help file. Please set the CTOOLS "
2480 "environment variable.";
2481 throw GException::invalid_value(G_PROVIDE_HELP, msg);
2482 }
2483
2484 // If help file exists then display it, otherwise notify that no
2485 // help is available
2486 std::string fname = std::string(ptr) + "/share/help/" + helpfile;
2487 FILE* fptr = fopen(fname.c_str(), "r");
2488 if (fptr != NULL) {
2489 char line[n];
2490 while (fgets(line, n, fptr) != NULL) {
2491 std::cout << std::string(line);
2492 }
2493 fclose(fptr);
2494 }
2495 else {
2496 std::cout << "No help available for "+name()+"." << std::endl;
2497 }
2498
2499 // Return
2500 return;
2501}
Base class for ctools.
Definition ctool.hpp:50
virtual void run(void)
Run ctool.
Definition ctool.cpp:257
virtual void process(void)=0
GEnergies create_energies(void)
Create energies from user parameters.
Definition ctool.cpp:776
void set_obs_response(GCTAObservation *obs)
Set response for CTA observation.
Definition ctool.cpp:1752
std::string warn_too_few_energies(const GEnergies &energies) const
Set warning string if there are too few energies.
Definition ctool.cpp:2386
void set_response(GObservations &obs)
Set response for all CTA observations in container.
Definition ctool.cpp:1614
void require_inobs_nocube(const std::string &method)
Throws exception if inobs parameter is a counts cube.
Definition ctool.cpp:1149
bool m_use_xml
Use XML file instead of FITS file for observations.
Definition ctool.hpp:162
void restore_edisp(GObservations &obs, const std::vector< bool > &edisp) const
Restore energy dispersion flags of CTA observations.
Definition ctool.cpp:1689
GSkyDir get_skydir(void)
Return sky direction from User parameters.
Definition ctool.cpp:1465
GEnergies insert_energy_boundaries(const GEnergies &energies, const GCTAObservation &obs)
Insert observation energy boundaries into list of energies.
Definition ctool.cpp:2119
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
ctool(const std::string &name, const std::string &version)
Name constructor.
Definition ctool.cpp:90
void setup_models(GObservations &obs, const std::string &name="")
Setup model container.
Definition ctool.cpp:545
GEbounds create_ebounds(void)
Create energy boundaries from user parameters.
Definition ctool.cpp:612
void free_members(void)
Delete class members.
Definition ctool.cpp:357
size_t get_current_rss(void)
Get current resident set size (physical memory use) in Bytes.
Definition ctool.cpp:2035
std::string get_gtiname(const std::string &filename, const std::string &evtname) const
Get Good Time Intervals extension name.
Definition ctool.cpp:2352
GSkyMap create_map(const GObservations &obs)
Create a skymap from user parameters.
Definition ctool.cpp:937
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
ctool & operator=(const ctool &app)
Assignment operator.
Definition ctool.cpp:221
std::vector< bool > set_edisp(GObservations &obs, const bool &edisp) const
Set energy dispersion to CTA observations.
Definition ctool.cpp:1649
bool is_stacked(void)
Query user parameters for stacked analysis.
Definition ctool.cpp:1531
bool m_read_ahead
Read ahead output parameters.
Definition ctool.hpp:155
GObservations get_observations(const bool &get_response=true)
Get observation container.
Definition ctool.cpp:1885
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition ctool.cpp:1208
void copy_members(const ctool &app)
Copy class members.
Definition ctool.cpp:343
void sync_pfiles(void)
Synchronise parameter files.
Definition ctool.cpp:370
GCTAObservation create_cta_obs(void)
Create a CTA observation from User parameters.
Definition ctool.cpp:1056
void init_members(void)
Initialise class members.
Definition ctool.cpp:321
virtual ~ctool(void)
Destructor.
Definition ctool.cpp:197
GSkyDir get_mean_pointing(const GObservations &obs)
Derives mean pointing from CTA observations.
Definition ctool.cpp:1979
virtual void execute(void)
Execute ctool.
Definition ctool.cpp:285
void require_inobs(const std::string &method)
Throws exception if inobs parameter is not valid.
Definition ctool.cpp:1119
virtual void save(void)=0
GCTAPointing get_pointing(void)
Return CTA pointing from User parameters.
Definition ctool.cpp:1445
GCTAEventCube create_cube(const GObservations &obs)
Create a CTA event cube from user parameters.
Definition ctool.cpp:1006
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::vector< bool > cube_layer_usage(const GEbounds &cube_ebounds, const GEbounds &list_ebounds) const
Determine the counts cube layer usage.
Definition ctool.cpp:2194
bool is_onoff(void)
Query user parameters for On/Off analysis.
Definition ctool.cpp:1571
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
GEbounds get_ebounds(void)
Return energy boundaries from User parameters.
Definition ctool.cpp:1378
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
void provide_help(void) const
Dump help text in the console.
Definition ctool.cpp:2467
void log_models(const GChatter &chatter, const GModels &models, const std::string &what="Model")
Log model container.
Definition ctool.cpp:1285
#define G_SETUP_OBSERVATION
Definition ctool.cpp:53
#define G_CREATE_EBOUNDS
Definition ctool.cpp:56
#define G_GET_MEAN_POINTING
Definition ctool.cpp:55
#define G_CREATE_ENERGIES
Definition ctool.cpp:57
#define G_SET_OBS_RESPONSE
Definition ctool.cpp:60
#define G_PROVIDE_HELP
Definition ctool.cpp:61
#define G_RESTORE_EDISP
Definition ctool.cpp:58
#define G_SETUP_MODELS
Definition ctool.cpp:54
ctool base class implementation