34 Carbon footprint report script
45 List of IRAF command line parameter strings of the form
49 self._init_cscript(self.__class__.__name__, ctools.__version__, argv)
60 Extend ctools.cscript __getstate__ method
68 state = {
'base' : ctools.cscript.__getstate__(self),
76 Extend ctools.cscript __setstate__ method
84 ctools.cscript.__setstate__(self, state[
'base'])
94 Get parameters from parfile
97 self[
'infile'].filename()
98 if self[
'tmin'].is_valid():
100 if self[
'tmax'].is_valid():
104 if self._read_ahead():
105 if self[
'outfile'].is_valid():
106 self[
'outfile'].filename()
109 self._log_parameters(gammalib.TERSE)
123 infile = self[
'infile'].filename()
126 xml = gammalib.GXml()
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')
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()
144 if self[
'tmin'].is_valid():
145 start = self[
'tmin'].time().utc()
146 if start < statistics[
'start']:
147 start = statistics[
'start']
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']
155 stop = statistics[
'stop']
158 statistics[
'use_start'] = start
159 statistics[
'use_stop'] = stop
162 statistics[
'countries'] = []
163 num = countries.elements()
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)
176 statistics[
'versions'] = []
177 num = versions.elements()
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)
190 statistics[
'daily'] = []
191 statistics[
'tools'] = []
192 num = daily.elements()
200 element = daily.element(i)
201 date = element.element(
'value',0).string()
204 if date < start[0:10]
or date > stop[0:10]:
208 tools = element.element(
'tools',0)
209 ntools = tools.elements()
214 for k
in range(ntools):
217 tool = tools.element(k)
219 calls = int(tool.attribute(
'calls'))
220 wall = float(tool.attribute(
'wall'))
221 cpu = float(tool.attribute(
'cpu'))
222 gCO2e = float(tool.attribute(
'gCO2e'))
232 for s
in statistics[
'tools']:
233 if name == s[
'name']:
241 entry = {
'name': name,
'calls': calls,
'wall': wall,
242 'cpu': cpu,
'gCO2e': gCO2e}
243 statistics[
'tools'].append(entry)
246 total_calls += sum_calls
247 total_wall += sum_wall
249 total_gCO2e += sum_gCO2e
252 entry = {
'date': date,
'calls': sum_calls,
'wall': sum_wall,
253 'cpu': sum_cpu,
'gCO2e': sum_gCO2e}
254 statistics[
'daily'].append(entry)
257 statistics[
'calls'] = total_calls
258 statistics[
'wall'] = total_wall
259 statistics[
'cpu'] = total_cpu
260 statistics[
'gCO2e'] = total_gCO2e
277 format =
'%.1f seconds' % (seconds)
278 elif seconds < 3600.0:
279 format =
'%.1f minutes' % (seconds/60.0)
281 format =
'%.1f hours' % (seconds/3600.0)
289 Format carbon footprint
294 Carbon footprint in gCO2e
295 latex : bool, optional
306 format =
'%.1f g %s' % (gCO2e, unit)
308 format =
'%.1f kg %s' % (gCO2e/1000.0, unit)
310 format =
'%.1f t %s' % (gCO2e/1.0e6, unit)
318 Get global information from statistics
323 Statistics dictionary
324 latex : bool, optional
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
337 fp_dur = statistics[
'gCO2e']/(duration * gammalib.sec2day)
340 fp_yr = fp_dur * 365.25
342 if statistics[
'wall'] != 0.0:
343 load_str =
'%.1f %%' % (statistics[
'cpu']/statistics[
'wall']*100.0)
345 load_str =
'undefined'
352 date_str =
'%s - %s' % (statistics[
'start'], statistics[
'stop'])
353 use_str =
'%s - %s' % (statistics[
'use_start'], statistics[
'use_stop'])
356 if statistics[
'cpu'] != 0.0:
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
373 elect_str =
'%s (%s)' % (gCO2e_elect_str, gCO2e_kWh_str)
376 result = {
'ci_cpu': ci_cpu_str,
377 'fp_dur': fp_dur_str,
386 'gCO2e_kWh': gCO2e_kWh_str,
387 'gCO2e_infra': gCO2e_infra_str,
388 'gCO2e_elect': gCO2e_elect_str,
397 Log global statistics
402 Statistics dictionary
405 self._log_header1(gammalib.TERSE,
'Global statistics')
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,
'Expected annual footprint', info[
'fp_yr'])
438 Statistics dictionary
441 self._log_header1(gammalib.TERSE,
'Daily statistics')
444 self._log_header3(gammalib.NORMAL,
'Carbon footprint')
447 for entry
in statistics[
'daily']:
448 self._log_value(gammalib.NORMAL, entry[
'date'], self.
_format_footprint(entry[
'gCO2e']))
451 self._log_header3(gammalib.NORMAL,
'ctools or cscript calls')
454 for entry
in statistics[
'daily']:
455 self._log_value(gammalib.NORMAL, entry[
'date'], entry[
'calls'])
458 self._log_header3(gammalib.NORMAL,
'Used wall clock time')
461 for entry
in statistics[
'daily']:
462 self._log_value(gammalib.NORMAL, entry[
'date'], self.
_format_time(entry[
'wall']))
465 self._log_header3(gammalib.NORMAL,
'Used CPU time')
468 for entry
in statistics[
'daily']:
469 self._log_value(gammalib.NORMAL, entry[
'date'], self.
_format_time(entry[
'cpu']))
482 Statistics dictionary
486 cur_version = sys.version_info
489 self._log_header1(gammalib.TERSE,
'ctools and cscripts statistics')
492 self._log_header3(gammalib.NORMAL,
'Carbon footprint')
495 if cur_version > req_version:
496 sorted_entries = sorted(statistics[
'tools'], key=
lambda d: d[
'gCO2e'], reverse=
True)
498 sorted_entries = statistics[
'tools']
501 for i, entry
in enumerate(sorted_entries):
503 level = gammalib.NORMAL
505 level = gammalib.EXPLICIT
507 if len(sorted_entries) > 9
and self[
'chatter'].integer() < 3:
508 self._log_string(gammalib.NORMAL,
' ... (list truncated after 10 entries) ...')
511 self._log_header3(gammalib.NORMAL,
'ctools or cscript calls')
514 if cur_version > req_version:
515 sorted_entries = sorted(statistics[
'tools'], key=
lambda d: d[
'calls'], reverse=
True)
517 sorted_entries = statistics[
'tools']
520 for i, entry
in enumerate(sorted_entries):
522 level = gammalib.NORMAL
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) ...')
530 self._log_header3(gammalib.NORMAL,
'Used wall clock time')
533 if cur_version > req_version:
534 sorted_entries = sorted(statistics[
'tools'], key=
lambda d: d[
'wall'], reverse=
True)
536 sorted_entries = statistics[
'tools']
539 for i, entry
in enumerate(sorted_entries):
541 level = gammalib.NORMAL
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) ...')
549 self._log_header3(gammalib.NORMAL,
'Used CPU time')
552 if cur_version > req_version:
553 sorted_entries = sorted(statistics[
'tools'], key=
lambda d: d[
'cpu'], reverse=
True)
555 sorted_entries = statistics[
'tools']
558 for i, entry
in enumerate(sorted_entries):
560 level = gammalib.NORMAL
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) ...')
580 Statistics dictionary
586 import matplotlib.pyplot
as plt
587 import matplotlib.gridspec
as gridspec
591 xsize = 1.4142 * ysize
592 fig = plt.figure(figsize=(xsize, ysize))
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')
603 fig.subplots_adjust(left=0.08, bottom=0.07, right=0.97, top=0.88,
604 wspace=0.1, hspace=0.2)
607 gs1 = gridspec.GridSpec(4,3)
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])
614 gs2 = gridspec.GridSpec(8,3)
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])
620 self.
_plot_daily(ax1, statistics, quantity=
'gCO2e', title=
'Footprint',
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')
630 args, _, _, _ = inspect.getargspec(plt.pie)
633 self.
_plot_pie(ax10, statistics, quantity=
'gCO2e', title=
'Footprint', args=args)
634 self.
_plot_pie(ax11, statistics, quantity=
'calls', title=
'ctools calls', args=args)
645 fig.text(x0, y0,
'Total carbon footprint: ', fontsize=fontsize, ha=
'left')
646 fig.text(x0+dx, y0, info[
'gCO2e'], fontsize=fontsize, ha=
'left')
648 fig.text(x0, y0,
' due to power consumption: ', fontsize=fontsize, ha=
'left')
649 fig.text(x0+dx, y0, info[
'elect'], fontsize=fontsize, ha=
'left')
651 fig.text(x0, y0,
' due to infrastructure: ', fontsize=fontsize, ha=
'left')
652 fig.text(x0+dx, y0, info[
'gCO2e_infra'], fontsize=fontsize, ha=
'left')
654 fig.text(x0, y0,
'Total CPU time: ', fontsize=fontsize, ha=
'left')
655 fig.text(x0+dx, y0, info[
'cpu'], fontsize=fontsize, ha=
'left')
657 fig.text(x0, y0,
'Average carbon intensity: ', fontsize=fontsize, ha=
'left')
658 fig.text(x0+dx, y0, info[
'ci_cpu'], fontsize=fontsize, ha=
'left')
660 fig.text(x0, y0,
'Average daily footprint: ', fontsize=fontsize, ha=
'left')
661 fig.text(x0+dx, y0, info[
'fp_dur'], fontsize=fontsize, ha=
'left')
663 fig.text(x0, y0,
'Expected annual footprint: ', fontsize=fontsize, ha=
'left')
664 fig.text(x0+dx, y0, info[
'fp_yr'], fontsize=fontsize, ha=
'left')
671 fig.savefig(outfile, dpi=300)
674 self._log_value(gammalib.NORMAL,
'Graphics file', outfile)
677 except (ImportError, RuntimeError):
680 self._log_value(gammalib.NORMAL,
'Graphics file',
'matplotlib not available')
686 def _plot_daily(self, ax, statistics, quantity='gCO2e', title='Footprint',
687 ylabel=
r'g CO$_2$e', yscale=1.0):
696 Statistics dictionary
697 quantity : str, optional
699 title : str, optional
701 ylabel : str, optional
703 yscale : float, optional
707 days = [i
for i, _
in enumerate(statistics[
'daily'])]
708 data = [entry[quantity]*yscale
for entry
in statistics[
'daily']]
711 ax.bar(days, data, 1.0, bottom=0.0, color=
'red')
715 ax.set_xlabel(
'Days since %s' % statistics[
'use_start'][0:10])
716 ax.set_ylabel(ylabel)
722 def _plot_pie(self, ax, statistics, quantity='gCO2e', title='Footprint', num=5, args=None):
731 Statistics dictionary
732 quantity : str, optional
734 title : str, optional
736 num : integer, optional
737 Number of pies displayed explicitly
738 arg : list or str, optional
739 List of pie method keyword arguments
743 cur_version = sys.version_info
746 if cur_version > req_version:
747 sorted_entries = sorted(statistics[
'tools'], key=
lambda d: d[quantity], reverse=
True)
749 sorted_entries = statistics[
'tools']
755 for i, entry
in enumerate(sorted_entries):
757 labels.append(entry[
'name'])
758 sizes.append(entry[quantity])
760 others += entry[quantity]
761 labels.append(
'others')
765 if 'wedgeprops' in args:
766 wedges, _, _ = ax.pie(sizes, autopct=
'%1.0f%%', pctdistance=0.8,
767 startangle=90, radius=1.0,
768 wedgeprops=dict(width=0.4, edgecolor=
'w'))
770 wedges, _, _ = ax.pie(sizes, autopct=
'%1.0f%%', pctdistance=0.8,
771 startangle=90, radius=1.0)
773 ax.legend(wedges, labels, loc=
'center', fontsize=8, frameon=
False)
791 self._log_header1(gammalib.TERSE,
'Load statistics data')
809 self._log_header1(gammalib.TERSE,
'Save graphics')
812 if self[
'outfile'].is_valid():
815 outfile = self[
'outfile'].filename()
822 self._log_value(gammalib.NORMAL,
'Graphics file',
'not specified')
831 if __name__ ==
'__main__':