# coding=utf-8
# Copyright (C) Duncan Macleod (2014)
#
# This file is part of GWSumm
#
# GWSumm is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GWSumm is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GWSumm. If not, see <http://www.gnu.org/licenses/>
"""Definition of the `AccountingTab`
"""
import re
from collections import OrderedDict
from configparser import NoOptionError
from glue.lal import Cache
from gwpy.segments import (DataQualityDict, SegmentList)
from gwdetchar.io import html
from gwdetchar.plot import texify
from ..config import GWSummConfigParser
from .registry import (get_tab, register_tab)
from .. import globalv
from ..data import get_timeseries
from ..segments import get_segments
from ..plot.registry import get_plot
from ..utils import vprint
__author__ = 'Duncan Macleod <duncan.macleod@ligo.org>'
__all__ = ['AccountingTab']
ParentTab = get_tab('default')
[docs]
class AccountingTab(ParentTab):
"""Summarise the data recorded by the operating mode channels
"""
type = 'accounting'
[docs]
@classmethod
def from_ini(cls, config, section, plotdir='plots', **kwargs):
new = super(ParentTab, cls).from_ini(config, section, **kwargs)
# add information
new.plots = []
new.channel = config.get(section, 'channel')
new.ifo = config.get(section, 'IFO')
new.modes = OrderedDict((int(idx), name) for (idx, name) in
config.nditems(section) if idx.isdigit())
# -----------
# build plots
new.set_layout([2, 2])
new.segmenttag = '%s:%%s' % (new.channel)
tag = new.channel.split(':', 1)[-1].replace('-', '_')
groups = type(new.modes)(
(idx, name.strip('*')) for (idx, name) in new.modes.items()
if int(idx) % 10 == 0)
pstates = OrderedDict(
(idx, name) for (idx, name) in new.modes.items() if
not name.startswith('*'))
# parse colors
colors = dict(
(int(key[6:]), eval(color)) for (key, color) in
config.nditems(section) if re.match(r'color-\d+', key))
for flag in pstates:
group = int(flag // 10 * 10)
if flag not in colors:
colors[flag] = colors.get(group, None)
# plot segments
for flags, ptag in zip([groups, pstates], ['overview', 'details']):
segcolors = [colors.get(flag, None) for flag in flags]
if not any(segcolors):
segcolors = None
for state in new.states:
new.plots.append(get_plot('segments')(
[new.segmenttag % idx for idx in flags],
new.span[0], new.span[1], labels=list(flags.values()),
state=state, outdir=plotdir,
pid='%s_SEGMENTS_%s' % (tag, ptag.upper()),
active=segcolors,
known={'alpha': 0.1, 'facecolor': 'lightgray'},
title='%s operating mode %s' % (new.ifo, ptag)))
# plot pie charts
try:
explode = list(
map(float, config.get(section, 'pie-explode').split(',')))
except NoOptionError:
explode = None
ptag = 'overview'
piecolors = [colors.get(flag, None) for flag in groups]
if not any(piecolors):
piecolors = None
for state in new.states:
labels = list(groups.values())
new.plots.append(get_plot('segment-pie')(
[new.segmenttag % idx for idx in groups],
new.span[0], new.span[1], state=state, labels=labels,
outdir=plotdir, pid='%s_PIE_%s' % (tag, ptag.upper()),
colors=piecolors, explode=explode,
title='%s operating mode %s' % (new.ifo, ptag)))
shortlabels = ['\n'.join('{0:.9}.'.format(w) if len(w) > 9 else w
for w in l.split()) for
l in labels]
new.plots.append(get_plot('duty')(
[new.segmenttag % idx for idx in groups],
new.span[0], new.span[1], state=state, labels=shortlabels,
outdir=plotdir, pid='%s_SEGMENT_BAR_%s' % (tag, ptag.upper()),
colors=piecolors, stacked=True, ylim=[0, 100],
ylabel=texify('Percentage [%] of available time'),
legend_loc='upper left', legend_bbox_to_anchor=(1., 1),
legend_fontsize=12, legend_borderaxespad=0,
legend_frameon=False,
legend_handlelength=1.0, legend_handletextpad=.5,
title='%s operating mode %s' % (new.ifo, ptag)))
return new
[docs]
def process(self, nds=None, nproc=1,
config=GWSummConfigParser(), datacache=None,
datafind_error='raise', **kwargs):
"""Process time accounting data
"""
for p in self.plots:
if p.outputfile in globalv.WRITTEN_PLOTS:
p.new = False
# get archived GPS time
tag = self.segmenttag % list(self.modes)[0]
try:
lastgps = globalv.SEGMENTS[tag].known[-1][-1]
except (IndexError, KeyError):
lastgps = self.span[0]
# get data
new = SegmentList([type(self.span)(lastgps, self.span[1])])
data = get_timeseries(self.channel, new,
config=config, nds=nds, dtype='int16',
datafind_error=datafind_error,
nproc=nproc, cache=datacache)
vprint(" All time-series data loaded\n")
# find segments
for ts in data:
modesegments = DataQualityDict()
for idx, name in self.modes.items():
# get segments for state
tag = self.segmenttag % idx
instate = ts == idx * ts.unit
modesegments[tag] = instate.to_dqflag(name=name.strip('*'))
# append segments for group
group = int(idx // 10. * 10)
gtag = self.segmenttag % group
try:
modesegments[gtag] += modesegments[tag]
except KeyError:
modesegments[gtag] = modesegments[tag]
globalv.SEGMENTS += modesegments
kwargs['segdb_error'] = 'ignore'
super(AccountingTab, self).process(
config=config, nds=nds, nproc=nproc,
segmentcache=Cache(), datacache=datacache,
datafind_error=datafind_error, **kwargs)
[docs]
def write_state_html(self, state):
"""Write the HTML for the given state of this `GuardianTab`
"""
page = self.scaffold_plots(state=state)
page.div(class_='row')
page.div(class_='col-md-12')
# get segment data
groups = type(self.modes)((idx, name) for idx, name in
self.modes.items() if idx % 10 == 0)
modes = type(self.modes)(
(idx, name) for idx, name in self.modes.items() if
not name.startswith('*'))
headers = ['Index', 'Name', 'Active seconds',
'Hours', '%']
prefix = self.ifo.lower()
for flags, title, id in zip(
[groups, modes],
['Top-level mode information', 'Detailed mode information'],
['%s-top-level-mode' % prefix, '%s-detailed-mode' % prefix]):
page.h1(title, class_='mt-4')
data = []
pc = float(abs(state.active) / 100.)
tots = 0
toth = 0
totp = 0
for idx, name in flags.items():
flag = get_segments(self.segmenttag % idx,
state.active, query=False).copy()
actives = abs(flag.active)
activeh = actives / 3600.
try:
activep = actives / pc
except ZeroDivisionError:
activep = 0
tots += actives
toth += activeh
totp += activep
data.append([str(idx), name.strip('*'), '%.1f' % actives,
'%.1f' % activeh, '%.1f' % activep])
data.append(['<strong>%s</strong>' % x for x in [
'', 'Total:', '%.1f' % tots, '%.1f' % toth, '%.1f' % totp]])
page.add(str(html.table(
headers, data, id=id,
caption="%s observatory mode statistics as recorded in "
"<samp>%s</samp>" % (title.split()[0], self.channel))))
return super(ParentTab, self).write_state_html(state, plots=False,
pre=page)
register_tab(AccountingTab)