ctools 2.1.0
Loading...
Searching...
No Matches
csfootprint.py
Go to the documentation of this file.
1#! /usr/bin/env python
2# ==========================================================================
3# Carbon footprint report script
4#
5# Copyright (C) 2022-2023 Juergen Knoedlseder
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# ==========================================================================
21import os
22import pwd
23import sys
24import inspect
25import gammalib
26import ctools
27
28
29# ================= #
30# csfootprint class #
31# ================= #
32class csfootprint(ctools.cscript):
33 """
34 Carbon footprint report script
35 """
36
37 # Constructor
38 def __init__(self, *argv):
39 """
40 Constructor
41
42 Parameters
43 ----------
44 argv : list of str
45 List of IRAF command line parameter strings of the form
46 ``parameter=3``.
47 """
48 # Initialise application by calling the base class constructor
49 self._init_cscript(self.__class__.__name__, ctools.__version__, argv)
50
51 # Initialise members
52 self._statistics = {}
53
54 # Return
55 return
56
57 # State methods for pickling
58 def __getstate__(self):
59 """
60 Extend ctools.cscript __getstate__ method
61
62 Returns
63 -------
64 state : dict
65 Pickled instance
66 """
67 # Set pickled dictionary
68 state = {'base' : ctools.cscript.__getstate__(self),
69 'statistics' : self._statistics}
70
71 # Return pickled dictionary
72 return state
73
74 def __setstate__(self, state):
75 """
76 Extend ctools.cscript __setstate__ method
77
78 Parameters
79 ----------
80 state : dict
81 Pickled instance
82 """
83 # Set state
84 ctools.cscript.__setstate__(self, state['base'])
85 self._statistics = state['statistics']
86
87 # Return
88 return
89
90
91 # Private methods
92 def _get_parameters(self):
93 """
94 Get parameters from parfile
95 """
96 # Query input parameters
97 self['infile'].filename()
98 if self['tmin'].is_valid():
99 self['tmin'].time()
100 if self['tmax'].is_valid():
101 self['tmax'].time()
102
103 # Query ahead output model filename
104 if self._read_ahead():
105 if self['outfile'].is_valid():
106 self['outfile'].filename()
107
108 # Write input parameters into logger
109 self._log_parameters(gammalib.TERSE)
110
111 # Return
112 return
113
114 # Load statistics
116 """
117 Load statistics
118 """
119 # Initialise statistics
120 statistics = {}
121
122 # Get filename
123 infile = self['infile'].filename()
124
125 # Load XML file
126 xml = gammalib.GXml()
127 xml.load(infile)
128
129 # Get useful nodes
130 header = xml.element('statistics > header')
131 data = xml.element('statistics > data')
132 dates = header.element('dates')
133 countries = data.element('countries')
134 versions = data.element('versions')
135 daily = data.element('daily')
136
137 # Extract header dates information
138 statistics['creation'] = dates.element('creation').string()
139 statistics['modified'] = dates.element('modified').string()
140 statistics['start'] = dates.element('start').string()
141 statistics['stop'] = dates.element('stop').string()
142
143 # Set used time interval [start,stop]
144 if self['tmin'].is_valid():
145 start = self['tmin'].time().utc()
146 if start < statistics['start']:
147 start = statistics['start']
148 else:
149 start = statistics['start']
150 if self['tmax'].is_valid():
151 stop = self['tmax'].time().utc()
152 if stop > statistics['stop']:
153 stop = statistics['stop']
154 else:
155 stop = statistics['stop']
156
157 # Set used time interval
158 statistics['use_start'] = start
159 statistics['use_stop'] = stop
160
161 # Extract list of countries
162 statistics['countries'] = []
163 num = countries.elements()
164 for i in range(num):
165 element = countries.element(i)
166 country = element.name()
167 calls = element.attribute('calls')
168 wall = element.attribute('wall')
169 cpu = element.attribute('cpu')
170 gCO2e = element.attribute('gCO2e')
171 entry = {'country': country, 'calls': calls, 'wall': wall,
172 'cpu': cpu, 'gCO2e': gCO2e}
173 statistics['countries'].append(entry)
174
175 # Extract list of versions
176 statistics['versions'] = []
177 num = versions.elements()
178 for i in range(num):
179 element = versions.element(i)
180 version = element.name()
181 calls = element.attribute('calls')
182 wall = element.attribute('wall')
183 cpu = element.attribute('cpu')
184 gCO2e = element.attribute('gCO2e')
185 entry = {'version': version, 'calls': calls, 'wall': wall,
186 'cpu': cpu, 'gCO2e': gCO2e}
187 statistics['versions'].append(entry)
188
189 # Extract daily and tools statistics
190 statistics['daily'] = []
191 statistics['tools'] = []
192 num = daily.elements()
193 total_calls = 0
194 total_wall = 0.0
195 total_cpu = 0.0
196 total_gCO2e = 0.0
197 for i in range(num):
198
199 # Get date
200 element = daily.element(i)
201 date = element.element('value',0).string()
202
203 # Continue if date is out of range
204 if date < start[0:10] or date > stop[0:10]:
205 continue
206
207 # Collect information
208 tools = element.element('tools',0)
209 ntools = tools.elements()
210 sum_calls = 0
211 sum_wall = 0.0
212 sum_cpu = 0.0
213 sum_gCO2e = 0.0
214 for k in range(ntools):
215
216 # Extract information
217 tool = tools.element(k)
218 name = tool.name()
219 calls = int(tool.attribute('calls'))
220 wall = float(tool.attribute('wall'))
221 cpu = float(tool.attribute('cpu'))
222 gCO2e = float(tool.attribute('gCO2e'))
223
224 # Update sums
225 sum_calls += calls
226 sum_wall += wall
227 sum_cpu += cpu
228 sum_gCO2e += gCO2e
229
230 # Update tools data
231 exists = False
232 for s in statistics['tools']:
233 if name == s['name']:
234 s['calls'] += calls
235 s['wall'] += wall
236 s['cpu'] += cpu
237 s['gCO2e'] += gCO2e
238 exists = True
239 break
240 if not exists:
241 entry = {'name': name, 'calls': calls, 'wall': wall,
242 'cpu': cpu, 'gCO2e': gCO2e}
243 statistics['tools'].append(entry)
244
245 # Update totals
246 total_calls += sum_calls
247 total_wall += sum_wall
248 total_cpu += sum_cpu
249 total_gCO2e += sum_gCO2e
250
251 # Set entry
252 entry = {'date': date, 'calls': sum_calls, 'wall': sum_wall,
253 'cpu': sum_cpu, 'gCO2e': sum_gCO2e}
254 statistics['daily'].append(entry)
255
256 # Update total statistics
257 statistics['calls'] = total_calls
258 statistics['wall'] = total_wall
259 statistics['cpu'] = total_cpu
260 statistics['gCO2e'] = total_gCO2e
261
262 # Return statistics
263 return statistics
264
265 # Format time
266 def _format_time(self, seconds):
267 """
268 Format time
269
270 Parameters
271 ----------
272 seconds : float
273 Time in seconds
274 """
275 # Format according to precision
276 if seconds < 60.0:
277 format = '%.1f seconds' % (seconds)
278 elif seconds < 3600.0:
279 format = '%.1f minutes' % (seconds/60.0)
280 else:
281 format = '%.1f hours' % (seconds/3600.0)
282
283 # Return format
284 return format
285
286 # Format carbon footprint
287 def _format_footprint(self, gCO2e, latex=False):
288 """
289 Format carbon footprint
290
291 Parameters
292 ----------
293 gCO2e : float
294 Carbon footprint in gCO2e
295 latex : bool, optional
296 Use LaTeX formatting
297 """
298 # Set unit
299 if latex:
300 unit = r'CO$_2$e'
301 else:
302 unit = 'CO2e'
303
304 # Format according to precision
305 if gCO2e < 1000.0:
306 format = '%.1f g %s' % (gCO2e, unit)
307 elif gCO2e < 1.0e6:
308 format = '%.1f kg %s' % (gCO2e/1000.0, unit)
309 else:
310 format = '%.1f t %s' % (gCO2e/1.0e6, unit)
311
312 # Return format
313 return format
314
315 # Get global information
316 def _get_global_information(self, statistics, latex=False):
317 """
318 Get global information from statistics
319
320 Parameters
321 ----------
322 statistics : dict
323 Statistics dictionary
324 latex : bool, optional
325 Use LaTeX formatting
326 """
327 # Derive information
328 tstart = gammalib.GTime(statistics['use_start'])
329 tstop = gammalib.GTime(statistics['use_stop'])
330 duration = tstop - tstart
331 cpu_hours = statistics['cpu']/3600.0
332 if statistics['cpu'] != 0.0:
333 ci_cpu = statistics['gCO2e']/cpu_hours
334 else:
335 ci_cpu = 0.0
336 if duration != 0.0:
337 fp_dur = statistics['gCO2e']/(duration * gammalib.sec2day)
338 else:
339 fp_dur = 0.0
340 fp_yr = fp_dur * 365.25
341 dur_str = self._format_time(duration)
342 if statistics['wall'] != 0.0:
343 load_str = '%.1f %%' % (statistics['cpu']/statistics['wall']*100.0)
344 else:
345 load_str = 'undefined'
346 ci_cpu_str = self._format_footprint(ci_cpu, latex) + ' / CPU hour'
347 fp_dur_str = self._format_footprint(fp_dur, latex) + ' / day'
348 fp_yr_str = self._format_footprint(fp_yr, latex) + ' / year'
349 wall_str = self._format_time(statistics['wall'])
350 cpu_str = self._format_time(statistics['cpu'])
351 gCO2e_str = self._format_footprint(statistics['gCO2e'], latex)
352 date_str = '%s - %s' % (statistics['start'], statistics['stop'])
353 use_str = '%s - %s' % (statistics['use_start'], statistics['use_stop'])
354
355 # Split footprint into infrastructure and electricity use footprint
356 if statistics['cpu'] != 0.0:
357 ef_kWh = 108.0
358 ef_total = 4.68
359 ef_electricity = 2.43
360 ef_other = ef_total - ef_electricity
361 val_ef = (ci_cpu - ef_other) * ef_kWh / ef_electricity
362 gCO2e_infra = ef_other * cpu_hours
363 gCO2e_elect = ef_electricity * val_ef / ef_kWh * cpu_hours
364 else:
365 val_ef = 0.0
366 gCO2e_infra = 0.0
367 gCO2e_elect = 0.0
368 gCO2e_kWh_str = self._format_footprint(val_ef, latex) + ' / kWh'
369 gCO2e_infra_str = self._format_footprint(gCO2e_infra, latex)
370 gCO2e_elect_str = self._format_footprint(gCO2e_elect, latex)
371
372 # Build electricity footprint string
373 elect_str = '%s (%s)' % (gCO2e_elect_str, gCO2e_kWh_str)
374
375 # Build result directory
376 result = {'ci_cpu': ci_cpu_str,
377 'fp_dur': fp_dur_str,
378 'fp_yr': fp_yr_str,
379 'wall': wall_str,
380 'cpu': cpu_str,
381 'gCO2e': gCO2e_str,
382 'date': date_str,
383 'use': use_str,
384 'dur': dur_str,
385 'load': load_str,
386 'gCO2e_kWh': gCO2e_kWh_str,
387 'gCO2e_infra': gCO2e_infra_str,
388 'gCO2e_elect': gCO2e_elect_str,
389 'elect': elect_str}
390
391 # Return result
392 return result
393
394 # Log global statistics
395 def _global_statistics(self, statistics):
396 """
397 Log global statistics
398
399 Parameters
400 ----------
401 statistics : dict
402 Statistics dictionary
403 """
404 # Log header
405 self._log_header1(gammalib.TERSE, 'Global statistics')
406
407 # Derive information from statistics
408 info = self._get_global_information(statistics)
409
410 # Log report information
411 self._log_value(gammalib.NORMAL, 'Creation date', statistics['creation'])
412 self._log_value(gammalib.NORMAL, 'Last statistics update', statistics['modified'])
413 self._log_value(gammalib.NORMAL, 'Statistics date interval', info['date'])
414 self._log_value(gammalib.NORMAL, 'Used date interval', info['use'])
415 self._log_value(gammalib.NORMAL, 'Duration of used interval', info['dur'])
416 self._log_value(gammalib.NORMAL, 'Total number of ctool runs', statistics['calls'])
417 self._log_value(gammalib.NORMAL, 'Total wall clock time', info['wall'])
418 self._log_value(gammalib.NORMAL, 'Total CPU time', info['cpu'])
419 self._log_value(gammalib.NORMAL, 'Average CPU load', info['load'])
420 self._log_value(gammalib.NORMAL, 'Total carbon footprint', info['gCO2e'])
421 self._log_value(gammalib.NORMAL, ' due to power consumption', info['elect'])
422 self._log_value(gammalib.NORMAL, ' due to infrastructure', info['gCO2e_infra'])
423 self._log_value(gammalib.NORMAL, 'Average carbon intensity', info['ci_cpu'])
424 self._log_value(gammalib.NORMAL, 'Average daily footprint', info['fp_dur'])
425 self._log_value(gammalib.NORMAL, 'Estimated annual footprint', info['fp_yr'])
426
427 # Return
428 return
429
430 # Log daily statistics
431 def _daily_statistics(self, statistics):
432 """
433 Log daily statistics
434
435 Parameters
436 ----------
437 statistics : dict
438 Statistics dictionary
439 """
440 # Log header
441 self._log_header1(gammalib.TERSE, 'Daily statistics')
442
443 # Log daily carbon footprint statistics
444 self._log_header3(gammalib.NORMAL, 'Carbon footprint')
445
446 # Loop over daily entries
447 for entry in statistics['daily']:
448 self._log_value(gammalib.NORMAL, entry['date'], self._format_footprint(entry['gCO2e']))
449
450 # Log daily run statistics
451 self._log_header3(gammalib.NORMAL, 'ctools or cscript calls')
452
453 # Loop over daily entries
454 for entry in statistics['daily']:
455 self._log_value(gammalib.NORMAL, entry['date'], entry['calls'])
456
457 # Log daily carbon footprint statistics
458 self._log_header3(gammalib.NORMAL, 'Used wall clock time')
459
460 # Loop over daily entries
461 for entry in statistics['daily']:
462 self._log_value(gammalib.NORMAL, entry['date'], self._format_time(entry['wall']))
463
464 # Log daily carbon footprint statistics
465 self._log_header3(gammalib.NORMAL, 'Used CPU time')
466
467 # Loop over daily entries
468 for entry in statistics['daily']:
469 self._log_value(gammalib.NORMAL, entry['date'], self._format_time(entry['cpu']))
470
471 # Return
472 return
473
474 # Log tools statistics
475 def _tools_statistics(self, statistics):
476 """
477 Log tools statistics
478
479 Parameters
480 ----------
481 statistics : dict
482 Statistics dictionary
483 """
484 # Get Python version information
485 req_version = (2,4)
486 cur_version = sys.version_info
487
488 # Log header
489 self._log_header1(gammalib.TERSE, 'ctools and cscripts statistics')
490
491 # Log daily carbon footprint statistics
492 self._log_header3(gammalib.NORMAL, 'Carbon footprint')
493
494 # Optionally sort list
495 if cur_version > req_version:
496 sorted_entries = sorted(statistics['tools'], key=lambda d: d['gCO2e'], reverse=True)
497 else:
498 sorted_entries = statistics['tools']
499
500 # Loop over entries
501 for i, entry in enumerate(sorted_entries):
502 if i < 10:
503 level = gammalib.NORMAL
504 else:
505 level = gammalib.EXPLICIT
506 self._log_value(level, entry['name'], self._format_footprint(entry['gCO2e']))
507 if len(sorted_entries) > 9 and self['chatter'].integer() < 3:
508 self._log_string(gammalib.NORMAL, ' ... (list truncated after 10 entries) ...')
509
510 # Log daily run statistics
511 self._log_header3(gammalib.NORMAL, 'ctools or cscript calls')
512
513 # Optionally sort list
514 if cur_version > req_version:
515 sorted_entries = sorted(statistics['tools'], key=lambda d: d['calls'], reverse=True)
516 else:
517 sorted_entries = statistics['tools']
518
519 # Loop over entries
520 for i, entry in enumerate(sorted_entries):
521 if i < 10:
522 level = gammalib.NORMAL
523 else:
524 level = gammalib.EXPLICIT
525 self._log_value(level, entry['name'], entry['calls'])
526 if len(sorted_entries) > 9 and self['chatter'].integer() < 3:
527 self._log_string(gammalib.NORMAL, ' ... (list truncated after 10 entries) ...')
528
529 # Log daily carbon footprint statistics
530 self._log_header3(gammalib.NORMAL, 'Used wall clock time')
531
532 # Optionally sort list
533 if cur_version > req_version:
534 sorted_entries = sorted(statistics['tools'], key=lambda d: d['wall'], reverse=True)
535 else:
536 sorted_entries = statistics['tools']
537
538 # Loop over entries
539 for i, entry in enumerate(sorted_entries):
540 if i < 10:
541 level = gammalib.NORMAL
542 else:
543 level = gammalib.EXPLICIT
544 self._log_value(level, entry['name'], self._format_time(entry['wall']))
545 if len(sorted_entries) > 9 and self['chatter'].integer() < 3:
546 self._log_string(gammalib.NORMAL, ' ... (list truncated after 10 entries) ...')
547
548 # Log daily carbon footprint statistics
549 self._log_header3(gammalib.NORMAL, 'Used CPU time')
550
551 # Optionally sort list
552 if cur_version > req_version:
553 sorted_entries = sorted(statistics['tools'], key=lambda d: d['cpu'], reverse=True)
554 else:
555 sorted_entries = statistics['tools']
556
557 # Loop over entries
558 for i, entry in enumerate(sorted_entries):
559 if i < 10:
560 level = gammalib.NORMAL
561 else:
562 level = gammalib.EXPLICIT
563 self._log_value(level, entry['name'], self._format_time(entry['cpu']))
564 if len(sorted_entries) > 9 and self['chatter'].integer() < 3:
565 self._log_string(gammalib.NORMAL, ' ... (list truncated after 10 entries) ...')
566
567 # Return
568 return
569
570 # Create figure
571 def _create_figure(self, outfile, statistics):
572 """
573 Create figure
574
575 Parameters
576 ----------
577 outfile : str
578 Figure file name
579 statistics : dict
580 Statistics dictionary
581 """
582 # Optionally use matplotlib to create a figure
583 try:
584
585 # Import matplotlib
586 import matplotlib.pyplot as plt
587 import matplotlib.gridspec as gridspec
588
589 # Create figure
590 ysize = 7.0
591 xsize = 1.4142 * ysize
592 fig = plt.figure(figsize=(xsize, ysize))
593
594 # Create title and subtitle
595 user = pwd.getpwuid(os.getuid())[0]
596 title = 'ctools carbon footprint report for user "%s"' % (user)
597 subtitle = r'Dates: %s - %s' % \
598 (statistics['use_start'], statistics['use_stop'])
599 fig.suptitle(title, fontsize=16)
600 fig.text(0.5, 0.925, subtitle, fontsize=11, ha='center')
601
602 # Set plot margins
603 fig.subplots_adjust(left=0.08, bottom=0.07, right=0.97, top=0.88,
604 wspace=0.1, hspace=0.2)
605
606 # Divide figure
607 gs1 = gridspec.GridSpec(4,3) # (rows,cols)
608 gs1.update(hspace=0.8, wspace=0.8)
609 ax1 = fig.add_subplot(gs1[0,0:2])
610 ax2 = fig.add_subplot(gs1[1,0:2])
611 ax3 = fig.add_subplot(gs1[2,0:2])
612 ax4 = fig.add_subplot(gs1[3,0:2])
613 #
614 gs2 = gridspec.GridSpec(8,3) # (rows,cols)
615 gs2.update(hspace=0.3, bottom=0.0, top=0.95, right=0.96, wspace=0.0)
616 ax10 = fig.add_subplot(gs2[2:5,2])
617 ax11 = fig.add_subplot(gs2[5:8,2])
618
619 # Plot daily figures
620 self._plot_daily(ax1, statistics, quantity='gCO2e', title='Footprint',
621 ylabel=r'g CO$_2$e')
622 self._plot_daily(ax2, statistics, quantity='cpu', title='CPU hours',
623 ylabel='hours', yscale=1.0/3600.0)
624 self._plot_daily(ax3, statistics, quantity='wall', title='Wall clock hours',
625 ylabel='hours', yscale=1.0/3600.0)
626 self._plot_daily(ax4, statistics, quantity='calls',
627 title='ctools calls', ylabel='calls')
628
629 # Kludge to adapt pie plotting to matplotlib version
630 ver = sys.version.split()[0]
631 if ver >= '3.0.0':
632 args, _, _, _, _, _, _ = inspect.getfullargspec(plt.pie)
633 else:
634 args, _, _, _ = inspect.getargspec(plt.pie)
635
636 # Plot pie figures
637 self._plot_pie(ax10, statistics, quantity='gCO2e', title='Footprint', args=args)
638 self._plot_pie(ax11, statistics, quantity='calls', title='ctools calls', args=args)
639
640 # Derive summary information from statistics
641 info = self._get_global_information(statistics, latex=True)
642
643 # Write summary information
644 x0 = 0.63
645 y0 = 0.89
646 dx = 0.17
647 dy = 0.022
648 fontsize = 8
649 fig.text(x0, y0, 'Total carbon footprint: ', fontsize=fontsize, ha='left')
650 fig.text(x0+dx, y0, info['gCO2e'], fontsize=fontsize, ha='left')
651 y0 -= dy
652 fig.text(x0, y0, ' due to power consumption: ', fontsize=fontsize, ha='left')
653 fig.text(x0+dx, y0, info['elect'], fontsize=fontsize, ha='left')
654 y0 -= dy
655 fig.text(x0, y0, ' due to infrastructure: ', fontsize=fontsize, ha='left')
656 fig.text(x0+dx, y0, info['gCO2e_infra'], fontsize=fontsize, ha='left')
657 y0 -= dy
658 fig.text(x0, y0, 'Total CPU time: ', fontsize=fontsize, ha='left')
659 fig.text(x0+dx, y0, info['cpu'], fontsize=fontsize, ha='left')
660 y0 -= dy
661 fig.text(x0, y0, 'Average carbon intensity: ', fontsize=fontsize, ha='left')
662 fig.text(x0+dx, y0, info['ci_cpu'], fontsize=fontsize, ha='left')
663 y0 -= dy
664 fig.text(x0, y0, 'Average daily footprint: ', fontsize=fontsize, ha='left')
665 fig.text(x0+dx, y0, info['fp_dur'], fontsize=fontsize, ha='left')
666 y0 -= dy
667 fig.text(x0, y0, 'Estimated annual footprint: ', fontsize=fontsize, ha='left')
668 fig.text(x0+dx, y0, info['fp_yr'], fontsize=fontsize, ha='left')
669
670 # Optionally display figure for debugging
671 if self._logDebug():
672 plt.show()
673
674 # Save figure
675 fig.savefig(outfile, dpi=300)
676
677 # Log file creation
678 self._log_value(gammalib.NORMAL, 'Graphics file', outfile)
679
680 # Catch exceptions
681 except (ImportError, RuntimeError):
682
683 # Log file creation
684 self._log_value(gammalib.NORMAL, 'Graphics file', 'matplotlib not available')
685
686 # Return
687 return
688
689 # Plot daily figure
690 def _plot_daily(self, ax, statistics, quantity='gCO2e', title='Footprint',
691 ylabel=r'g CO$_2$e', yscale=1.0):
692 """
693 Plot daily figure
694
695 Parameters
696 ----------
697 ax : pyplot
698 Plotting frame
699 statistics : dict
700 Statistics dictionary
701 quantity : str, optional
702 Quantity to plot
703 title : str, optional
704 Plot title
705 ylabel : str, optional
706 Y axis label
707 yscale : float, optional
708 Y axis scale
709 """
710 # Import modules for handling of dates
711 from matplotlib import dates as mdates
712 from datetime import datetime as dt
713
714 # Create bar data
715 days = [dt.strptime(entry['date'], '%Y-%m-%d').date() for entry in statistics['daily']]
716 data = [entry[quantity]*yscale for entry in statistics['daily']]
717
718 # Plot bar data
719 ax.bar(days, data, 1.0, bottom=0.0, color='red')
720
721 # Set labels
722 ax.set_title(title)
723 ax.set_xlabel('Date')
724 ax.set_ylabel(ylabel)
725
726 # Return
727 return
728
729 # Plot pie figure
730 def _plot_pie(self, ax, statistics, quantity='gCO2e', title='Footprint', num=5, args=None):
731 """
732 Plot pie figure
733
734 Parameters
735 ----------
736 ax : pyplot
737 Plotting frame
738 statistics : dict
739 Statistics dictionary
740 quantity : str, optional
741 Quantity to plot
742 title : str, optional
743 Plot title
744 num : integer, optional
745 Number of pies displayed explicitly
746 arg : list or str, optional
747 List of pie method keyword arguments
748 """
749 # Get Python version information
750 req_version = (2,4)
751 cur_version = sys.version_info
752
753 # Optionally sort list
754 if cur_version > req_version:
755 sorted_entries = sorted(statistics['tools'], key=lambda d: d[quantity], reverse=True)
756 else:
757 sorted_entries = statistics['tools']
758
759 # Create pie data
760 labels = []
761 sizes = []
762 others = 0.0
763 for i, entry in enumerate(sorted_entries):
764 if i < num:
765 labels.append(entry['name'])
766 sizes.append(entry[quantity])
767 else:
768 others += entry[quantity]
769 labels.append('others')
770 sizes.append(others)
771
772 # Plot pie chart
773 if 'wedgeprops' in args:
774 wedges, _, _ = ax.pie(sizes, autopct='%1.0f%%', pctdistance=0.8,
775 startangle=90, radius=1.0,
776 wedgeprops=dict(width=0.4, edgecolor='w'))
777 else:
778 wedges, _, _ = ax.pie(sizes, autopct='%1.0f%%', pctdistance=0.8,
779 startangle=90, radius=1.0)
780 ax.axis('equal')
781 ax.legend(wedges, labels, loc='center', fontsize=8, frameon=False)
782
783 # Set labels
784 ax.set_title(title)
785
786 # Return
787 return
788
789
790 # Public methods
791 def process(self):
792 """
793 Process the script
794 """
795 # Get parameters
796 self._get_parameters()
797
798 # Log header
799 self._log_header1(gammalib.TERSE, 'Load statistics data')
800
801 # Load statistics
802 self._statistics = self._load_statistics()
803
804 # Log statistics
808
809 # Return
810 return
811
812 def save(self):
813 """
814 Save something
815 """
816 # Write header
817 self._log_header1(gammalib.TERSE, 'Save graphics')
818
819 # Continue only if filename is valid
820 if self['outfile'].is_valid():
821
822 # Get outfile
823 outfile = self['outfile'].filename()
824
825 # Create figure
826 self._create_figure(outfile.url(), self._statistics)
827
828 # ... signal that no graphics file was specified
829 else:
830 self._log_value(gammalib.NORMAL, 'Graphics file', 'not specified')
831
832 # Return
833 return
834
835
836# ======================== #
837# Main routine entry point #
838# ======================== #
839if __name__ == '__main__':
840
841 # Create instance of application
842 app = csfootprint(sys.argv)
843
844 # Execute application
845 app.execute()
_tools_statistics(self, statistics)
_daily_statistics(self, statistics)
_get_global_information(self, statistics, latex=False)
_plot_daily(self, ax, statistics, quantity='gCO2e', title='Footprint', ylabel=r 'g CO$_2$e', yscale=1.0)
_plot_pie(self, ax, statistics, quantity='gCO2e', title='Footprint', num=5, args=None)
_format_footprint(self, gCO2e, latex=False)
_create_figure(self, outfile, statistics)
_global_statistics(self, statistics)