GammaLib 2.0.0
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-2022 by Juergen Knoedlseder *
5 * ----------------------------------------------------------------------- *
6 * *
7 * This program is free software: you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation, either version 3 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19 * *
20 ***************************************************************************/
21/**
22 * @file GApplication.cpp
23 * @brief GammaLib application base class
24 * @author Juergen Knoedlseder
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include <fcntl.h> // for file locking
32#include <unistd.h> // access() function
33#include <cstdlib> // exit() function
34#include <cstdio> // std::fopen(), etc. functions
35#include <signal.h> // signal() function
36#include "GApplication.hpp"
37#include "GTools.hpp"
38#include "GFits.hpp"
39#include "GFitsHDU.hpp"
40#include "GFilename.hpp"
41#include "GDaemon.hpp"
42
43/* __ OpenMP section _____________________________________________________ */
44#ifdef _OPENMP
45#include <omp.h>
46#endif
47
48/* __ Constants __________________________________________________________ */
49
50/* __ Globals ____________________________________________________________ */
51
52/* __ Method name definitions ____________________________________________ */
53
54/* __ Macros _____________________________________________________________ */
55
56/* __ Coding definitions _________________________________________________ */
57
58/* __ Debug definitions __________________________________________________ */
59
60
61/*==========================================================================
62 = =
63 = Constructors/destructors =
64 = =
65 ==========================================================================*/
66
67/***********************************************************************//**
68 * @brief Void constructor
69 *
70 * Constructs an empty application.
71 ***************************************************************************/
73{
74 // Initialise members
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 *
889 * Writes a parameter value into the log file if chattiness is at least
890 * @p chatter.
891 ***************************************************************************/
893 const std::string& name,
894 const std::string& value)
895{
896 // Get chattiness of application
897 GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
898
899 // Only write message if chattiness is at least equal to the minimum
900 // required chattiness
901 if (chattiness >= chatter) {
903 log << value << std::endl;
904 }
905
906 // Return
907 return;
908}
909
910
911/***********************************************************************//**
912 * @brief Write parameter value in log file
913 *
914 * @param[in] chatter Minimum required chattiness
915 * @param[in] name Parameter name string
916 * @param[in] value Integer value
917 *
918 * Writes a parameter value into the log file if chattiness is at least
919 * @p chatter.
920 ***************************************************************************/
922 const std::string& name,
923 const int& value)
924{
925 // Get chattiness of application
926 GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
927
928 // Only write message if chattiness is at least equal to the minimum
929 // required chattiness
930 if (chattiness >= chatter) {
932 log << value << std::endl;
933 }
934
935 // Return
936 return;
937}
938
939
940/***********************************************************************//**
941 * @brief Write parameter value in log file
942 *
943 * @param[in] chatter Minimum required chattiness
944 * @param[in] name Parameter name string
945 * @param[in] value Floating point value
946 *
947 * Writes a parameter value into the log file if chattiness is at least
948 * @p chatter.
949 ***************************************************************************/
951 const std::string& name,
952 const double& value)
953{
954 // Get chattiness of application
955 GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
956
957 // Only write message if chattiness is at least equal to the minimum
958 // required chattiness
959 if (chattiness >= chatter) {
961 log << value << std::endl;
962 }
963
964 // Return
965 return;
966}
967
968
969/***********************************************************************//**
970 * @brief Write header 1 in log file
971 *
972 * @param[in] chatter Minimum required chattiness
973 * @param[in] header Header string
974 *
975 * Writes a header of level 1 into the log file if chattiness is at least
976 * @p chatter.
977 ***************************************************************************/
979 const std::string& header)
980{
981 // Get chattiness of application
982 GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
983
984 // Only write message if chattiness is at least equal to the minimum
985 // required chattiness
986 if (chattiness >= chatter) {
987 log << std::endl;
988 log.header1(header);
989 }
990
991 // Return
992 return;
993}
994
995
996/***********************************************************************//**
997 * @brief Write header 2 in log file
998 *
999 * @param[in] chatter Minimum required chattiness
1000 * @param[in] header Header string
1001 *
1002 * Writes a header of level 2 into the log file if chattiness is at least
1003 * @p chatter.
1004 ***************************************************************************/
1006 const std::string& header)
1007{
1008 // Get chattiness of application
1009 GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1010
1011 // Only write message if chattiness is at least equal to the minimum
1012 // required chattiness
1013 if (chattiness >= chatter) {
1014 log.header2(header);
1015 }
1016
1017 // Return
1018 return;
1019}
1020
1021
1022/***********************************************************************//**
1023 * @brief Write header 3 in log file
1024 *
1025 * @param[in] chatter Minimum required chattiness
1026 * @param[in] header Header string
1027 *
1028 * Writes a header of level 3 into the log file if chattiness is at least
1029 * @p chatter.
1030 ***************************************************************************/
1032 const std::string& header)
1033{
1034 // Get chattiness of application
1035 GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1036
1037 // Only write message if chattiness is at least equal to the minimum
1038 // required chattiness
1039 if (chattiness >= chatter) {
1040 log.header3(header);
1041 }
1042
1043 // Return
1044 return;
1045}
1046
1047
1048/***********************************************************************//**
1049 * @brief Write application parameters in log file
1050 *
1051 * @param[in] chatter Minimum required chattiness
1052 *
1053 * Writes all application parameters in the log file. For parameters that
1054 * have not yet been queried the method does not write the current value
1055 * but signals [not queried].
1056 ***************************************************************************/
1058{
1059 // Get chattiness of application
1060 GChatter chattiness = static_cast<GChatter>((&m_pars["chatter"])->integer());
1061
1062 // Only write parameters if the chattiness is at least equal to the
1063 // minimum required chattiness
1064 if (chattiness >= chatter) {
1065
1066 // Write header
1067 log.header1("Parameters");
1068
1069 // Write parameters in logger
1070 for (int i = 0; i < m_pars.size(); ++i) {
1071
1072 // Set parameter name
1073 std::string name = " " + m_pars.m_pars[i].name() + " ";
1074 name = name + gammalib::fill(".", 28-name.length()) + ": ";
1075
1076 // Write parameter
1077 if (m_pars.m_pars[i].is_query() &&
1078 !m_pars.m_pars[i].was_queried()) {
1079 log << name << "[not queried]" << std::endl;
1080 }
1081 else {
1082 log << name << m_pars.m_pars[i].m_value << std::endl;
1083 }
1084
1085 } // endfor: looped over all parameters
1086
1087 } // endif: chattiness satisfied minimum required level
1088
1089 // Return
1090 return;
1091}
1092
1093
1094/***********************************************************************//**
1095 * @brief Stamp FITS header with provenance information
1096 *
1097 * @param[in] hdu FITS header.
1098 *
1099 * Write provenance information into a FITS header.
1100 ***************************************************************************/
1102{
1103 // Set provenance information
1104 std::string creator = name() + " v" + version();
1105
1106 // Write provenance information
1107 hdu.card("CREATOR", creator, "Program which created the file");
1108
1109 // Return
1110 return;
1111}
1112
1113
1114/***********************************************************************//**
1115 * @brief Stamp all headers in FITS object with provenance information
1116 *
1117 * @param[in] fits FITS object.
1118 *
1119 * Write provenance information into all headers of FITS object.
1120 ***************************************************************************/
1122{
1123 // Loop over FITS headers
1124 for (int i = 0; i < fits.size(); ++i) {
1125 stamp(*fits[i]);
1126 }
1127
1128 // Return
1129 return;
1130}
1131
1132
1133/***********************************************************************//**
1134 * @brief Stamp all headers in FITS file with provenance information
1135 *
1136 * @param[in] filename FITS file name.
1137 *
1138 * Write provenance information into all headers of FITS file.
1139 ***************************************************************************/
1140void GApplication::stamp(const GFilename& filename) const
1141{
1142 // Open FITS file
1143 GFits fits(filename);
1144
1145 // Stamp headers
1146 stamp(fits);
1147
1148 // Save FITS file
1149 fits.save(true);
1150
1151 // Return
1152 return;
1153}
1154
1155
1156/***********************************************************************//**
1157 * @brief Print application
1158 *
1159 * @param[in] chatter Chattiness.
1160 * @return String containing application information.
1161 ***************************************************************************/
1162std::string GApplication::print(const GChatter& chatter) const
1163{
1164 // Initialise result string
1165 std::string result;
1166
1167 // Continue only if chatter is not silent
1168 if (chatter != SILENT) {
1169
1170 // Append header
1171 result.append("=== GApplication ===");
1172
1173 // Append application name, version and arguments
1174 result.append("\n"+gammalib::parformat("Name")+name());
1175 result.append("\n"+gammalib::parformat("Version")+version());
1176 result.append("\n"+gammalib::parformat("Running instances"));
1177 result.append(gammalib::str(running()));
1178 for (int i = 0; i < m_args.size(); ++i) {
1179 if (i == 0) {
1180 result.append("\n"+gammalib::parformat("Command")+m_args[i]);
1181 }
1182 else if (i == 1) {
1183 result.append("\n"+gammalib::parformat("Arguments")+m_args[i]);
1184 }
1185 else {
1186 result.append("\n"+gammalib::parformat(" ")+m_args[i]);
1187 }
1188 }
1189
1190 // Append parameters
1191 for (int i = 0; i < m_pars.size(); ++i) {
1192 result.append("\n"+gammalib::parformat(m_pars.m_pars[i].name()) +
1193 m_pars.m_pars[i].m_value);
1194 }
1195
1196 } // endif: chatter was not silent
1197
1198 // Return result
1199 return result;
1200}
1201
1202
1203/*==========================================================================
1204 = =
1205 = Private methods =
1206 = =
1207 ==========================================================================*/
1208
1209/***********************************************************************//**
1210 * @brief Initialise class members
1211 ***************************************************************************/
1213{
1214 // Initialise public members
1215 log.clear();
1216
1217 // Initialise protected members
1218 m_name.clear();
1219 m_version.clear();
1220 m_parfile.clear();
1221 m_logfile.clear();
1222 m_args.clear();
1223 m_pars.clear();
1224 m_pars_loaded = false;
1225 m_need_help = false;
1226 m_statistics = true;
1227
1228 // Save the execution calendar start time
1229 std::time(&m_tstart);
1230
1231 // Save the execution start clock
1232 #ifdef _OPENMP
1233 m_cstart = omp_get_wtime();
1234 #else
1235 m_cstart = double(clock());
1236 #endif
1237
1238 // Initialise internal CPU seconds counter
1239 m_celapse = 0.0;
1240
1241 // Return
1242 return;
1243}
1244
1245
1246/***********************************************************************//**
1247 * @brief Copy class members
1248 *
1249 * @param[in] app Application.
1250 *
1251 * Copies all class members.
1252 ***************************************************************************/
1254{
1255 // Copy public attributes
1256 log = app.log;
1257
1258 // Copy protected attributes
1259 m_name = app.m_name;
1260 m_version = app.m_version;
1261 m_parfile = app.m_parfile;
1262 m_logfile = app.m_logfile;
1263 m_args = app.m_args;
1264 m_tstart = app.m_tstart;
1265 m_cstart = app.m_cstart;
1266 m_celapse = app.m_celapse;
1267 m_pars = app.m_pars;
1271
1272 // Return
1273 return;
1274}
1275
1276
1277/***********************************************************************//**
1278 * @brief Delete class members
1279 ***************************************************************************/
1281{
1282 // Save application parameters if they have been loaded
1283 if (m_pars_loaded) {
1285 }
1286
1287 // Close log file
1288 logFileClose();
1289
1290 // Write statistics
1292
1293 // Return
1294 return;
1295}
1296
1297
1298/***********************************************************************//**
1299 * @brief Set statistics according to parent child status
1300 *
1301 * Makes sure that application statistics are only written for the parent
1302 * which is the first running instances. If this instance signals that an
1303 * instance is already running, this instance is a child process that was
1304 * started by another running instance.
1305 ***************************************************************************/
1307{
1308 // Determine whether instance is a parent
1309 bool parent = (running() < 1);
1310
1311 // Enable statistics only for parents
1312 statistics(parent);
1313
1314 // Return
1315 return;
1316}
1317
1318
1319/***********************************************************************//**
1320 * @brief Set chattiness of logger
1321 ***************************************************************************/
1323{
1324 // Get chattiness from application parameter
1325 int chatter = m_pars["chatter"].integer();
1326
1327 // Make sure that chatter is within valid range
1328 if (chatter < 0) {
1329 chatter = 0;
1330 }
1331 else if (chatter > 4) {
1332 chatter = 4;
1333 }
1334
1335 // Set logger chatter
1336 log.chatter((GChatter)chatter);
1337
1338 // Return
1339 return;
1340}
1341
1342
1343/***********************************************************************//**
1344 * @brief Set log filename from "logfile" parameter
1345 *
1346 * If the application parameters contain a "logfile" parameter then set the
1347 * file name of the log file from this parameter.
1348 *
1349 * In case that the file name is not valid, the file name will be set to an
1350 * empty string, which will prevent opening a log file.
1351 ***************************************************************************/
1353{
1354 // Continue only if logfile parameter exists
1355 if (m_pars.contains("logfile")) {
1356
1357 // Get log filename from application parameter
1358 if (m_pars["logfile"].is_valid()) {
1359 m_logfile = m_pars["logfile"].filename();
1360 }
1361 else {
1362 m_logfile = "";
1363 }
1364
1365 }
1366
1367 // Return
1368 return;
1369}
1370
1371
1372/***********************************************************************//**
1373 * @brief Write application statistics
1374 *
1375 * Write application statistics in a global ASCII file.
1376 ***************************************************************************/
1378{
1379 // Continue only if statistics should be written
1380 if (m_statistics) {
1381
1382 // Get statistics ASCII file name
1383 GFilename filename = gammalib::gamma_filename("statistics.csv");
1384
1385 // Make sure that file exists and that it contains a header line
1386 if (access(filename.url().c_str(), R_OK) != 0) {
1387
1388 // OpenMP critical zone to write header in case that the file
1389 // does not yet exist
1390 #pragma omp critical(GApplication_write_statistics)
1391 {
1392
1393 // Open statistics file, and in case of success, write header
1394 // line
1395 FILE* fptr = fopen(filename.url().c_str(), "w");
1396 if (fptr != NULL) {
1397 fprintf(fptr, "Date,"
1398 "GammaLib version,"
1399 "Country,"
1400 "Application,"
1401 "Version,"
1402 "Wall clock seconds,"
1403 "CPU seconds,"
1404 "g eCO2\n");
1405 fclose(fptr);
1406 }
1407
1408 } // end of OMP critial zone
1409
1410 } // endif: no statistics file existed
1411
1412 // OpenMP critical zone to write statitics
1413 #pragma omp critical(GApplication_write_statistics)
1414 {
1415
1416 // Get file lock. Continue only in case of success
1417 struct flock lock;
1418 lock.l_type = F_WRLCK; // Want a write lock
1419 lock.l_whence = SEEK_SET; // Want beginning of file
1420 lock.l_start = 0; // No offset, lock entire file ...
1421 lock.l_len = 0; // ... to the end
1422 lock.l_pid = getpid(); // Current process ID
1423 int fd = open(filename.url().c_str(), O_WRONLY);
1424 if (fd != -1) {
1425
1426 // Lock file
1427 fcntl(fd, F_SETLKW, &lock);
1428
1429 // Open statistics file, and in case of success, write
1430 // statistics
1431 FILE* fptr = fopen(filename.url().c_str(), "a");
1432 if (fptr != NULL) {
1433
1434 // Get host country
1435 std::string country = gammalib::host_country();
1436
1437 // Write statistics string
1438 fprintf(fptr, "%s,%s,%s,%s,%s,%e,%e,%e\n",
1439 gammalib::strdate().c_str(), VERSION,
1440 country.c_str(),
1441 name().c_str(), version().c_str(),
1442 telapse(), celapse(), gCO2e(country));
1443
1444 // Close file
1445 fclose(fptr);
1446 }
1447
1448 // Unlock file
1449 lock.l_type = F_UNLCK;
1450 fcntl(fd, F_SETLK, &lock);
1451
1452 // Close file
1453 close(fd);
1454
1455 } // endif: file locking successful
1456
1457 } // end of OMP critial zone
1458
1459 } // endif: statistics should be written
1460
1461 // Return
1462 return;
1463}
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.
void log_value(const GChatter &chatter, const std::string &name, const std::string &value)
Write parameter value in log file.
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.
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:1143
GFilename gamma_filename(const std::string &name)
Returns filename in .gamma directory.
Definition GTools.cpp:2484
std::string strdate(void)
Return current date.
Definition GTools.cpp:727
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:489
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
std::string strip_whitespace(const std::string &arg)
Strip leading and trailing whitespace from string.
Definition GTools.cpp:80
std::string host_country(const bool &force_query=false)
Return two-digit host country code.
Definition GTools.cpp:2326
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
std::string fill(const std::string &s, const int &n)
Fill string with n strings of same type.
Definition GTools.cpp:1044