GammaLib  2.0.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GApplication.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * GApplication.cpp - GammaLib application base class *
3  * ----------------------------------------------------------------------- *
4  * copyright (C) 2010-2022 by Juergen Knoedlseder *
5  * ----------------------------------------------------------------------- *
6  * *
7  * This program is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * This program is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have received a copy of the GNU General Public License *
18  * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19  * *
20  ***************************************************************************/
21 /**
22  * @file GApplication.cpp
23  * @brief GammaLib application base class
24  * @author Juergen Knoedlseder
25  */
26 
27 /* __ Includes ___________________________________________________________ */
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31 #include <fcntl.h> // for file locking
32 #include <unistd.h> // access() function
33 #include <cstdlib> // exit() function
34 #include <cstdio> // std::fopen(), etc. functions
35 #include <signal.h> // signal() function
36 #include "GApplication.hpp"
37 #include "GTools.hpp"
38 #include "GFits.hpp"
39 #include "GFitsHDU.hpp"
40 #include "GFilename.hpp"
41 #include "GDaemon.hpp"
42 
43 /* __ OpenMP section _____________________________________________________ */
44 #ifdef _OPENMP
45 #include <omp.h>
46 #endif
47 
48 /* __ Constants __________________________________________________________ */
49 
50 /* __ Globals ____________________________________________________________ */
51 
52 /* __ Method name definitions ____________________________________________ */
53 
54 /* __ Macros _____________________________________________________________ */
55 
56 /* __ Coding definitions _________________________________________________ */
57 
58 /* __ Debug definitions __________________________________________________ */
59 
60 
61 /*==========================================================================
62  = =
63  = Constructors/destructors =
64  = =
65  ==========================================================================*/
66 
67 /***********************************************************************//**
68  * @brief Void constructor
69  *
70  * Constructs an empty application.
71  ***************************************************************************/
73 {
74  // Initialise members
75  init_members();
76 
77  // Set statistics
79 
80  // Return
81  return;
82 }
83 
84 
85 /***********************************************************************//**
86  * @brief Application constructor
87  *
88  * @param[in] name Application name.
89  * @param[in] version Application version.
90  *
91  * Constructs an application from an application @p name and @p version. The
92  * constructor will set the parameter filename to "<name>.par" and the log
93  * filename to "<name>".log. The parameters will be loaded from the parameter
94  * file.
95  *
96  * No log file will be opened. To open the log file an explicit call to the
97  * logFileOpen() method is required.
98  *
99  * This constructor should be used for Python scripts.
100  ***************************************************************************/
101 GApplication::GApplication(const std::string& name, const std::string& version)
102 {
103  // Initialise members
104  init_members();
105 
106  // Set application name and version
107  m_name = name;
108  m_version = version;
109 
110  // Set default parfile and logfile name
111  m_parfile = name+".par";
112  m_logfile = name+".log";
113 
114  // Load application parameters
116 
117  // Signal that application parameters have been loaded
118  m_pars_loaded = true;
119 
120  // Set log filename and chattiness
122  set_log_chatter();
123 
124  // Set statistics
125  set_statistics();
126 
127  // Return
128  return;
129 }
130 
131 
132 /***********************************************************************//**
133  * @brief Application constructor
134  *
135  * @param[in] name Application name.
136  * @param[in] version Application version.
137  * @param[in] pars Application parameters.
138  *
139  * Constructs an application from an application @p name and @p version. The
140  * constructor will set the parameter filename to "<name>.par" and the log
141  * filename to "<name>".log. The parameters will be loaded from the parameter
142  * file.
143  *
144  * No log file will be opened. To open the log file an explicit call to the
145  * logFileOpen() method is required.
146  *
147  * This constructor should be used for Python scripts.
148  ***************************************************************************/
149 GApplication::GApplication(const std::string& name,
150  const std::string& version,
151  const GApplicationPars& pars)
152 {
153  // Initialise members
154  init_members();
155 
156  // Set application name and version
157  m_name = name;
158  m_version = version;
159 
160  // Set default parfile and logfile name
161  m_parfile = name+".par";
162  m_logfile = name+".log";
163 
164  // Set application parameters
165  m_pars = pars;
166 
167  // Signal that application parameters have been loaded
168  m_pars_loaded = true;
169 
170  // Set log filename and chattiness
172  set_log_chatter();
173 
174  // Set statistics
175  set_statistics();
176 
177  // Return
178  return;
179 }
180 
181 
182 /***********************************************************************//**
183  * @brief Application constructor
184  *
185  * @param[in] name Application name.
186  * @param[in] version Application version.
187  * @param[in] argc Number of command line arguments
188  * @param[in] argv Command line arguments
189  *
190  * Constructs an application from an application @p name, @p version and a
191  * number @p argc of command line arguments @p argv.
192  *
193  * This constructor should be used for C++ applications.
194  ***************************************************************************/
195 GApplication::GApplication(const std::string& name,
196  const std::string& version,
197  int argc,
198  char* argv[])
199 {
200  // Initialise members
201  init_members();
202 
203  // Set application name and version
204  m_name = name;
205  m_version = version;
206 
207  // Set default parfile and logfile name
208  m_parfile = name+".par";
209  m_logfile = name+".log";
210 
211  // Put arguments in vector string
212  m_args.clear();
213  for (int i = 0; i < argc; ++i) {
214  m_args.push_back(gammalib::strip_whitespace(argv[i]));
215  }
216 
217  // Catch --help option before doing anything else. The actual action
218  // needs to be done by the client, but we skip the loading of the
219  // parameter file and the opening of the logger if the help option
220  // was specified.
221  m_need_help = false;
222  if (m_args.size() >= 2) {
223  if (m_args[1] == "--help") {
224  m_need_help = true;
225  }
226  }
227 
228  // If no --help option has been specified then proceed with loading
229  // the parameter file and opening the logger
230  if (!m_need_help) {
231 
232  // Initialise application parameters
234 
235  // Signal that application parameters have been loaded
236  m_pars_loaded = true;
237 
238  // Set log filename and chattiness
240  set_log_chatter();
241 
242  } // endif: no --help option specified
243 
244  // Set statistics
245  set_statistics();
246 
247  // Return
248  return;
249 }
250 
251 
252 /***********************************************************************//**
253  * @brief Copy constructor
254  *
255  * @param[in] app Application.
256  ***************************************************************************/
258 {
259  // Initialise members
260  init_members();
261 
262  // Copy members
263  copy_members(app);
264 
265  // Set statistics
266  set_statistics();
267 
268  // Return
269  return;
270 }
271 
272 
273 /***********************************************************************//**
274  * @brief Destructor
275  ***************************************************************************/
277 {
278  // Free members
279  free_members();
280 
281  // Return
282  return;
283 }
284 
285 
286 /*==========================================================================
287  = =
288  = Operators =
289  = =
290  ==========================================================================*/
291 
292 /***********************************************************************//**
293  * @brief Assignment operator
294  *
295  * @param[in] app Application.
296  * @return Application.
297  ***************************************************************************/
299 {
300  // Execute only if object is not identical
301  if (this != &app) {
302 
303  // Free members
304  free_members();
305 
306  // Initialise private members for clean destruction
307  init_members();
308 
309  // Copy members
310  copy_members(app);
311 
312  // Set statistics
313  set_statistics();
314 
315  } // endif: object was not identical
316 
317  // Return
318  return *this;
319 }
320 
321 
322 /*==========================================================================
323  = =
324  = Public methods =
325  = =
326  ==========================================================================*/
327 
328 /***********************************************************************//**
329  * @brief Clear application
330  ***************************************************************************/
332 {
333  // Save application name and version as we need them to reconstruct
334  // the application after freeing and initialising its members
335  std::string name = m_name;
336  std::string version = m_version;
337 
338  // Free members
339  free_members();
340 
341  // Initialise members
342  init_members();
343 
344  // Recover saved application name and version
345  m_name = name;
346  m_version = version;
347 
348  // Set default parfile and logfile name
349  m_parfile = name+".par";
350  m_logfile = name+".log";
351 
352  // Initialise application parameters
354 
355  // Signal that application parameters have been loaded
356  m_pars_loaded = true;
357 
358  // Set log filename and chattiness
360  set_log_chatter();
361 
362  // Return
363  return;
364 }
365 
366 
367 /***********************************************************************//**
368  * @brief Clone application
369  *
370  * @return Pointer to deep copy of application.
371  ***************************************************************************/
373 {
374  // Clone application
375  return new GApplication(*this);
376 }
377 
378 
379 /***********************************************************************//**
380  * @brief Return application elapsed time in calendar seconds
381  *
382  * @return Application elapsed time in calendar seconds.
383  ***************************************************************************/
384 double GApplication::telapse(void) const
385 {
386  // Get actual time
387  std::time_t acttime;
388  std::time(&acttime);
389 
390  // Compute elapsed time
391  double telapse = difftime(acttime, m_tstart);
392 
393  // Return elapsed time
394  return telapse;
395 }
396 
397 
398 /***********************************************************************//**
399  * @brief Return application elapsed time in CPU seconds
400  *
401  * @return Application elapsed time in CPU seconds.
402  *
403  * Returns the CPU seconds that were consumed by the application. The
404  * content of @p m_celapse is added to the measurement which allows tracking
405  * of additional CPU seconds from child processes that are not properly
406  * tracked otherwise.
407  ***************************************************************************/
408 double GApplication::celapse(void) const
409 {
410  // Compute elapsed CPU clock time
411  #ifdef _OPENMP
412  double celapse = omp_get_wtime() - m_cstart;
413  #else
414  double celapse = (double(clock()) - m_cstart) / (double)CLOCKS_PER_SEC;
415  #endif
416 
417  // Add CPU seconds of internal counter
418  celapse += m_celapse;
419 
420  // Return elapsed time
421  return celapse;
422 }
423 
424 
425 /***********************************************************************//**
426  * @brief Return application equivalent CO2 footprint (units: g CO2e)
427  *
428  * @param[in] country Country where computation is performed.
429  * @return Application equivalent CO2 footprint (g CO2e).
430  *
431  * The method returns the equivalent CO2 footprint in grams after the
432  * currently elpased CPU time, as returned by celapse(). The method assumes
433  * an emission factor of 4.68 g CO2e / CPU hour as estimated by Berthoud et
434  * al. (2020) (https://hal.archives-ouvertes.fr/hal-02549565v4/document),
435  * where 2.43 g CO2e / CPU hour are due to electricity use and 2.25
436  * g CO2e / CPU hour are due to fabrication and personnel. Specifically
437  * the footprint estimated includes
438  *
439  * * server fabrication
440  * * server environment
441  * * server usage (electricity)
442  * * travelling of personnel in the context of their work
443  * * travelling of personnel from home to office
444  * * personnel equipment
445  * * personnel energy
446  *
447  * The emission factors are based on an estimate done for the DAHU cluster
448  * of the UMS GRICAD in 2019. Note that the electricity footprint a carbon
449  * intensity of 108 g eCO2 / kWh was assumed.
450  *
451  * The method tries to determine the appropriate carbon intensity from the
452  * two-digit country code and then computes the applicable emission factor
453  * \f$EF\f$ in units of g CO2e / CPU hour using
454  *
455  * \f[
456  * EF = 2.25 + 2.43 \frac{CI}{108}
457  * \f]
458  *
459  * where \f$CI\f$ is the country specific carbon intensity of electricity
460  * generation, given in units of g eCO2 / kWh.
461  *
462  * The equivalent CO2 footprint is then computed by multiplying \f$EF\f$
463  * by the number of CPU hours.
464  *
465  * Country specific carbon intensities are specified by the file
466  * @p share/refdata/emission-factors.fits.
467  ***************************************************************************/
468 double GApplication::gCO2e(const std::string& country) const
469 {
470  // Initialise constants
471  const double ef_total = 4.68;
472  const double ef_electricity = 2.43;
473  const double ef_other = ef_total - ef_electricity;
474  const double ef_kWh = 108.0;
475 
476  // Initialise vectors of country codes and emission factors
477  static bool has_data = true;
478  static std::vector<std::string> code;
479  static std::vector<double> ef;
480  static std::string last_code = "";
481  static double last_ef = 0.0;
482 
483  // Initialise equivalent CO2 footprint
484  double gCO2e = 0.0;
485 
486  // If there is a country code then try to find the country specific
487  // equivalent CO2 footprint
488  if (!country.empty()) {
489 
490  // If there are data but no country codes then load the country
491  // codes and emission factors from the FITS file. This is only
492  // attempted once, and if it fails the has_data flag will be set
493  // "false"
494  if (has_data && code.empty()) {
495 
496  // Signal that we attempted loading the data
497  has_data = false;
498 
499  // Initialise filename
500  GFilename filename("$GAMMALIB/share/refdata/emission-factors.fits");
501 
502  // If file does not exist then it may not yet be installed,
503  // possibly because a unit test is performed. Therefore try to
504  // get file in the source directory.
505  if (!filename.is_fits()) {
506  filename = GFilename("$TEST_SRCDIR/refdata/emission-factors.fits");
507  }
508 
509  // If file exists then load the data
510  if (filename.is_fits()) {
511 
512  // Open FITS file
513  GFits fits(filename);
514 
515  // Get first table
516  const GFitsTable* table = fits.table(1);
517 
518  // Extract number of rows in FITS file
519  int num = table->nrows();
520 
521  // Continue if there are data
522  if (num > 0) {
523 
524  // Reserve data
525  code.reserve(num);
526  ef.reserve(num);
527 
528  // Get column pointers
529  const GFitsTableCol* ptr_code = (*table)["CODE"];
530  const GFitsTableCol* ptr_ef = (*table)["EF"];
531 
532  // Extract data
533  for (int i = 0; i < num; ++i) {
534 
535  // Get code and emission factor
536  std::string val_code = ptr_code->string(i,0);
537  double val_ef = ptr_ef->real(i,0);
538 
539  // Store code and emission factor
540  code.push_back(val_code);
541  ef.push_back(val_ef);
542 
543  // Update last code and emission factor
544  if (val_code == country) {
545  last_code = val_code;
546  last_ef = val_ef;
547  }
548 
549  } // endfor: extracted data
550 
551  } // endif: loaded data
552 
553  } // endif: FITS file existed
554 
555  } // endif: loaded emission data
556 
557  // If there are data then try to find the country dependent
558  // equivalent CO2 footprint
559  if (!code.empty()) {
560 
561  // Initialise Berthoud et al. (2020) emission factor
562  double val_ef = ef_kWh;
563 
564  // If country is identical to last code then get the last
565  // emission factor
566  if (country == last_code) {
567  val_ef = last_ef;
568  }
569 
570  // ... otherwise search emission factor in table
571  else {
572  int size = code.size();
573  for (int i = 0; i < size; ++i) {
574  if (code[i] == country) {
575  val_ef = ef[i];
576  last_code = code[i];
577  last_ef = val_ef;
578  break;
579  }
580  }
581  }
582 
583  // Get elapsed time in CPU jours
584  double cpu_hours = celapse() / 3600.0;
585 
586  // Compute effective emission factor
587  double eff_ef = ef_other + ef_electricity * val_ef / ef_kWh;
588 
589  // Convert into g eCO2
590  gCO2e = eff_ef * cpu_hours;
591 
592  } // endif: there were codes
593 
594  } // endif: country code specified
595 
596  // If equivalent CO2 footprint is not yet set then use the emission
597  // factor of Berthoud et al. (2020)
598  if (gCO2e == 0.0) {
599 
600  // Get elapsed time in CPU jours
601  double cpu_hours = celapse() / 3600.0;
602 
603  // Convert into g eCO2
604  gCO2e = ef_total * cpu_hours;
605 
606  }
607 
608  // Return equivalent CO2 footprint
609  return gCO2e;
610 }
611 
612 
613 /***********************************************************************//**
614  * @brief Open log file
615  *
616  * @param[in] clobber Overwrite (true) or append (false) to existing file
617  * (defaults to true)
618  *
619  * Opens application log file and sets the logger chattiness dependent on
620  * the chatter parameter of the application.
621  ***************************************************************************/
622 void GApplication::logFileOpen(const bool& clobber)
623 {
624  // Open logger only if filename is non-empty
625  if (!log_filename().empty()) {
626 
627  // Initialise the application logger
628  log.open(log_filename(), clobber);
629 
630  // If we're in debug mode then all output is also dumped on the
631  // screen
632  if (has_par("debug")) {
633  if (logDebug()) {
634  log.cout(true);
635  }
636  }
637 
638  // Write header into log file
639  log_header();
640 
641  } // endif: file name was not empty
642 
643  // Set logger chattiness
644  set_log_chatter();
645 
646  // Return
647  return;
648 }
649 
650 
651 /***********************************************************************//**
652  * @brief Close log file
653  *
654  * Close the log file. In case that some characters have been written
655  * through the logger a trailer will be appended to the logger before
656  * closing the log file. The trailer informs about the computation time
657  * used by the application.
658  ***************************************************************************/
660 {
661  // Continue only if log file is open and if something has been written
662  // through this logger. This avoid writing trailers for logger than
663  // have not been used.
664  if (log.is_open() && (log.written_size() > 0)) {
665 
666  // Write line feed before trailer into logger
667  log << std::endl;
668 
669  // Write trailer into logger
670  log_trailer();
671 
672  // Close log file
673  log.close();
674 
675  } // endif: log file was open
676 
677  // Return
678  return;
679 }
680 
681 
682 /***********************************************************************//**
683  * @brief Signal terse logging
684  *
685  * @return True if at least terse logging is requested.
686  *
687  * The terse level is used for the most crucial application information that
688  * should be logged in all cases (chatter >= 1).
689  ***************************************************************************/
690 bool GApplication::logTerse(void) const
691 {
692  // Get chatter level (circumvent const correctness)
693  int chatter = const_cast<GApplicationPar*>(&m_pars["chatter"])->integer();
694 
695  // Return terse logging condition
696  return (chatter > 0);
697 }
698 
699 
700 /***********************************************************************//**
701  * @brief Signal normal logging
702  *
703  * @return True if at least normal logging is requested.
704  *
705  * The normal level is used for standard application information that should
706  * be logged in normal operations (chatter >= 2).
707  ***************************************************************************/
708 bool GApplication::logNormal(void) const
709 {
710  // Get chatter level (circumvent const correctness)
711  int chatter = const_cast<GApplicationPar*>(&m_pars["chatter"])->integer();
712 
713  // Return normal logging condition
714  return (chatter > 1);
715 }
716 
717 
718 /***********************************************************************//**
719  * @brief Signal explicit logging
720  *
721  * @return True if at least explicit logging is requested.
722  *
723  * The explicit level is used for detailed application information that
724  * should be logged in detailed studies (chatter >= 3).
725  ***************************************************************************/
727 {
728  // Get chatter level (circumvent const correctness)
729  int chatter = const_cast<GApplicationPar*>(&m_pars["chatter"])->integer();
730 
731  // Return explicit logging condition
732  return (chatter > 2);
733 }
734 
735 
736 /***********************************************************************//**
737  * @brief Signal verbose logging
738  *
739  * @return True if verbose logging is requested.
740  *
741  * The verbose level is used for full application information (chatter >= 4).
742  ***************************************************************************/
743 bool GApplication::logVerbose(void) const
744 {
745  // Get chatter level (circumvent const correctness)
746  int chatter = const_cast<GApplicationPar*>(&m_pars["chatter"])->integer();
747 
748  // Return verbose logging condition
749  return (chatter > 3);
750 }
751 
752 
753 /***********************************************************************//**
754  * @brief Signal debug logging
755  *
756  * @return True if the debugging mode has been selected.
757  *
758  * The debug level is used for application debugging.
759  ***************************************************************************/
760 bool GApplication::logDebug(void) const
761 {
762  // Get debug condition (circumvent const correctness)
763  bool debug = const_cast<GApplicationPar*>(&m_pars["debug"])->boolean();
764 
765  // Return debug condition
766  return (debug);
767 }
768 
769 
770 /***********************************************************************//**
771  * @brief Return clobber
772  *
773  * @return True if the clobber parameter is true.
774  *
775  * The clobber indicates if existing files should be overwritten.
776  ***************************************************************************/
777 bool GApplication::clobber(void) const
778 {
779  // Get clobber condition (circumvent const correctness)
780  bool clobber = const_cast<GApplicationPar*>(&m_pars["clobber"])->boolean();
781 
782  // Return clobber condition
783  return (clobber);
784 }
785 
786 
787 /***********************************************************************//**
788  * @brief Write application header in log file
789  *
790  * Dump the application header into the log file. The header is composed of
791  * a fixed width block delimined by "*" characters that contains information
792  * about the application name and version.
793  ***************************************************************************/
795 {
796  // Set header width
797  const int header_width = 80;
798 
799  // Reset any indentation
800  log.indent(0);
801 
802  // Dump header
803  log << gammalib::fill("*", header_width) << std::endl;
804  log << "*" << gammalib::centre(m_name, header_width-2) << "*" << std::endl;
805  log << "* " << gammalib::fill("-", header_width-4) << " *" << std::endl;
806  log << "* Version: " << gammalib::left(m_version, header_width-12) << "*" << std::endl;
807  log << gammalib::fill("*", header_width) << std::endl;
808 
809  // Return
810  return;
811 }
812 
813 
814 /***********************************************************************//**
815  * @brief Write application trailer in log file
816  *
817  * The application trailer gives the total number of elapsed calendar and
818  * CPU seconds, as well as the carbon footprint of the application execution.
819  ***************************************************************************/
821 {
822  // Reset any indentation
823  log.indent(0);
824 
825  // Get application statistics
826  double telapse = this->telapse();
827  double celapse = this->celapse();
828  double gCO2e = this->gCO2e(gammalib::host_country());
829 
830  // Dump trailer
831  log << "Application \"" << m_name << "\" terminated after ";
832  log << telapse << " wall clock seconds, consuming ";
833  log << celapse << " seconds of CPU time and generating a carbon";
834  log << " footprint of " << gCO2e << " g eCO2.";
835  if (gCO2e >= 1000.0) {
836  log << " Please watch your carbon footprint.";
837  }
838  if (telapse < 0.1) {
839  log << " This was rather quick and economic!";
840  }
841  if (telapse > 86400.0) {
842  log << " Hope it was worth waiting ...";
843  }
844  log << std::endl;
845 
846  // Return
847  return;
848 }
849 
850 
851 /***********************************************************************//**
852  * @brief Write string in log file
853  *
854  * @param[in] chatter Minimum required chattiness
855  * @param[in] string String
856  * @param[in] linefeed Terminate string with linefeed?
857  *
858  * Writes a string into the log file if chattiness is at least @p chatter.
859  * If @p linefeed is true the string is terminated with a linefeed.
860  ***************************************************************************/
861 void GApplication::log_string(const GChatter& chatter,
862  const std::string& string,
863  const bool& linefeed)
864 {
865  // Get chattiness of application
866  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
867 
868  // Only write message if chattiness is at least equal to the minimum
869  // required chattiness
870  if (chattiness >= chatter) {
871  log << string;
872  if (linefeed) {
873  log << std::endl;
874  }
875  }
876 
877  // Return
878  return;
879 }
880 
881 
882 /***********************************************************************//**
883  * @brief Write parameter value in log file
884  *
885  * @param[in] chatter Minimum required chattiness
886  * @param[in] name Parameter name string
887  * @param[in] value Value string
888  *
889  * Writes a parameter value into the log file if chattiness is at least
890  * @p chatter.
891  ***************************************************************************/
892 void GApplication::log_value(const GChatter& chatter,
893  const std::string& name,
894  const std::string& value)
895 {
896  // Get chattiness of application
897  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
898 
899  // Only write message if chattiness is at least equal to the minimum
900  // required chattiness
901  if (chattiness >= chatter) {
902  log << gammalib::parformat(name);
903  log << value << std::endl;
904  }
905 
906  // Return
907  return;
908 }
909 
910 
911 /***********************************************************************//**
912  * @brief Write parameter value in log file
913  *
914  * @param[in] chatter Minimum required chattiness
915  * @param[in] name Parameter name string
916  * @param[in] value Integer value
917  *
918  * Writes a parameter value into the log file if chattiness is at least
919  * @p chatter.
920  ***************************************************************************/
921 void GApplication::log_value(const GChatter& chatter,
922  const std::string& name,
923  const int& value)
924 {
925  // Get chattiness of application
926  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
927 
928  // Only write message if chattiness is at least equal to the minimum
929  // required chattiness
930  if (chattiness >= chatter) {
931  log << gammalib::parformat(name);
932  log << value << std::endl;
933  }
934 
935  // Return
936  return;
937 }
938 
939 
940 /***********************************************************************//**
941  * @brief Write parameter value in log file
942  *
943  * @param[in] chatter Minimum required chattiness
944  * @param[in] name Parameter name string
945  * @param[in] value Floating point value
946  *
947  * Writes a parameter value into the log file if chattiness is at least
948  * @p chatter.
949  ***************************************************************************/
950 void GApplication::log_value(const GChatter& chatter,
951  const std::string& name,
952  const double& value)
953 {
954  // Get chattiness of application
955  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
956 
957  // Only write message if chattiness is at least equal to the minimum
958  // required chattiness
959  if (chattiness >= chatter) {
960  log << gammalib::parformat(name);
961  log << value << std::endl;
962  }
963 
964  // Return
965  return;
966 }
967 
968 
969 /***********************************************************************//**
970  * @brief Write header 1 in log file
971  *
972  * @param[in] chatter Minimum required chattiness
973  * @param[in] header Header string
974  *
975  * Writes a header of level 1 into the log file if chattiness is at least
976  * @p chatter.
977  ***************************************************************************/
979  const std::string& header)
980 {
981  // Get chattiness of application
982  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
983 
984  // Only write message if chattiness is at least equal to the minimum
985  // required chattiness
986  if (chattiness >= chatter) {
987  log << std::endl;
988  log.header1(header);
989  }
990 
991  // Return
992  return;
993 }
994 
995 
996 /***********************************************************************//**
997  * @brief Write header 2 in log file
998  *
999  * @param[in] chatter Minimum required chattiness
1000  * @param[in] header Header string
1001  *
1002  * Writes a header of level 2 into the log file if chattiness is at least
1003  * @p chatter.
1004  ***************************************************************************/
1006  const std::string& header)
1007 {
1008  // Get chattiness of application
1009  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1010 
1011  // Only write message if chattiness is at least equal to the minimum
1012  // required chattiness
1013  if (chattiness >= chatter) {
1014  log.header2(header);
1015  }
1016 
1017  // Return
1018  return;
1019 }
1020 
1021 
1022 /***********************************************************************//**
1023  * @brief Write header 3 in log file
1024  *
1025  * @param[in] chatter Minimum required chattiness
1026  * @param[in] header Header string
1027  *
1028  * Writes a header of level 3 into the log file if chattiness is at least
1029  * @p chatter.
1030  ***************************************************************************/
1032  const std::string& header)
1033 {
1034  // Get chattiness of application
1035  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1036 
1037  // Only write message if chattiness is at least equal to the minimum
1038  // required chattiness
1039  if (chattiness >= chatter) {
1040  log.header3(header);
1041  }
1042 
1043  // Return
1044  return;
1045 }
1046 
1047 
1048 /***********************************************************************//**
1049  * @brief Write application parameters in log file
1050  *
1051  * @param[in] chatter Minimum required chattiness
1052  *
1053  * Writes all application parameters in the log file. For parameters that
1054  * have not yet been queried the method does not write the current value
1055  * but signals [not queried].
1056  ***************************************************************************/
1058 {
1059  // Get chattiness of application
1060  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1061 
1062  // Only write parameters if the chattiness is at least equal to the
1063  // minimum required chattiness
1064  if (chattiness >= chatter) {
1065 
1066  // Write header
1067  log.header1("Parameters");
1068 
1069  // Write parameters in logger
1070  for (int i = 0; i < m_pars.size(); ++i) {
1071 
1072  // Set parameter name
1073  std::string name = " " + m_pars.m_pars[i].name() + " ";
1074  name = name + gammalib::fill(".", 28-name.length()) + ": ";
1075 
1076  // Write parameter
1077  if (m_pars.m_pars[i].is_query() &&
1078  !m_pars.m_pars[i].was_queried()) {
1079  log << name << "[not queried]" << std::endl;
1080  }
1081  else {
1082  log << name << m_pars.m_pars[i].m_value << std::endl;
1083  }
1084 
1085  } // endfor: looped over all parameters
1086 
1087  } // endif: chattiness satisfied minimum required level
1088 
1089  // Return
1090  return;
1091 }
1092 
1093 
1094 /***********************************************************************//**
1095  * @brief Stamp FITS header with provenance information
1096  *
1097  * @param[in] hdu FITS header.
1098  *
1099  * Write provenance information into a FITS header.
1100  ***************************************************************************/
1102 {
1103  // Set provenance information
1104  std::string creator = name() + " v" + version();
1105 
1106  // Write provenance information
1107  hdu.card("CREATOR", creator, "Program which created the file");
1108 
1109  // Return
1110  return;
1111 }
1112 
1113 
1114 /***********************************************************************//**
1115  * @brief Stamp all headers in FITS object with provenance information
1116  *
1117  * @param[in] fits FITS object.
1118  *
1119  * Write provenance information into all headers of FITS object.
1120  ***************************************************************************/
1121 void GApplication::stamp(GFits& fits) const
1122 {
1123  // Loop over FITS headers
1124  for (int i = 0; i < fits.size(); ++i) {
1125  stamp(*fits[i]);
1126  }
1127 
1128  // Return
1129  return;
1130 }
1131 
1132 
1133 /***********************************************************************//**
1134  * @brief Stamp all headers in FITS file with provenance information
1135  *
1136  * @param[in] filename FITS file name.
1137  *
1138  * Write provenance information into all headers of FITS file.
1139  ***************************************************************************/
1140 void GApplication::stamp(const GFilename& filename) const
1141 {
1142  // Open FITS file
1143  GFits fits(filename);
1144 
1145  // Stamp headers
1146  stamp(fits);
1147 
1148  // Save FITS file
1149  fits.save(true);
1150 
1151  // Return
1152  return;
1153 }
1154 
1155 
1156 /***********************************************************************//**
1157  * @brief Print application
1158  *
1159  * @param[in] chatter Chattiness.
1160  * @return String containing application information.
1161  ***************************************************************************/
1162 std::string GApplication::print(const GChatter& chatter) const
1163 {
1164  // Initialise result string
1165  std::string result;
1166 
1167  // Continue only if chatter is not silent
1168  if (chatter != SILENT) {
1169 
1170  // Append header
1171  result.append("=== GApplication ===");
1172 
1173  // Append application name, version and arguments
1174  result.append("\n"+gammalib::parformat("Name")+name());
1175  result.append("\n"+gammalib::parformat("Version")+version());
1176  result.append("\n"+gammalib::parformat("Running instances"));
1177  result.append(gammalib::str(running()));
1178  for (int i = 0; i < m_args.size(); ++i) {
1179  if (i == 0) {
1180  result.append("\n"+gammalib::parformat("Command")+m_args[i]);
1181  }
1182  else if (i == 1) {
1183  result.append("\n"+gammalib::parformat("Arguments")+m_args[i]);
1184  }
1185  else {
1186  result.append("\n"+gammalib::parformat(" ")+m_args[i]);
1187  }
1188  }
1189 
1190  // Append parameters
1191  for (int i = 0; i < m_pars.size(); ++i) {
1192  result.append("\n"+gammalib::parformat(m_pars.m_pars[i].name()) +
1193  m_pars.m_pars[i].m_value);
1194  }
1195 
1196  } // endif: chatter was not silent
1197 
1198  // Return result
1199  return result;
1200 }
1201 
1202 
1203 /*==========================================================================
1204  = =
1205  = Private methods =
1206  = =
1207  ==========================================================================*/
1208 
1209 /***********************************************************************//**
1210  * @brief Initialise class members
1211  ***************************************************************************/
1213 {
1214  // Initialise public members
1215  log.clear();
1216 
1217  // Initialise protected members
1218  m_name.clear();
1219  m_version.clear();
1220  m_parfile.clear();
1221  m_logfile.clear();
1222  m_args.clear();
1223  m_pars.clear();
1224  m_pars_loaded = false;
1225  m_need_help = false;
1226  m_statistics = true;
1227 
1228  // Save the execution calendar start time
1229  std::time(&m_tstart);
1230 
1231  // Save the execution start clock
1232  #ifdef _OPENMP
1233  m_cstart = omp_get_wtime();
1234  #else
1235  m_cstart = double(clock());
1236  #endif
1237 
1238  // Initialise internal CPU seconds counter
1239  m_celapse = 0.0;
1240 
1241  // Return
1242  return;
1243 }
1244 
1245 
1246 /***********************************************************************//**
1247  * @brief Copy class members
1248  *
1249  * @param[in] app Application.
1250  *
1251  * Copies all class members.
1252  ***************************************************************************/
1254 {
1255  // Copy public attributes
1256  log = app.log;
1257 
1258  // Copy protected attributes
1259  m_name = app.m_name;
1260  m_version = app.m_version;
1261  m_parfile = app.m_parfile;
1262  m_logfile = app.m_logfile;
1263  m_args = app.m_args;
1264  m_tstart = app.m_tstart;
1265  m_cstart = app.m_cstart;
1266  m_celapse = app.m_celapse;
1267  m_pars = app.m_pars;
1269  m_need_help = app.m_need_help;
1270  m_statistics = app.m_statistics;
1271 
1272  // Return
1273  return;
1274 }
1275 
1276 
1277 /***********************************************************************//**
1278  * @brief Delete class members
1279  ***************************************************************************/
1281 {
1282  // Save application parameters if they have been loaded
1283  if (m_pars_loaded) {
1285  }
1286 
1287  // Close log file
1288  logFileClose();
1289 
1290  // Write statistics
1291  write_statistics();
1292 
1293  // Return
1294  return;
1295 }
1296 
1297 
1298 /***********************************************************************//**
1299  * @brief Set statistics according to parent child status
1300  *
1301  * Makes sure that application statistics are only written for the parent
1302  * which is the first running instances. If this instance signals that an
1303  * instance is already running, this instance is a child process that was
1304  * started by another running instance.
1305  ***************************************************************************/
1307 {
1308  // Determine whether instance is a parent
1309  bool parent = (running() < 1);
1310 
1311  // Enable statistics only for parents
1312  statistics(parent);
1313 
1314  // Return
1315  return;
1316 }
1317 
1318 
1319 /***********************************************************************//**
1320  * @brief Set chattiness of logger
1321  ***************************************************************************/
1323 {
1324  // Get chattiness from application parameter
1325  int chatter = m_pars["chatter"].integer();
1326 
1327  // Make sure that chatter is within valid range
1328  if (chatter < 0) {
1329  chatter = 0;
1330  }
1331  else if (chatter > 4) {
1332  chatter = 4;
1333  }
1334 
1335  // Set logger chatter
1336  log.chatter((GChatter)chatter);
1337 
1338  // Return
1339  return;
1340 }
1341 
1342 
1343 /***********************************************************************//**
1344  * @brief Set log filename from "logfile" parameter
1345  *
1346  * If the application parameters contain a "logfile" parameter then set the
1347  * file name of the log file from this parameter.
1348  *
1349  * In case that the file name is not valid, the file name will be set to an
1350  * empty string, which will prevent opening a log file.
1351  ***************************************************************************/
1353 {
1354  // Continue only if logfile parameter exists
1355  if (m_pars.contains("logfile")) {
1356 
1357  // Get log filename from application parameter
1358  if (m_pars["logfile"].is_valid()) {
1359  m_logfile = m_pars["logfile"].filename();
1360  }
1361  else {
1362  m_logfile = "";
1363  }
1364 
1365  }
1366 
1367  // Return
1368  return;
1369 }
1370 
1371 
1372 /***********************************************************************//**
1373  * @brief Write application statistics
1374  *
1375  * Write application statistics in a global ASCII file.
1376  ***************************************************************************/
1378 {
1379  // Continue only if statistics should be written
1380  if (m_statistics) {
1381 
1382  // Get statistics ASCII file name
1383  GFilename filename = gammalib::gamma_filename("statistics.csv");
1384 
1385  // Make sure that file exists and that it contains a header line
1386  if (access(filename.url().c_str(), R_OK) != 0) {
1387 
1388  // OpenMP critical zone to write header in case that the file
1389  // does not yet exist
1390  #pragma omp critical(GApplication_write_statistics)
1391  {
1392 
1393  // Open statistics file, and in case of success, write header
1394  // line
1395  FILE* fptr = fopen(filename.url().c_str(), "w");
1396  if (fptr != NULL) {
1397  fprintf(fptr, "Date,"
1398  "GammaLib version,"
1399  "Country,"
1400  "Application,"
1401  "Version,"
1402  "Wall clock seconds,"
1403  "CPU seconds,"
1404  "g eCO2\n");
1405  fclose(fptr);
1406  }
1407 
1408  } // end of OMP critial zone
1409 
1410  } // endif: no statistics file existed
1411 
1412  // OpenMP critical zone to write statitics
1413  #pragma omp critical(GApplication_write_statistics)
1414  {
1415 
1416  // Get file lock. Continue only in case of success
1417  struct flock lock;
1418  lock.l_type = F_WRLCK; // Want a write lock
1419  lock.l_whence = SEEK_SET; // Want beginning of file
1420  lock.l_start = 0; // No offset, lock entire file ...
1421  lock.l_len = 0; // ... to the end
1422  lock.l_pid = getpid(); // Current process ID
1423  int fd = open(filename.url().c_str(), O_WRONLY);
1424  if (fd != -1) {
1425 
1426  // Lock file
1427  fcntl(fd, F_SETLKW, &lock);
1428 
1429  // Open statistics file, and in case of success, write
1430  // statistics
1431  FILE* fptr = fopen(filename.url().c_str(), "a");
1432  if (fptr != NULL) {
1433 
1434  // Get host country
1435  std::string country = gammalib::host_country();
1436 
1437  // Write statistics string
1438  fprintf(fptr, "%s,%s,%s,%s,%s,%e,%e,%e\n",
1439  gammalib::strdate().c_str(), VERSION,
1440  country.c_str(),
1441  name().c_str(), version().c_str(),
1442  telapse(), celapse(), gCO2e(country));
1443 
1444  // Close file
1445  fclose(fptr);
1446  }
1447 
1448  // Unlock file
1449  lock.l_type = F_UNLCK;
1450  fcntl(fd, F_SETLK, &lock);
1451 
1452  // Close file
1453  close(fd);
1454 
1455  } // endif: file locking successful
1456 
1457  } // end of OMP critial zone
1458 
1459  } // endif: statistics should be written
1460 
1461  // Return
1462  return;
1463 }
GApplication(void)
Void constructor.
bool logTerse(void) const
Signal terse logging.
void set_log_chatter(void)
Set chattiness of logger.
Application parameter container class.
void open(const GFilename &filename, const bool &clobber=false)
Open log file.
Definition: GLog.cpp:505
void header1(const std::string &arg)
Write string as header framed by &#39;=&#39; characters into logger.
Definition: GLog.hpp:213
void log_header3(const GChatter &chatter, const std::string &header)
Write header 3 in log file.
void log_value(const GChatter &chatter, const std::string &name, const std::string &value)
Write parameter value in log file.
GFitsTable * table(const int &extno)
Get pointer to table HDU.
Definition: GFits.cpp:482
void write_statistics(void)
Write application statistics.
~GApplication(void)
Destructor.
static int & running()
const std::string & name(void) const
Return application name.
void header2(const std::string &arg)
Write string as header framed by &#39;-&#39; characters into logger.
Definition: GLog.hpp:228
void set_log_filename(void)
Set log filename from &quot;logfile&quot; parameter.
void copy_members(const GApplication &app)
Copy class members.
double m_celapse
Internal CPU seconds counter.
std::time_t m_tstart
Calendar start time of execution.
const bool & statistics(void) const
Signals if statistics will be written.
bool is_open(void) const
Signal that log file is open.
Definition: GLog.hpp:172
GApplication * clone(void) const
Clone application.
Abstract FITS extension base class.
Definition: GFitsHDU.hpp:51
const std::string & log_filename(void) const
Returns log filename.
bool has_par(const std::string &name) const
Signal if specified parameter exists.
std::vector< GApplicationPar > m_pars
Parameters.
std::string strdate(void)
Return current date.
Definition: GTools.cpp:727
void logFileClose(void)
Close log file.
void chatter(const GChatter &chatter)
Set chattiness.
Definition: GLog.hpp:366
double m_cstart
Clock start time of execution.
const std::string & par_filename(void) const
Returns parameter filename.
void log_header2(const GChatter &chatter, const std::string &header)
Write header 2 in log file.
std::string m_logfile
Log filename.
void header3(const std::string &arg)
Write string as header enclosed by &#39;===&#39; into logger.
Definition: GLog.hpp:243
void log_header(void)
Write application header in log file.
Gammalib tools definition.
FITS file class.
Definition: GFits.hpp:63
void log_string(const GChatter &chatter, const std::string &string, const bool &linefeed=true)
Write string in log file.
FITS file class interface definition.
std::string strip_whitespace(const std::string &arg)
Strip leading and trailing whitespace from string.
Definition: GTools.cpp:80
double celapse(void) const
Return application elapsed time in CPU seconds.
void stamp(GFitsHDU &hdu) const
Stamp FITS header with provenance information.
void cout(const bool &flag)
Set standard output stream (cout) flag.
Definition: GLog.hpp:275
std::string centre(const std::string &s, const int &n, const char &c= ' ')
Centre string to achieve a length of n characters.
Definition: GTools.cpp:1118
void log_trailer(void)
Write application trailer in log file.
GApplication & operator=(const GApplication &app)
Assignment operator.
bool logExplicit(void) const
Signal explicit logging.
virtual std::string string(const int &row, const int &inx=0) const =0
void init_members(void)
Initialise class members.
Abstract FITS extension base class definition.
void clear(void)
Clear application.
bool logNormal(void) const
Signal normal logging.
std::vector< std::string > m_args
Command line arguments.
int size(void) const
Return number of parameters in container.
void save(const bool &clobber=false)
Saves FITS file.
Definition: GFits.cpp:1178
void log_header1(const GChatter &chatter, const std::string &header)
Write header 1 in log file.
bool clobber(void) const
Return clobber.
Daemon class definition.
Filename class.
Definition: GFilename.hpp:62
Abstract interface for FITS table column.
std::string left(const std::string &s, const int &n, const char &c= ' ')
Left justify string to achieve a length of n characters.
Definition: GTools.cpp:1070
GammaLib application base class.
double telapse(void) const
Return application elapsed time in calendar seconds.
void close(void)
Close log file.
Definition: GLog.cpp:538
void set_statistics(void)
Set statistics according to parent child status.
Abstract interface for FITS table.
Definition: GFitsTable.hpp:44
GChatter
Definition: GTypemaps.hpp:33
void free_members(void)
Delete class members.
bool m_pars_loaded
Application parameters loaded.
void save(const GFilename &filename)
Save parameters.
bool is_fits(void) const
Checks whether file is a FITS file.
Definition: GFilename.cpp:313
std::string m_version
Application version.
GFilename gamma_filename(const std::string &name)
Returns filename in .gamma directory.
Definition: GTools.cpp:2484
std::string print(const GChatter &chatter=NORMAL) const
Print application.
bool m_need_help
–help specified
const std::string & version(void) const
Return application version.
void logFileOpen(const bool &clobber=true)
Open log file.
const int & nrows(void) const
Return number of rows in table.
Definition: GFitsTable.hpp:119
void log_parameters(const GChatter &chatter)
Write application parameters in log file.
const GApplicationPars & pars(void) const
Return application parameters.
GLog log
Application logger.
GApplicationPars m_pars
Application parameters.
std::string url(void) const
Return Uniform Resource Locator (URL)
Definition: GFilename.hpp:189
std::string host_country(const bool &force_query=false)
Return two-digit host country code.
Definition: GTools.cpp:2326
virtual double real(const int &row, const int &inx=0) const =0
bool logDebug(void) const
Signal debug logging.
long int written_size(void) const
Return number of characters written through this logger.
Definition: GLog.hpp:160
int size(void) const
Return number of HDUs in FITS file.
Definition: GFits.hpp:237
Application parameter class.
bool logVerbose(void) const
Signal verbose logging.
GammaLib application interface definition.
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition: GTools.cpp:1143
std::string m_name
Application name.
bool m_statistics
Enable writing of statistics.
std::string fill(const std::string &s, const int &n)
Fill string with n strings of same type.
Definition: GTools.cpp:1044
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition: GFitsHDU.hpp:259
void indent(const int &indent)
Set indentation.
Definition: GLog.hpp:345
double gCO2e(const std::string &country) const
Return application equivalent CO2 footprint (units: g CO2e)
std::string m_parfile
Parameter filename.
Filename class interface definition.
void load(const GFilename &filename)
Load parameters.
bool contains(const std::string &name) const
Check parameter exists.
void clear(void)
Clear object.
Definition: GLog.cpp:456
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition: GTools.cpp:489
void clear(void)
Clear parameter container.