GammaLib  2.1.0.dev
 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-2023 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  * @param[in] unit Value unit string
889  *
890  * Writes a parameter value into the log file if chattiness is at least
891  * @p chatter.
892  ***************************************************************************/
893 void GApplication::log_value(const GChatter& chatter,
894  const std::string& name,
895  const std::string& value,
896  const std::string& unit)
897 {
898  // Get chattiness of application
899  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
900 
901  // Only write message if chattiness is at least equal to the minimum
902  // required chattiness
903  if (chattiness >= chatter) {
904  log << gammalib::parformat(name);
905  log << value;
906  if (!unit.empty()) {
907  log << " " << unit;
908  }
909  log << std::endl;
910  }
911 
912  // Return
913  return;
914 }
915 
916 
917 /***********************************************************************//**
918  * @brief Write parameter value in log file
919  *
920  * @param[in] chatter Minimum required chattiness
921  * @param[in] name Parameter name string
922  * @param[in] value Integer value
923  * @param[in] unit Value unit string
924  *
925  * Writes a parameter value into the log file if chattiness is at least
926  * @p chatter.
927  ***************************************************************************/
928 void GApplication::log_value(const GChatter& chatter,
929  const std::string& name,
930  const int& value,
931  const std::string& unit)
932 {
933  // Get chattiness of application
934  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
935 
936  // Only write message if chattiness is at least equal to the minimum
937  // required chattiness
938  if (chattiness >= chatter) {
939  log << gammalib::parformat(name);
940  log << value;
941  if (!unit.empty()) {
942  log << " " << unit;
943  }
944  log << std::endl;
945  }
946 
947  // Return
948  return;
949 }
950 
951 
952 /***********************************************************************//**
953  * @brief Write parameter value in log file
954  *
955  * @param[in] chatter Minimum required chattiness
956  * @param[in] name Parameter name string
957  * @param[in] value Floating point value
958  * @param[in] unit Value unit string
959  *
960  * Writes a parameter value into the log file if chattiness is at least
961  * @p chatter.
962  ***************************************************************************/
963 void GApplication::log_value(const GChatter& chatter,
964  const std::string& name,
965  const double& value,
966  const std::string& unit)
967 {
968  // Get chattiness of application
969  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
970 
971  // Only write message if chattiness is at least equal to the minimum
972  // required chattiness
973  if (chattiness >= chatter) {
974  log << gammalib::parformat(name);
975  log << value;
976  if (!unit.empty()) {
977  log << " " << unit;
978  }
979  log << std::endl;
980  }
981 
982  // Return
983  return;
984 }
985 
986 
987 /***********************************************************************//**
988  * @brief Write header 1 in log file
989  *
990  * @param[in] chatter Minimum required chattiness
991  * @param[in] header Header string
992  *
993  * Writes a header of level 1 into the log file if chattiness is at least
994  * @p chatter.
995  ***************************************************************************/
997  const std::string& header)
998 {
999  // Get chattiness of application
1000  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1001 
1002  // Only write message if chattiness is at least equal to the minimum
1003  // required chattiness
1004  if (chattiness >= chatter) {
1005  log << std::endl;
1006  log.header1(header);
1007  }
1008 
1009  // Return
1010  return;
1011 }
1012 
1013 
1014 /***********************************************************************//**
1015  * @brief Write header 2 in log file
1016  *
1017  * @param[in] chatter Minimum required chattiness
1018  * @param[in] header Header string
1019  *
1020  * Writes a header of level 2 into the log file if chattiness is at least
1021  * @p chatter.
1022  ***************************************************************************/
1024  const std::string& header)
1025 {
1026  // Get chattiness of application
1027  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1028 
1029  // Only write message if chattiness is at least equal to the minimum
1030  // required chattiness
1031  if (chattiness >= chatter) {
1032  log.header2(header);
1033  }
1034 
1035  // Return
1036  return;
1037 }
1038 
1039 
1040 /***********************************************************************//**
1041  * @brief Write header 3 in log file
1042  *
1043  * @param[in] chatter Minimum required chattiness
1044  * @param[in] header Header string
1045  *
1046  * Writes a header of level 3 into the log file if chattiness is at least
1047  * @p chatter.
1048  ***************************************************************************/
1050  const std::string& header)
1051 {
1052  // Get chattiness of application
1053  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1054 
1055  // Only write message if chattiness is at least equal to the minimum
1056  // required chattiness
1057  if (chattiness >= chatter) {
1058  log.header3(header);
1059  }
1060 
1061  // Return
1062  return;
1063 }
1064 
1065 
1066 /***********************************************************************//**
1067  * @brief Write application parameters in log file
1068  *
1069  * @param[in] chatter Minimum required chattiness
1070  *
1071  * Writes all application parameters in the log file. For parameters that
1072  * have not yet been queried the method does not write the current value
1073  * but signals [not queried].
1074  ***************************************************************************/
1076 {
1077  // Get chattiness of application
1078  GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1079 
1080  // Only write parameters if the chattiness is at least equal to the
1081  // minimum required chattiness
1082  if (chattiness >= chatter) {
1083 
1084  // Write header
1085  log.header1("Parameters");
1086 
1087  // Write parameters in logger
1088  for (int i = 0; i < m_pars.size(); ++i) {
1089 
1090  // Set parameter name
1091  std::string name = " " + m_pars.m_pars[i].name() + " ";
1092  name = name + gammalib::fill(".", 28-name.length()) + ": ";
1093 
1094  // Write parameter
1095  if (m_pars.m_pars[i].is_query() &&
1096  !m_pars.m_pars[i].was_queried()) {
1097  log << name << "[not queried]" << std::endl;
1098  }
1099  else {
1100  log << name << m_pars.m_pars[i].m_value << std::endl;
1101  }
1102 
1103  } // endfor: looped over all parameters
1104 
1105  } // endif: chattiness satisfied minimum required level
1106 
1107  // Return
1108  return;
1109 }
1110 
1111 
1112 /***********************************************************************//**
1113  * @brief Stamp FITS header with provenance information
1114  *
1115  * @param[in] hdu FITS header.
1116  *
1117  * Write provenance information into a FITS header.
1118  ***************************************************************************/
1120 {
1121  // Set provenance information
1122  std::string creator = name() + " v" + version();
1123 
1124  // Write provenance information
1125  hdu.card("CREATOR", creator, "Program which created the file");
1126 
1127  // Return
1128  return;
1129 }
1130 
1131 
1132 /***********************************************************************//**
1133  * @brief Stamp all headers in FITS object with provenance information
1134  *
1135  * @param[in] fits FITS object.
1136  *
1137  * Write provenance information into all headers of FITS object.
1138  ***************************************************************************/
1139 void GApplication::stamp(GFits& fits) const
1140 {
1141  // Loop over FITS headers
1142  for (int i = 0; i < fits.size(); ++i) {
1143  stamp(*fits[i]);
1144  }
1145 
1146  // Return
1147  return;
1148 }
1149 
1150 
1151 /***********************************************************************//**
1152  * @brief Stamp all headers in FITS file with provenance information
1153  *
1154  * @param[in] filename FITS file name.
1155  *
1156  * Write provenance information into all headers of FITS file.
1157  ***************************************************************************/
1158 void GApplication::stamp(const GFilename& filename) const
1159 {
1160  // Open FITS file
1161  GFits fits(filename);
1162 
1163  // Stamp headers
1164  stamp(fits);
1165 
1166  // Save FITS file
1167  fits.save(true);
1168 
1169  // Return
1170  return;
1171 }
1172 
1173 
1174 /***********************************************************************//**
1175  * @brief Print application
1176  *
1177  * @param[in] chatter Chattiness.
1178  * @return String containing application information.
1179  ***************************************************************************/
1180 std::string GApplication::print(const GChatter& chatter) const
1181 {
1182  // Initialise result string
1183  std::string result;
1184 
1185  // Continue only if chatter is not silent
1186  if (chatter != SILENT) {
1187 
1188  // Append header
1189  result.append("=== GApplication ===");
1190 
1191  // Append application name, version and arguments
1192  result.append("\n"+gammalib::parformat("Name")+name());
1193  result.append("\n"+gammalib::parformat("Version")+version());
1194  result.append("\n"+gammalib::parformat("Running instances"));
1195  result.append(gammalib::str(running()));
1196  for (int i = 0; i < m_args.size(); ++i) {
1197  if (i == 0) {
1198  result.append("\n"+gammalib::parformat("Command")+m_args[i]);
1199  }
1200  else if (i == 1) {
1201  result.append("\n"+gammalib::parformat("Arguments")+m_args[i]);
1202  }
1203  else {
1204  result.append("\n"+gammalib::parformat(" ")+m_args[i]);
1205  }
1206  }
1207 
1208  // Append parameters
1209  for (int i = 0; i < m_pars.size(); ++i) {
1210  result.append("\n"+gammalib::parformat(m_pars.m_pars[i].name()) +
1211  m_pars.m_pars[i].m_value);
1212  }
1213 
1214  } // endif: chatter was not silent
1215 
1216  // Return result
1217  return result;
1218 }
1219 
1220 
1221 /*==========================================================================
1222  = =
1223  = Private methods =
1224  = =
1225  ==========================================================================*/
1226 
1227 /***********************************************************************//**
1228  * @brief Initialise class members
1229  ***************************************************************************/
1231 {
1232  // Initialise public members
1233  log.clear();
1234 
1235  // Initialise protected members
1236  m_name.clear();
1237  m_version.clear();
1238  m_parfile.clear();
1239  m_logfile.clear();
1240  m_args.clear();
1241  m_pars.clear();
1242  m_pars_loaded = false;
1243  m_need_help = false;
1244  m_statistics = true;
1245 
1246  // Save the execution calendar start time
1247  std::time(&m_tstart);
1248 
1249  // Save the execution start clock
1250  #ifdef _OPENMP
1251  m_cstart = omp_get_wtime();
1252  #else
1253  m_cstart = double(clock());
1254  #endif
1255 
1256  // Initialise internal CPU seconds counter
1257  m_celapse = 0.0;
1258 
1259  // Return
1260  return;
1261 }
1262 
1263 
1264 /***********************************************************************//**
1265  * @brief Copy class members
1266  *
1267  * @param[in] app Application.
1268  *
1269  * Copies all class members.
1270  ***************************************************************************/
1272 {
1273  // Copy public attributes
1274  log = app.log;
1275 
1276  // Copy protected attributes
1277  m_name = app.m_name;
1278  m_version = app.m_version;
1279  m_parfile = app.m_parfile;
1280  m_logfile = app.m_logfile;
1281  m_args = app.m_args;
1282  m_tstart = app.m_tstart;
1283  m_cstart = app.m_cstart;
1284  m_celapse = app.m_celapse;
1285  m_pars = app.m_pars;
1287  m_need_help = app.m_need_help;
1288  m_statistics = app.m_statistics;
1289 
1290  // Return
1291  return;
1292 }
1293 
1294 
1295 /***********************************************************************//**
1296  * @brief Delete class members
1297  ***************************************************************************/
1299 {
1300  // Save application parameters if they have been loaded
1301  if (m_pars_loaded) {
1303  }
1304 
1305  // Close log file
1306  logFileClose();
1307 
1308  // Write statistics
1309  write_statistics();
1310 
1311  // Return
1312  return;
1313 }
1314 
1315 
1316 /***********************************************************************//**
1317  * @brief Set statistics according to parent child status
1318  *
1319  * Makes sure that application statistics are only written for the parent
1320  * which is the first running instances. If this instance signals that an
1321  * instance is already running, this instance is a child process that was
1322  * started by another running instance.
1323  ***************************************************************************/
1325 {
1326  // Determine whether instance is a parent
1327  bool parent = (running() < 1);
1328 
1329  // Enable statistics only for parents
1330  statistics(parent);
1331 
1332  // Return
1333  return;
1334 }
1335 
1336 
1337 /***********************************************************************//**
1338  * @brief Set chattiness of logger
1339  ***************************************************************************/
1341 {
1342  // Get chattiness from application parameter
1343  int chatter = m_pars["chatter"].integer();
1344 
1345  // Make sure that chatter is within valid range
1346  if (chatter < 0) {
1347  chatter = 0;
1348  }
1349  else if (chatter > 4) {
1350  chatter = 4;
1351  }
1352 
1353  // Set logger chatter
1354  log.chatter((GChatter)chatter);
1355 
1356  // Return
1357  return;
1358 }
1359 
1360 
1361 /***********************************************************************//**
1362  * @brief Set log filename from "logfile" parameter
1363  *
1364  * If the application parameters contain a "logfile" parameter then set the
1365  * file name of the log file from this parameter.
1366  *
1367  * In case that the file name is not valid, the file name will be set to an
1368  * empty string, which will prevent opening a log file.
1369  ***************************************************************************/
1371 {
1372  // Continue only if logfile parameter exists
1373  if (m_pars.contains("logfile")) {
1374 
1375  // Get log filename from application parameter
1376  if (m_pars["logfile"].is_valid()) {
1377  m_logfile = m_pars["logfile"].filename();
1378  }
1379  else {
1380  m_logfile = "";
1381  }
1382 
1383  }
1384 
1385  // Return
1386  return;
1387 }
1388 
1389 
1390 /***********************************************************************//**
1391  * @brief Write application statistics
1392  *
1393  * Write application statistics in a global ASCII file.
1394  ***************************************************************************/
1396 {
1397  // Continue only if statistics should be written
1398  if (m_statistics) {
1399 
1400  // Get statistics ASCII file name
1401  GFilename filename = gammalib::gamma_filename("statistics.csv");
1402 
1403  // Make sure that file exists and that it contains a header line
1404  if (access(filename.url().c_str(), R_OK) != 0) {
1405 
1406  // OpenMP critical zone to write header in case that the file
1407  // does not yet exist
1408  #pragma omp critical(GApplication_write_statistics)
1409  {
1410 
1411  // Open statistics file, and in case of success, write header
1412  // line
1413  FILE* fptr = fopen(filename.url().c_str(), "w");
1414  if (fptr != NULL) {
1415  fprintf(fptr, "Date,"
1416  "GammaLib version,"
1417  "Country,"
1418  "Application,"
1419  "Version,"
1420  "Wall clock seconds,"
1421  "CPU seconds,"
1422  "g eCO2\n");
1423  fclose(fptr);
1424  }
1425 
1426  } // end of OMP critial zone
1427 
1428  } // endif: no statistics file existed
1429 
1430  // OpenMP critical zone to write statitics
1431  #pragma omp critical(GApplication_write_statistics)
1432  {
1433 
1434  // Get file lock. Continue only in case of success
1435  struct flock lock;
1436  lock.l_type = F_WRLCK; // Want a write lock
1437  lock.l_whence = SEEK_SET; // Want beginning of file
1438  lock.l_start = 0; // No offset, lock entire file ...
1439  lock.l_len = 0; // ... to the end
1440  lock.l_pid = getpid(); // Current process ID
1441  int fd = open(filename.url().c_str(), O_WRONLY);
1442  if (fd != -1) {
1443 
1444  // Lock file
1445  fcntl(fd, F_SETLKW, &lock);
1446 
1447  // Open statistics file, and in case of success, write
1448  // statistics
1449  FILE* fptr = fopen(filename.url().c_str(), "a");
1450  if (fptr != NULL) {
1451 
1452  // Get host country
1453  std::string country = gammalib::host_country();
1454 
1455  // Write statistics string
1456  fprintf(fptr, "%s,%s,%s,%s,%s,%e,%e,%e\n",
1457  gammalib::strdate().c_str(), VERSION,
1458  country.c_str(),
1459  name().c_str(), version().c_str(),
1460  telapse(), celapse(), gCO2e(country));
1461 
1462  // Close file
1463  fclose(fptr);
1464  }
1465 
1466  // Unlock file
1467  lock.l_type = F_UNLCK;
1468  fcntl(fd, F_SETLK, &lock);
1469 
1470  // Close file
1471  close(fd);
1472 
1473  } // endif: file locking successful
1474 
1475  } // end of OMP critial zone
1476 
1477  } // endif: statistics should be written
1478 
1479  // Return
1480  return;
1481 }
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.
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.
void log_value(const GChatter &chatter, const std::string &name, const std::string &value, const std::string &unit="")
Write parameter value in log file.
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.