ctools  2.0.0
 All Classes Namespaces Files Functions Variables Macros Pages
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  ***************************************************************************/
90 ctool::ctool(const std::string& name,
91  const std::string& version) : GApplication(name, version)
92 {
93  // Initialise members
94  init_members();
95 
96  // Synchronise parameter files
97  sync_pfiles();
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  ***************************************************************************/
116 ctool::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  ***************************************************************************/
149 ctool::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  ***************************************************************************/
257 void 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  ***************************************************************************/
285 void ctool::execute(void)
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
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  ***************************************************************************/
431 void 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  ***************************************************************************/
545 void 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  ***************************************************************************/
612 GEbounds ctool::create_ebounds(void)
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  ***************************************************************************/
776 GEnergies ctool::create_energies(void)
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  ***************************************************************************/
937 GSkyMap 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  ***************************************************************************/
1006 GCTAEventCube 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  ***************************************************************************/
1056 GCTAObservation 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  ***************************************************************************/
1119 void 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  ***************************************************************************/
1149 void 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  ***************************************************************************/
1208 void 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  ***************************************************************************/
1251 void 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  ***************************************************************************/
1285 void 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  ***************************************************************************/
1325 GCTARoi 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  ***************************************************************************/
1378 GEbounds ctool::get_ebounds(void)
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  ***************************************************************************/
1415 GGti 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  ***************************************************************************/
1445 GCTAPointing 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  ***************************************************************************/
1465 GSkyDir ctool::get_skydir(void)
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  ***************************************************************************/
1491 std::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  ***************************************************************************/
1614 void 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  ***************************************************************************/
1649 std::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  ***************************************************************************/
1689 void 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  ***************************************************************************/
1752 void 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  ***************************************************************************/
1885 GObservations 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  ***************************************************************************/
1979 GSkyDir 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  ***************************************************************************/
2090 std::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  ***************************************************************************/
2119 GEnergies 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  ***************************************************************************/
2194 std::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  ***************************************************************************/
2266 void 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  ***************************************************************************/
2352 std::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  ***************************************************************************/
2386 std::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  ***************************************************************************/
2427 std::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  ***************************************************************************/
2467 void 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 }
GCTARoi get_roi(const GCTAPointing &pnt=GCTAPointing())
Return RoI from User parameters.
Definition: ctool.cpp:1325
void sync_pfiles(void)
Synchronise parameter files.
Definition: ctool.cpp:370
void log_models(const GChatter &chatter, const GModels &models, const std::string &what="Model")
Log model container.
Definition: ctool.cpp:1285
void require_inobs(const std::string &method)
Throws exception if inobs parameter is not valid.
Definition: ctool.cpp:1119
void set_response(GObservations &obs)
Set response for all CTA observations in container.
Definition: ctool.cpp:1614
#define G_CREATE_EBOUNDS
Definition: ctool.cpp:56
GCTAObservation create_cta_obs(void)
Create a CTA observation from User parameters.
Definition: ctool.cpp:1056
#define G_GET_MEAN_POINTING
Definition: ctool.cpp:55
void setup_observations(GObservations &obs, const bool &response=true, const bool &list=true, const bool &cube=true)
Setup observation container.
Definition: ctool.cpp:431
void setup_models(GObservations &obs, const std::string &name="")
Setup model container.
Definition: ctool.cpp:545
GEnergies create_energies(void)
Create energies from user parameters.
Definition: ctool.cpp:776
GSkyDir get_mean_pointing(const GObservations &obs)
Derives mean pointing from CTA observations.
Definition: ctool.cpp:1979
virtual void process(void)=0
virtual ~ctool(void)
Destructor.
Definition: ctool.cpp:197
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
GGti get_gti(const GTimeReference &ref)
Return Good Time Intervals from User parameter.
Definition: ctool.cpp:1415
#define G_CREATE_ENERGIES
Definition: ctool.cpp:57
std::string warn_xml_suffix(const GFilename &filename) const
Set warning string if file has no .xml suffix.
Definition: ctool.cpp:2427
GSkyMap create_map(const GObservations &obs)
Create a skymap from user parameters.
Definition: ctool.cpp:937
void init_members(void)
Initialise class members.
Definition: ctool.cpp:321
void require_inobs_nocube(const std::string &method)
Throws exception if inobs parameter is a counts cube.
Definition: ctool.cpp:1149
Base class for ctools.
Definition: ctool.hpp:50
bool is_onoff(void)
Query user parameters for On/Off analysis.
Definition: ctool.cpp:1571
GSkyDir get_skydir(void)
Return sky direction from User parameters.
Definition: ctool.cpp:1465
#define G_SETUP_OBSERVATION
Definition: ctool.cpp:53
GEbounds get_ebounds(void)
Return energy boundaries from User parameters.
Definition: ctool.cpp:1378
ctool base class implementation
virtual void run(void)
Run ctool.
Definition: ctool.cpp:257
void log_parameters(const GChatter &chatter)
Log application parameters.
Definition: ctool.cpp:1208
GEbounds create_ebounds(void)
Create energy boundaries from user parameters.
Definition: ctool.cpp:612
void provide_help(void) const
Dump help text in the console.
Definition: ctool.cpp:2467
virtual void save(void)=0
virtual void execute(void)
Execute ctool.
Definition: ctool.cpp:285
GCTAPointing get_pointing(void)
Return CTA pointing from User parameters.
Definition: ctool.cpp:1445
#define G_PROVIDE_HELP
Definition: ctool.cpp:61
std::string get_obs_header(const GObservation *obs) const
Return observation header string.
Definition: ctool.cpp:2090
void free_members(void)
Delete class members.
Definition: ctool.cpp:357
bool is_stacked(void)
Query user parameters for stacked analysis.
Definition: ctool.cpp:1531
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 & operator=(const ctool &app)
Assignment operator.
Definition: ctool.cpp:221
std::string get_gtiname(const std::string &filename, const std::string &evtname) const
Get Good Time Intervals extension name.
Definition: ctool.cpp:2352
GCTAEventCube create_cube(const GObservations &obs)
Create a CTA event cube from user parameters.
Definition: ctool.cpp:1006
ctool(const std::string &name, const std::string &version)
Name constructor.
Definition: ctool.cpp:90
bool m_read_ahead
Read ahead output parameters.
Definition: ctool.hpp:155
std::string warn_too_few_energies(const GEnergies &energies) const
Set warning string if there are too few energies.
Definition: ctool.cpp:2386
#define G_RESTORE_EDISP
Definition: ctool.cpp:58
#define G_SET_OBS_RESPONSE
Definition: ctool.cpp:60
void restore_edisp(GObservations &obs, const std::vector< bool > &edisp) const
Restore energy dispersion flags of CTA observations.
Definition: ctool.cpp:1689
std::vector< bool > set_edisp(GObservations &obs, const bool &edisp) const
Set energy dispersion to CTA observations.
Definition: ctool.cpp:1649
GObservations get_observations(const bool &get_response=true)
Get observation container.
Definition: ctool.cpp:1885
bool m_use_xml
Use XML file instead of FITS file for observations.
Definition: ctool.hpp:162
std::string set_outfile_name(const std::string &filename)
Set output file name.
Definition: ctool.cpp:1491
void copy_members(const ctool &app)
Copy class members.
Definition: ctool.cpp:343
void log_observations(const GChatter &chatter, const GObservations &obs, const std::string &what="Observation")
Log observation container.
Definition: ctool.cpp:1251
void set_obs_response(GCTAObservation *obs)
Set response for CTA observation.
Definition: ctool.cpp:1752
#define G_SETUP_MODELS
Definition: ctool.cpp:54
size_t get_current_rss(void)
Get current resident set size (physical memory use) in Bytes.
Definition: ctool.cpp:2035
GEnergies insert_energy_boundaries(const GEnergies &energies, const GCTAObservation &obs)
Insert observation energy boundaries into list of energies.
Definition: ctool.cpp:2119