GammaLib 2.1.0.dev
Loading...
Searching...
No Matches
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
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 ***************************************************************************/
101GApplication::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;
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
123
124 // 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 ***************************************************************************/
149GApplication::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;
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
173
174 // 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 ***************************************************************************/
195GApplication::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;
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
241
242 } // endif: no --help option specified
243
244 // 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
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
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;
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
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 ***************************************************************************/
384double 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 ***************************************************************************/
408double 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
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 ***************************************************************************/
468double 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 ***************************************************************************/
622void 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
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
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 ***************************************************************************/
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 ***************************************************************************/
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 ***************************************************************************/
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 ***************************************************************************/
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 ***************************************************************************/
777bool 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 ***************************************************************************/
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 ***************************************************************************/
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) {
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 ***************************************************************************/
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) {
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 ***************************************************************************/
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) {
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 ***************************************************************************/
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 ***************************************************************************/
1158void 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 ***************************************************************************/
1180std::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;
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
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}
GammaLib application base class.
Daemon class definition.
Filename class interface definition.
Abstract FITS extension base class definition.
FITS file class interface definition.
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
Application parameter class.
Application parameter container class.
std::vector< GApplicationPar > m_pars
Parameters.
void load(const GFilename &filename)
Load parameters.
void save(const GFilename &filename)
Save parameters.
void clear(void)
Clear parameter container.
bool contains(const std::string &name) const
Check parameter exists.
int size(void) const
Return number of parameters in container.
GammaLib application interface definition.
void log_string(const GChatter &chatter, const std::string &string, const bool &linefeed=true)
Write string in log file.
GLog log
Application logger.
bool m_need_help
–help specified
double gCO2e(const std::string &country) const
Return application equivalent CO2 footprint (units: g CO2e)
void write_statistics(void)
Write application statistics.
void init_members(void)
Initialise class members.
std::vector< std::string > m_args
Command line arguments.
void set_log_chatter(void)
Set chattiness of logger.
GApplication(void)
Void constructor.
std::string m_version
Application version.
void log_parameters(const GChatter &chatter)
Write application parameters in log file.
bool logTerse(void) const
Signal terse logging.
const bool & statistics(void) const
Signals if statistics will be written.
std::string m_parfile
Parameter filename.
GApplicationPars m_pars
Application parameters.
static int & running()
bool clobber(void) const
Return clobber.
std::string print(const GChatter &chatter=NORMAL) const
Print application.
void copy_members(const GApplication &app)
Copy class members.
const std::string & log_filename(void) const
Returns log filename.
std::time_t m_tstart
Calendar start time of execution.
bool has_par(const std::string &name) const
Signal if specified parameter exists.
void stamp(GFitsHDU &hdu) const
Stamp FITS header with provenance information.
void set_log_filename(void)
Set log filename from "logfile" parameter.
void log_value(const GChatter &chatter, const std::string &name, const std::string &value, const std::string &unit="")
Write parameter value in log file.
bool m_pars_loaded
Application parameters loaded.
GApplication * clone(void) const
Clone application.
void logFileClose(void)
Close log file.
std::string m_logfile
Log filename.
void log_trailer(void)
Write application trailer in log file.
void clear(void)
Clear application.
void logFileOpen(const bool &clobber=true)
Open log file.
std::string m_name
Application name.
double celapse(void) const
Return application elapsed time in CPU seconds.
bool logNormal(void) const
Signal normal logging.
const std::string & version(void) const
Return application version.
const GApplicationPars & pars(void) const
Return application parameters.
void log_header2(const GChatter &chatter, const std::string &header)
Write header 2 in log file.
void set_statistics(void)
Set statistics according to parent child status.
void free_members(void)
Delete class members.
void log_header3(const GChatter &chatter, const std::string &header)
Write header 3 in log file.
void log_header1(const GChatter &chatter, const std::string &header)
Write header 1 in log file.
bool m_statistics
Enable writing of statistics.
bool logDebug(void) const
Signal debug logging.
const std::string & par_filename(void) const
Returns parameter filename.
void log_header(void)
Write application header in log file.
double telapse(void) const
Return application elapsed time in calendar seconds.
~GApplication(void)
Destructor.
const std::string & name(void) const
Return application name.
bool logExplicit(void) const
Signal explicit logging.
GApplication & operator=(const GApplication &app)
Assignment operator.
bool logVerbose(void) const
Signal verbose logging.
double m_cstart
Clock start time of execution.
double m_celapse
Internal CPU seconds counter.
Filename class.
Definition GFilename.hpp:62
bool is_fits(void) const
Checks whether file is a FITS file.
std::string url(void) const
Return Uniform Resource Locator (URL)
Abstract FITS extension base class.
Definition GFitsHDU.hpp:51
GFitsHeaderCard & card(const int &cardno)
Return header card.
Definition GFitsHDU.hpp:259
Abstract interface for FITS table column.
virtual double real(const int &row, const int &inx=0) const =0
virtual std::string string(const int &row, const int &inx=0) const =0
Abstract interface for FITS table.
const int & nrows(void) const
Return number of rows in table.
FITS file class.
Definition GFits.hpp:63
int size(void) const
Return number of HDUs in FITS file.
Definition GFits.hpp:237
void save(const bool &clobber=false)
Saves FITS file.
Definition GFits.cpp:1178
GFitsTable * table(const int &extno)
Get pointer to table HDU.
Definition GFits.cpp:482
long int written_size(void) const
Return number of characters written through this logger.
Definition GLog.hpp:160
void indent(const int &indent)
Set indentation.
Definition GLog.hpp:345
bool is_open(void) const
Signal that log file is open.
Definition GLog.hpp:172
void header3(const std::string &arg)
Write string as header enclosed by '===' into logger.
Definition GLog.hpp:243
void open(const GFilename &filename, const bool &clobber=false)
Open log file.
Definition GLog.cpp:505
void header2(const std::string &arg)
Write string as header framed by '-' characters into logger.
Definition GLog.hpp:228
void close(void)
Close log file.
Definition GLog.cpp:538
void chatter(const GChatter &chatter)
Set chattiness.
Definition GLog.hpp:366
void header1(const std::string &arg)
Write string as header framed by '=' characters into logger.
Definition GLog.hpp:213
void clear(void)
Clear object.
Definition GLog.cpp:456
void cout(const bool &flag)
Set standard output stream (cout) flag.
Definition GLog.hpp:275
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1162
GFilename gamma_filename(const std::string &name)
Returns filename in .gamma directory.
Definition GTools.cpp:2503
std::string strdate(void)
Return current date.
Definition GTools.cpp:746
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:508
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:1089
std::string strip_whitespace(const std::string &arg)
Strip leading and trailing whitespace from string.
Definition GTools.cpp:99
std::string host_country(const bool &force_query=false)
Return two-digit host country code.
Definition GTools.cpp:2345
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:1137
std::string fill(const std::string &s, const int &n)
Fill string with n strings of same type.
Definition GTools.cpp:1063